diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 8dba2df..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-/config/*.tmplc
-*.pyc
-*.pyo
-/_cache
-/_output
-/.venv
diff --git a/CNAME b/CNAME
new file mode 100644
index 0000000..fec7b5d
--- /dev/null
+++ b/CNAME
@@ -0,0 +1 @@
+planet.python.org.br
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index fe55838..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,32 +0,0 @@
-FROM ubuntu:bionic
-
-RUN apt-get update \
- && apt-get install -y --no-install-recommends \
- python-pip python-setuptools python-wheel \
- locales tzdata \
- ca-certificates \
- strace gdb lsof locate net-tools htop iputils-ping dnsutils \
- python2.7-dbg python2.7 libpython2.7 python-dbg libpython-dbg \
- curl nano vim tree less telnet patch \
- graphviz sqlite3 \
- dumb-init \
- && rm -rf /var/lib/apt/lists/*
-
-RUN locale-gen en_US.UTF-8
-
-COPY /code /planet/code
-COPY /config /planet/config
-COPY /static /planet/dist/static
-
-WORKDIR /planet
-
-ENTRYPOINT ["dumb-init"]
-
-RUN echo "#!/bin/bash -eux \n\
-python2.7 code/planet.py config/config.ini \n\
-cd /planet/dist/ \n\
-python2.7 -mSimpleHTTPServer 8080 \n\
-"> /start.sh
-RUN chmod +x /start.sh
-EXPOSE 8080
-
diff --git a/README.md b/README.md
deleted file mode 100644
index 1f3d15b..0000000
--- a/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# Planet
-
-Repo público para atualização da lista de sites do https://planet.python.org.br/
-
-Recuperado do repo planet python https://github.com/python/planet
-
-## Adição de Feeds:
-
-Para adicionar um feed, faça um PR com a adição no arquivo config/config.ini
-
-## Template
-
-```
-# Meu Blog
-[https://www.meusite.org/filter/python/feed.xml]
-name=Meu Blog Pythonista
-```
diff --git a/atom.xml b/atom.xml
new file mode 100644
index 0000000..d0ad08a
--- /dev/null
+++ b/atom.xml
@@ -0,0 +1,9505 @@
+
+
+
+ Planeta PythonBrasil
+
+
+ http://planet.python.org.br/atom.xml
+ 2025-04-17T02:15:10+00:00
+ Planet/2.0 +http://www.planetplanet.org
+
+
+ Building and Publishing Games to Steam Directly from GitHub Actions
+
+ https://nullonerror.org/2025/03/23/building-and-publishing-games-to-steam-directly-from-gitHub-actions
+ 2025-03-23T00:00:00+00:00
+ <p>I have been using GitHub Actions extensively both at work and in personal projects, as shown in this post <a href="https://nullonerror.org/2023/11/01/what-i-ve-been-automating-with-github-actions-an-automated-life/">What I’ve been automating with GitHub Actions, an automated life</a>.</p>
+
+<p>In my free time, I’m working on a 2D hide-and-seek game, and as you might expect, I’ve automated the entire release pipeline for publishing on Steam. After a few attempts, when it finally worked, it felt like magic: all I had to do was create a new tag, and within minutes, the Steam client was downloading the update.</p>
+
+<p>As I mentioned earlier, I have a 2D engine that, while simple, is quite comprehensive. With each new tag, I compile it in parallel for Windows, macOS, Linux, and WebAssembly. Once compilation is complete, I create a release and publish it on GitHub. <a href="https://github.com/willtobyte/carimbo/releases">Releases · willtobyte/carimbo</a></p>
+
+<p>This way</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Release</span>
+
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">push</span><span class="pi">:</span>
+ <span class="na">tags</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s2">"</span><span class="s">v*.*.*"</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">release</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">${{ matrix.config.os }}</span>
+
+ <span class="na">permissions</span><span class="pi">:</span>
+ <span class="na">contents</span><span class="pi">:</span> <span class="s">write</span>
+
+ <span class="na">strategy</span><span class="pi">:</span>
+ <span class="na">fail-fast</span><span class="pi">:</span> <span class="no">true</span>
+
+ <span class="na">matrix</span><span class="pi">:</span>
+ <span class="na">config</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">macOS</span>
+ <span class="na">os</span><span class="pi">:</span> <span class="s">macos-latest</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Ubuntu</span>
+ <span class="na">os</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">WebAssembly</span>
+ <span class="na">os</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Windows</span>
+ <span class="na">os</span><span class="pi">:</span> <span class="s">windows-latest</span>
+
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Cache Dependencies</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/cache@v4</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">path</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">~/.conan2/p</span>
+ <span class="s">C:/Users/runneradmin/.conan2/p</span>
+ <span class="na">key</span><span class="pi">:</span> <span class="s">${{ matrix.config.name }}-${{ hashFiles('**/conanfile.py') }}</span>
+ <span class="na">restore-keys</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">${{ matrix.config.name }}-</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Prepare Build Directory</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">mkdir build</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup Python</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-python@v5</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">python-version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3.12"</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Conan</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">pip install conan</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Detect Conan Profile</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">conan profile detect --force</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set Conan Center</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">conan remote update conancenter --url https://center2.conan.io</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Detect WebAssembly Conan Profile</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'WebAssembly'</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">cat > ~/.conan2/profiles/webassembly <<EOF</span>
+ <span class="s">include(default)</span>
+
+ <span class="s">[settings]</span>
+ <span class="s">arch=wasm</span>
+ <span class="s">os=Emscripten</span>
+
+ <span class="s">[tool_requires]</span>
+ <span class="s">*: emsdk/3.1.73</span>
+ <span class="s">EOF</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Windows Or macOS Dependencies</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'Windows' || matrix.config.name == 'macOS'</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">conan install . --output-folder=build --build=missing --settings compiler.cppstd=20 --settings build_type=Release</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Ubuntu Dependencies</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'Ubuntu'</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">conan install . --output-folder=build --build=missing --settings compiler.cppstd=20 --settings build_type=Release --conf "tools.system.package_manager:mode=install" --conf "tools.system.package_manager:sudo=True"</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install WebAssembly Dependencies</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'WebAssembly'</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">conan install . --output-folder=build --build=missing --profile=webassembly --settings compiler.cppstd=20 --settings build_type=Release</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Configure</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">cmake .. -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">cmake --build . --parallel 8 --config Release --verbose</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Create Artifacts Directory</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">mkdir artifacts</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Compress Artifacts</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'macOS'</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">chmod -R a+rwx carimbo</span>
+ <span class="s">tar -cpzvf macOS.tar.gz carimbo</span>
+ <span class="s">mv macOS.tar.gz ../artifacts</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Compress Artifacts</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'Ubuntu'</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">chmod +x carimbo</span>
+ <span class="s">tar -czvf Ubuntu.tar.gz --mode='a+rwx' carimbo</span>
+ <span class="s">mv Ubuntu.tar.gz ../artifacts</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Compress Artifacts</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'WebAssembly'</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">zip -jr WebAssembly.zip carimbo.wasm carimbo.js</span>
+ <span class="s">mv WebAssembly.zip ../artifacts</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Compress Artifacts</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'Windows'</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+ <span class="na">shell</span><span class="pi">:</span> <span class="s">powershell</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">Compress-Archive -LiteralPath 'Release/carimbo.exe' -DestinationPath "../artifacts/Windows.zip"</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Release</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">softprops/action-gh-release@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">tag_name</span><span class="pi">:</span> <span class="s">${{ github.event.inputs.tagName }}</span>
+ <span class="na">prerelease</span><span class="pi">:</span> <span class="s">${{ github.events.inputs.prerelease }}</span>
+ <span class="na">files</span><span class="pi">:</span> <span class="s">artifacts/*</span>
+</code></pre></div></div>
+
+<p>Publishing on Steam is quite simple. First, you need a developer account with the correct documentation and fees paid.</p>
+
+<p>After that, you’ll need to generate some secret keys as follows:</p>
+
+<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>steamcmd +login <username> <password> +quit
+</code></pre></div></div>
+
+<p>If you don’t have the steamcmd application installed, you’ll need to install it using:</p>
+
+<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cast <span class="nb">install</span> <span class="nt">--cask</span> steamcmd
+</code></pre></div></div>
+
+<p>Copy the contents of the authentication file:</p>
+
+<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> ~/Library/Application<span class="se">\ </span>Support/Steam/config/config.vdf | <span class="nb">base64</span> | pbcopy
+</code></pre></div></div>
+
+<p><strong>Note:</strong> You must have MFA enabled. After logging in, run the command below and copy the output into a GitHub Action variable named <code class="language-plaintext highlighter-rouge">STEAM_CONFIG_VDF</code>.</p>
+
+<p>Also, create the variables <code class="language-plaintext highlighter-rouge">STEAM_USERNAME</code> with your username and <code class="language-plaintext highlighter-rouge">STEAM_APP_ID</code> with your game’s ID.</p>
+
+<p>Additionally, the Action downloads the latest Carimbo release for Windows only (sorry Linux and macOS users, my time is limited). Ideally, I should pin the runtime version (the Carimbo version) using something like a runtime.txt file. Maybe I’ll implement this in the future, but for now, everything runs on the bleeding edge. :-)</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">on</span><span class="pi">:</span>
+ <span class="na">push</span><span class="pi">:</span>
+ <span class="na">tags</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s2">"</span><span class="s">v*.*.*"</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">publish</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">CARIMBO_TAG</span><span class="pi">:</span> <span class="s2">"</span><span class="s">v1.0.65"</span>
+
+ <span class="na">permissions</span><span class="pi">:</span>
+ <span class="na">contents</span><span class="pi">:</span> <span class="s">write</span>
+
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Clone the repository</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install 7zip</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">sudo apt install p7zip-full</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Create bundle</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">7z a -xr'!.git/*' -xr'!.git' -xr'!.*' -t7z -m0=lzma -mx=6 -mfb=64 -md=32m -ms=on bundle.7z .</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Download Carimbo runtime</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">GITHUB_TOKEN</span><span class="pi">:</span> <span class="s">${{ secrets.GITHUB_TOKEN }}</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">gh release download ${{ env.CARIMBO_TAG }} --repo willtobyte/carimbo --pattern "Windows.zip"</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Extract Carimbo runtime</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">7z x Windows.zip -o.</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Copy files</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">mkdir -p output</span>
+ <span class="s">mv bundle.7z output/</span>
+ <span class="s">mv carimbo.exe output/</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Upload build to Steam</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">game-ci/steam-deploy@v3</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">username</span><span class="pi">:</span> <span class="s">${{ secrets.STEAM_USERNAME }}</span>
+ <span class="na">configVdf</span><span class="pi">:</span> <span class="s">${{ secrets.STEAM_CONFIG_VDF }}</span>
+ <span class="na">appId</span><span class="pi">:</span> <span class="s">${{ secrets.STEAM_APP_ID }}</span>
+ <span class="na">rootPath</span><span class="pi">:</span> <span class="s">output</span>
+ <span class="na">depot1Path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">."</span>
+ <span class="na">releaseBranch</span><span class="pi">:</span> <span class="s">prerelease</span>
+</code></pre></div></div>
+
+<p>Boom! If everything is correct, your game should appear in your Steam client under the list of owned games.</p>
+
+ Rodrigo Delduca
+ rodrigo@delduca.org
+ https://nullonerror.org/
+
+
+ NULL on error
+ NULL on error
+
+ https://nullonerror.org/feed.xml
+ 2025-04-17T02:14:57+00:00
+
+
+
+
+ Advent of code 2024 - Day 15 of 25
+
+ https://blog.nilo.pro.br/en/posts/2024-12-16-adventofcode2024-day15/
+ 2024-12-16T00:49:05+00:00
+ <p>The fifteenth day of Advent of Code was yesterday 15/12/2024!
+If you&rsquo;re here for the first time today, don&rsquo;t forget to read the posts about
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-06-adventofcode2024-day01/">Day 1</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-06-adventofcode2024-day02/">Day 2</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-06-adventofcode2024-day03/">Day 3</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-06-adventofcode2024-day04/">Day 4</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-06-adventofcode2024-day05/">Day 5</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-07-adventofcode2024-day06/">Day 6</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-08-adventofcode2024-day07/">Day 7</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-09-adventofcode2024-day08/">Day 8</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-10-adventofcode2024-day09/">Day 9</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-11-adventofcode2024-day10/">Day 10</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-12-adventofcode2024-day11/">Day 11</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-13-adventofcode2024-day12/">Day 12</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-14-adventofcode2024-day13/">Day 13</a> and
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-15-adventofcode2024-day14/">Day 14</a>.</p>
+<p>I classified the <a href="https://adventofcode.com/2024/day/15">fifteenth problem</a> as medium (part 1) and medium (part 2).</p>
+<h1 id="tips">Tips</h1>
+<ul>
+<li>The first part of the problem is to read the maze (warehouse) and the robot commands.</li>
+<li>Use the example inputs to test the solution.</li>
+<li>You need to detect in the maze input where the walls, robot, and boxes are.</li>
+<li>The commands are on multiple lines, it&rsquo;s easier if you concatenate them into a single string.</li>
+<li>You can implement robot directions (commands) using vectors.</li>
+<li>Vectors can also be used to mark box and wall positions.</li>
+<li>The walls surround the entire warehouse, you don&rsquo;t need to worry about the robot leaving the warehouse.</li>
+<li>To check if the robot can move, verify if the new position is free. In this case, move the robot by updating its position.</li>
+<li>If the next position is occupied by a wall, the robot cannot move and stays in the same place.</li>
+<li>To know if boxes can be moved, you can recursively try all boxes from the movement direction. For each box, check if it can move (blank space in the movement direction). If one of the boxes cannot be moved, the movement is canceled.</li>
+<li>Remember that the robot can push multiple boxes at once. A recursive function can help solve this problem.</li>
+<li>In part 1, boxes are the same size as walls, meaning each box occupies one position. In part 2, each box occupies two positions.</li>
+<li>Part 2 is a bit more complex because you can move more than columns and rows of boxes, but entire parts of the warehouse.</li>
+<li>Horizontal movement is similar to part 1, as each box has a height of only one character, like in part 1.</li>
+<li>Each movement in part 2 can move up to 2 boxes at once. But this movement is done in cascade, so these two boxes can move other 2, 3, or more boxes. Each box movement can move zero, one, or two boxes at each step, but can cause the movement of many others.</li>
+<li>In part 2, you need to verify if the entire movement is valid. For example, when the boxes being pushed form a V or get stuck on a wall, they all have to stop moving. If you implement the movement function as in part 1, you&rsquo;ll see that the boxes will move in several pieces, which leads to the wrong answer.</li>
+</ul>
+<h1 id="how-much-python-do-you-need-to-know">How much Python do you need to know?</h1>
+<p>The chapters refer to my book <a href="https://python.nilo.pro.br/">Introduction to Programming with Python</a>.</p>
+<ul>
+<li>Lists - Chapter 6</li>
+<li>Strings - Chapter 7</li>
+<li>Recursive Functions - Chapter 8</li>
+<li>Classes, inheritance - Chapter 10</li>
+</ul>
+<h1 id="answer">Answer</h1>
+<p>Here&rsquo;s the code that solves the problem. You&rsquo;ll see the scars in the code from switching between part 1 and part 2, but the code works relatively well.</p>
+<p>You can use the Warehouse, Boxes, Wall, Robot, Problem, and Problem2 classes to debug your solution. The <code>draw</code> and <code>prompt</code> methods can help interpret and draw the commands step by step. Although they&rsquo;re not called in the answer, they&rsquo;re in the code if you need them.</p>
+<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> sys
+</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> helpers <span style="color:#f92672">import</span> read_file_without_blank_lines, Vector2d, Maze
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>TEST_INPUT <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;&#34;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O.O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##@.O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#...O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.#.O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#...O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#......#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;^^&gt;&gt;&gt;vv&lt;v&gt;&gt;v&lt;&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>INPUT <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;&#34;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O..O.O#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#......O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.OO..O.O#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O@..O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#O#..O...#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#O..O..O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.OO.O.OO#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#....O...#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;vv&gt;^&lt;v^&gt;v&gt;^vv^v&gt;v&lt;&gt;v^v&lt;v&lt;^vv&lt;&lt;&lt;^&gt;&lt;&lt;&gt;&lt;&gt;&gt;v&lt;vvv&lt;&gt;^v^&gt;^&lt;&lt;&lt;&gt;&lt;&lt;v&lt;&lt;&lt;v^vv^v&gt;^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">vvv&lt;&lt;^&gt;^v^^&gt;&lt;&lt;&gt;&gt;&gt;&lt;&gt;^&lt;&lt;&gt;&lt;^vv^^&lt;&gt;vvv&lt;&gt;&gt;&lt;^^v&gt;^&gt;vv&lt;&gt;v&lt;&lt;&lt;&lt;v&lt;^v&gt;^&lt;^^&gt;&gt;&gt;^&lt;v&lt;v
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&gt;&lt;&gt;vv&gt;v^v^&lt;&gt;&gt;&lt;&gt;&gt;&gt;&gt;&lt;^^&gt;vv&gt;v&lt;^^^&gt;&gt;v^v^&lt;^^&gt;v^^&gt;v^&lt;^v&gt;v&lt;&gt;&gt;v^v^&lt;v&gt;v^^&lt;^^vv&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;&lt;v&lt;^&gt;&gt;^^^^&gt;&gt;&gt;v^&lt;&gt;vvv^&gt;&lt;v&lt;&lt;&lt;&gt;^^^vv^&lt;vvv&gt;^&gt;v&lt;^^^^v&lt;&gt;^&gt;vvvv&gt;&lt;&gt;&gt;v^&lt;&lt;^^^^^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^&gt;&lt;^&gt;&lt;&gt;&gt;&gt;&lt;&gt;^^&lt;&lt;^^v&gt;&gt;&gt;&lt;^&lt;v&gt;^&lt;vv&gt;&gt;v&gt;&gt;&gt;^v&gt;&lt;&gt;^v&gt;&lt;&lt;&lt;&lt;v&gt;&gt;v&lt;v&lt;v&gt;vvv&gt;^&lt;&gt;&lt;&lt;&gt;^&gt;&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^&gt;&gt;&lt;&gt;^v&lt;&gt;&lt;^vvv&lt;^^&lt;&gt;&lt;v&lt;&lt;&lt;&lt;&lt;&gt;&lt;^v&lt;&lt;&lt;&gt;&lt;&lt;&lt;^^&lt;v&lt;^^^&gt;&lt;^&gt;&gt;^&lt;v^&gt;&lt;&lt;&lt;^&gt;&gt;^v&lt;v^v&lt;v^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&gt;^&gt;&gt;^v&gt;vv&gt;^&lt;&lt;^v&lt;&gt;&gt;&lt;&lt;&gt;&lt;&lt;v&lt;&lt;v&gt;&lt;&gt;v&lt;^vv&lt;&lt;&lt;&gt;^^v^&gt;^^&gt;&gt;&gt;&lt;&lt;^v&gt;&gt;v^v&gt;&lt;^^&gt;&gt;^&lt;&gt;vv^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;&gt;&lt;^^&gt;^^^&lt;&gt;&lt;vvvvv^v&lt;v&lt;&lt;&gt;^v&lt;v&gt;v&lt;&lt;^&gt;&lt;&lt;&gt;&lt;&lt;&gt;&lt;&lt;&lt;^^&lt;&lt;&lt;^&lt;&lt;&gt;&gt;&lt;&lt;&gt;&lt;^^^&gt;^^&lt;&gt;^&gt;v&lt;&gt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^^&gt;vv&lt;^v^v&lt;vv&gt;^&lt;&gt;&lt;v&lt;^v&gt;^^^&gt;&gt;&gt;^^vvv^&gt;vvv&lt;&gt;&gt;&gt;^&lt;^&gt;&gt;&gt;&gt;&gt;^&lt;&lt;^v&gt;^vvv&lt;&gt;^&lt;&gt;&lt;&lt;v&gt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">v^^&gt;&gt;&gt;&lt;&lt;^^&lt;&gt;&gt;^v^&lt;v^vv&lt;&gt;v^&lt;&lt;&gt;^&lt;^v^v&gt;&lt;^&lt;&lt;&lt;&gt;&lt;&lt;^&lt;v&gt;&lt;v&lt;&gt;vv&gt;&gt;v&gt;&lt;v^&lt;vv&lt;&gt;v^&lt;&lt;^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">read_input</span>(input: str) <span style="color:#f92672">-&gt;</span> tuple[str, str]:
+</span></span><span style="display:flex;"><span> maze <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> commands <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> line <span style="color:#f92672">in</span> input<span style="color:#f92672">.</span>splitlines():
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> line<span style="color:#f92672">.</span>strip():
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> line<span style="color:#f92672">.</span>startswith(<span style="color:#e6db74">&#34;#&#34;</span>):
+</span></span><span style="display:flex;"><span> maze<span style="color:#f92672">.</span>append(line)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>:
+</span></span><span style="display:flex;"><span> commands<span style="color:#f92672">.</span>append(line)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> maze, commands
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Vector2dWithSymbol</span>(Vector2d):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x, symbol):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>symbol <span style="color:#f92672">=</span> symbol
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __add__(self, other):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> Vector2dWithSymbol(self<span style="color:#f92672">.</span>y <span style="color:#f92672">+</span> other<span style="color:#f92672">.</span>y, self<span style="color:#f92672">.</span>x <span style="color:#f92672">+</span> other<span style="color:#f92672">.</span>x, self<span style="color:#f92672">.</span>symbol)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __sub__(self, other):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> Vector2dWithSymbol(self<span style="color:#f92672">.</span>y <span style="color:#f92672">-</span> other<span style="color:#f92672">.</span>y, self<span style="color:#f92672">.</span>x <span style="color:#f92672">-</span> other<span style="color:#f92672">.</span>x, self<span style="color:#f92672">.</span>symbol)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Box</span>(Vector2dWithSymbol):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x, symbol<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;O&#34;</span>):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, symbol)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Wall</span>(Vector2dWithSymbol):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, <span style="color:#e6db74">&#34;#&#34;</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Robot</span>(Vector2dWithSymbol):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, <span style="color:#e6db74">&#34;@&#34;</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Warehouse</span>(Maze):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">init_drawing</span>(self, background<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;.&#34;</span>):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>background <span style="color:#f92672">=</span> background
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>drawing <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> _ <span style="color:#f92672">in</span> range(self<span style="color:#f92672">.</span>max_y):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>drawing<span style="color:#f92672">.</span>append([background] <span style="color:#f92672">*</span> self<span style="color:#f92672">.</span>max_x)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">draw</span>(self, objects: list[Vector2dWithSymbol]):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> obj <span style="color:#f92672">in</span> objects:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>drawing[obj<span style="color:#f92672">.</span>y][obj<span style="color:#f92672">.</span>x] <span style="color:#f92672">=</span> obj<span style="color:#f92672">.</span>symbol
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">dump_drawing</span>(self, output<span style="color:#f92672">=</span>sys<span style="color:#f92672">.</span>stdout):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> line <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>drawing:
+</span></span><span style="display:flex;"><span> print(<span style="color:#e6db74">&#34;&#34;</span><span style="color:#f92672">.</span>join(line), file<span style="color:#f92672">=</span>output)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __iter__(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> iter(self<span style="color:#f92672">.</span>maze)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Problem</span>:
+</span></span><span style="display:flex;"><span> DIRECTIONS <span style="color:#f92672">=</span> {
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;&lt;&#34;</span>: Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;^&#34;</span>: Vector2d(<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;&gt;&#34;</span>: Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;v&#34;</span>: Vector2d(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>),
+</span></span><span style="display:flex;"><span> }
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, maze: list[str], commands: list[str]):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>commands <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;</span><span style="color:#f92672">.</span>join(commands)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse <span style="color:#f92672">=</span> Warehouse(maze)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>setup()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">setup</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>walls <span style="color:#f92672">=</span> set()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes <span style="color:#f92672">=</span> dict()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> y, line <span style="color:#f92672">in</span> enumerate(self<span style="color:#f92672">.</span>warehouse):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> x, char <span style="color:#f92672">in</span> enumerate(line):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;#&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>walls<span style="color:#f92672">.</span>add(Wall(y, x))
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;O&#34;</span>:
+</span></span><span style="display:flex;"><span> box <span style="color:#f92672">=</span> Box(y, x)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes[box] <span style="color:#f92672">=</span> box
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;@&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">=</span> Robot(y, x)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">draw</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>init_drawing()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>draw(self<span style="color:#f92672">.</span>walls)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>draw(self<span style="color:#f92672">.</span>boxes)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>draw([self<span style="color:#f92672">.</span>robot])
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>dump_drawing()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">execute_commands</span>(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> command <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>commands:
+</span></span><span style="display:flex;"><span> direction <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>DIRECTIONS[command]
+</span></span><span style="display:flex;"><span> new_position <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">+</span> direction
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> new_position <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> self<span style="color:#f92672">.</span>push(new_position, direction):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">elif</span> new_position <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>walls:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">+=</span> direction
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">push</span>(self, position: Vector2d, direction: Vector2d, execute: bool <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>):
+</span></span><span style="display:flex;"><span> new_position <span style="color:#f92672">=</span> position <span style="color:#f92672">+</span> direction
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> new_position <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>walls:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> new_position <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> can_push <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>push(new_position, direction, execute)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> can_push:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> execute:
+</span></span><span style="display:flex;"><span> box <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>boxes<span style="color:#f92672">.</span>pop(position)
+</span></span><span style="display:flex;"><span> box<span style="color:#f92672">.</span>y <span style="color:#f92672">=</span> new_position<span style="color:#f92672">.</span>y
+</span></span><span style="display:flex;"><span> box<span style="color:#f92672">.</span>x <span style="color:#f92672">=</span> new_position<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes[box] <span style="color:#f92672">=</span> box
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">gps_boxes</span>(self):
+</span></span><span style="display:flex;"><span> total <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> box <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> total <span style="color:#f92672">+=</span> box<span style="color:#f92672">.</span>y <span style="color:#f92672">*</span> <span style="color:#ae81ff">100</span> <span style="color:#f92672">+</span> box<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> total
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">prompt</span>(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">while</span> <span style="color:#66d9ef">True</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>draw()
+</span></span><span style="display:flex;"><span> commands <span style="color:#f92672">=</span> input(<span style="color:#e6db74">&#34;Commands: &#34;</span>)<span style="color:#f92672">.</span>lower()<span style="color:#f92672">.</span>strip()
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> commands:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>commands <span style="color:#f92672">=</span> commands
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Problem2</span>(Problem):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, maze: list[str], commands: list[str]):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(maze, commands)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>max_x <span style="color:#f92672">*=</span> <span style="color:#ae81ff">2</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">setup</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>walls <span style="color:#f92672">=</span> set()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes <span style="color:#f92672">=</span> dict()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> y, line <span style="color:#f92672">in</span> enumerate(self<span style="color:#f92672">.</span>warehouse):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> x, char <span style="color:#f92672">in</span> enumerate(line):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;#&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>walls<span style="color:#f92672">.</span>add(Wall(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>))
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>walls<span style="color:#f92672">.</span>add(Wall(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>))
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;O&#34;</span>:
+</span></span><span style="display:flex;"><span> box1 <span style="color:#f92672">=</span> Box(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>, <span style="color:#e6db74">&#34;[&#34;</span>)
+</span></span><span style="display:flex;"><span> box2 <span style="color:#f92672">=</span> Box(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>, <span style="color:#e6db74">&#34;]&#34;</span>)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes[box1] <span style="color:#f92672">=</span> box1
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes[box2] <span style="color:#f92672">=</span> box2
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;@&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">=</span> Robot(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">push</span>(self, position: Vector2d, direction: Vector2d, execute: bool <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>):
+</span></span><span style="display:flex;"><span> new_position <span style="color:#f92672">=</span> position <span style="color:#f92672">+</span> direction
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> direction <span style="color:#f92672">in</span> [Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>), Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>)] <span style="color:#f92672">or</span> new_position <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>walls:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> super()<span style="color:#f92672">.</span>push(position, direction, execute)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> position <span style="color:#f92672">not</span> <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> box1, box2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>find_box(position)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> can_push1 <span style="color:#f92672">=</span> super()<span style="color:#f92672">.</span>push(box1, direction, <span style="color:#66d9ef">False</span>)
+</span></span><span style="display:flex;"><span> can_push2 <span style="color:#f92672">=</span> super()<span style="color:#f92672">.</span>push(box2, direction, <span style="color:#66d9ef">False</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> can_push1 <span style="color:#f92672">and</span> can_push2:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> execute:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>push_v(box1, box2, direction)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">find_box</span>(self, position):
+</span></span><span style="display:flex;"><span> box1 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>boxes[position]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> box1<span style="color:#f92672">.</span>symbol <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;[&#34;</span>:
+</span></span><span style="display:flex;"><span> box2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>boxes[position <span style="color:#f92672">+</span> Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>)]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>:
+</span></span><span style="display:flex;"><span> box2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>boxes[position <span style="color:#f92672">+</span> Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> box1, box2
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">push_v</span>(self, position1, position2, direction):
+</span></span><span style="display:flex;"><span> np1 <span style="color:#f92672">=</span> position1 <span style="color:#f92672">+</span> direction
+</span></span><span style="display:flex;"><span> np2 <span style="color:#f92672">=</span> position2 <span style="color:#f92672">+</span> direction
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> np1 <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>push_v(<span style="color:#f92672">*</span>self<span style="color:#f92672">.</span>find_box(np1), direction)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> np2 <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>push_v(<span style="color:#f92672">*</span>self<span style="color:#f92672">.</span>find_box(np2), direction)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> position, new_position <span style="color:#f92672">in</span> zip([position1, position2], [np1, np2]):
+</span></span><span style="display:flex;"><span> box <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>boxes<span style="color:#f92672">.</span>pop(position)
+</span></span><span style="display:flex;"><span> box<span style="color:#f92672">.</span>y <span style="color:#f92672">=</span> new_position<span style="color:#f92672">.</span>y
+</span></span><span style="display:flex;"><span> box<span style="color:#f92672">.</span>x <span style="color:#f92672">=</span> new_position<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes[box] <span style="color:#f92672">=</span> box
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">gps_boxes</span>(self):
+</span></span><span style="display:flex;"><span> total <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> box <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> box<span style="color:#f92672">.</span>symbol <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;[&#34;</span>:
+</span></span><span style="display:flex;"><span> total <span style="color:#f92672">+=</span> box<span style="color:#f92672">.</span>y <span style="color:#f92672">*</span> <span style="color:#ae81ff">100</span> <span style="color:#f92672">+</span> box<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> total
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problem <span style="color:#f92672">=</span> Problem(<span style="color:#f92672">*</span>read_input(TEST_INPUT))
+</span></span><span style="display:flex;"><span>problem<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problem<span style="color:#f92672">.</span>gps_boxes()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Part 1 - small input test:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">2028</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problem <span style="color:#f92672">=</span> Problem(<span style="color:#f92672">*</span>read_input(INPUT))
+</span></span><span style="display:flex;"><span>problem<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problem<span style="color:#f92672">.</span>gps_boxes()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Part 1 - large input test:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">10092</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problem <span style="color:#f92672">=</span> Problem(<span style="color:#f92672">*</span>read_input(read_file_without_blank_lines(<span style="color:#e6db74">&#34;15/input.txt&#34;</span>)))
+</span></span><span style="display:flex;"><span>problem<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problem<span style="color:#f92672">.</span>gps_boxes()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Part 1 - large input:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">1552463</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#75715e"># Part 2</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problem <span style="color:#f92672">=</span> Problem2(<span style="color:#f92672">*</span>read_input(INPUT))
+</span></span><span style="display:flex;"><span>problem<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problem<span style="color:#f92672">.</span>gps_boxes()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Part 2 - large input test:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">9021</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problem <span style="color:#f92672">=</span> Problem2(<span style="color:#f92672">*</span>read_input(read_file_without_blank_lines(<span style="color:#e6db74">&#34;15/input.txt&#34;</span>)))
+</span></span><span style="display:flex;"><span>problem<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problem<span style="color:#f92672">.</span>gps_boxes()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Part 2 - large input:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">1554058</span>
+</span></span></code></pre></div>
+
+ Nilo Ney Coutinho Menezes
+ https://blog.nilo.pro.br/tags/python/
+
+
+ Python on Nilo Menezes
+ Recent content in Python on Nilo Menezes
+
+ https://blog.nilo.pro.br/tags/python/index.xml
+ 2025-04-17T02:14:49+00:00
+ Nilo Ney Coutinho Menezes &#xa9;2007-2024
+
+
+
+
+ Advent of code 2024 - Dia 15 de 25
+
+ https://blog.nilo.pro.br/posts/2024-12-16-adventofcode2024-dia15/
+ 2024-12-16T00:49:05+00:00
+ <p>O décimo quinto dia do Advent of Code foi ontem 15/12/2024!
+Se você caiu aqui hoje pela primeira vez, não deixe de ler os posts sobre o
+<a href="https://blog.nilo.pro.br/posts/2024-12-02-adventofcode2024-dia01/">Dia 1</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-03-adventofcode2024-dia02/">Dia 2</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-04-adventofcode2024-dia03/">Dia 3</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-05-adventofcode2024-dia04/">Dia 4</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-06-adventofcode2024-dia05/">Dia 5</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-07-adventofcode2024-dia06/">Dia 6</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-08-adventofcode2024-dia07/">Dia 7</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-09-adventofcode2024-dia08/">Dia 8</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-10-adventofcode2024-dia09/">Dia 9</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-11-adventofcode2024-dia10/">Dia 10</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-12-adventofcode2024-dia11/">Dia 11</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-13-adventofcode2024-dia12/">Dia 12</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-14-adventofcode2024-dia13/">Dia 13</a> e
+<a href="https://blog.nilo.pro.br/posts/2024-12-15-adventofcode2024-dia14/">Dia 14</a>.</p>
+<p>Classifiquei o <a href="https://adventofcode.com/2024/day/15">décimo quinto problema</a> como médio (parte 1) e médio (parte 2).</p>
+<h1 id="dicas">Dicas</h1>
+<ul>
+<li>A primeira parte do problema é ler o labirinto (armazém) e os comandos do robô.</li>
+<li>Utilize as entradas de exemplo para testar a solução.</li>
+<li>Você precisa detectar na entrada do labirinto, onde estão as paredes, o robô e as caixas.</li>
+<li>Os comandos estão em várias linhas, fica mais fácil se você concatená-las em uma só string.</li>
+<li>Você pode implementar as direções do robô (comandos) usando vetores.</li>
+<li>Vetores também podem ser utilizados para marcar as posições das caixas e paredes.</li>
+<li>As paredes circundam todo o armazém, você não precisa de preocupar com o robô saindo do armazém.</li>
+<li>Para verificar se o robô pode se mexer, verifique se a nova posição está livre. Neste caso, mova o robô, atualizando a posição.</li>
+<li>Se a posição seguinte for ocupada por uma parede, o robô não pode se mexer e fica no mesmo lugar.</li>
+<li>Para saber se pode mover as caixas, você pode tentar recursivamente todas as caixas a partir da direção do movimento. A cada caixa, verifique se ela pode se mover (espaço em branco na direção do movimento). Caso uma das caixas não possa ser movida, o movimento é cancelado.</li>
+<li>Lembre-se que o robô pode empurrar várias caixas ao mesmo tempo. Uma função recursiva pode ajudar a resolver este problema.</li>
+<li>Na parte 1, as caixas são do mesmo tamanho das paredes, ou seja, cada caixa ocupa uma posição. Na parte 2, cada caixa ocupa duas posições.</li>
+<li>A parte 2 é um pouco mais complexa, pois você pode mover mais que colunas e linhas de caixas, mas partes inteiras do armazém.</li>
+<li>O movimento horizontal é semelhante ao da parte 1, pois cada caixa tem altura de apenas um caractere, como na parte 1.</li>
+<li>Cada movimento na parte 2 pode mover até 2 caixas ao mesmo tempo. Mas esse movimento é feito em cascata, logo estas duas caixas podem mover outras 2, 3, ou mais caixas. Cada movimento de caixa pode movimentar a cada passo, zero, uma ou duas caixas, mas pode ocasionar o movimento de muitas outras.</li>
+<li>Na parte 2, você precisa verificar se o movimento inteiro é válido. Por exemplo, quando as caixas sendo empurradas formam um V ou ficam presas em uma parede, todas tem que parar de se mover. Se você implementar a função de movimento como na parte 1, verá que as caixas vão se mexer em vários pedaços, o que leva a resposta errada.</li>
+</ul>
+<h1 id="quanto-de-python-você-precisa-saber">Quanto de Python você precisa saber?</h1>
+<p>Os capítulos se referem ao meu livro <a href="https://python.nilo.pro.br/">Introdução à Programação com Python</a>.</p>
+<ul>
+<li>Listas - Capítulo 6</li>
+<li>Strings - Capítulo 7</li>
+<li>Funções recursivas - Capítulo 8</li>
+<li>Classes, herança - Capítulo 10</li>
+</ul>
+<h1 id="resposta">Resposta</h1>
+<p>Aqui o código que resolve o problema. Você verá as cicatrizes no código da troca entre a parte 1 e a parte 2, mas o código funciona relativamente bem.</p>
+<p>Você pode de usar as classes Armazém, Caixas, Parede, Robô, Problema e Problema2 para depurar sua solução. Os métodos <code>desenha</code>e <code>prompt</code> podem ajudar a interpretar e desenhar os comandos passo a passo. Embora não sejam chamados na resposta, eles estão no código caso você precise.</p>
+<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> sys
+</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> auxiliares <span style="color:#f92672">import</span> le_arquivo_sem_linhas_em_branco, Vector2d, Labirinto
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>ENTRADA_TESTE <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;&#34;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O.O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##@.O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#...O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.#.O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#...O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#......#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;^^&gt;&gt;&gt;vv&lt;v&gt;&gt;v&lt;&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>ENTRADA <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;&#34;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O..O.O#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#......O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.OO..O.O#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O@..O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#O#..O...#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#O..O..O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.OO.O.OO#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#....O...#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;vv&gt;^&lt;v^&gt;v&gt;^vv^v&gt;v&lt;&gt;v^v&lt;v&lt;^vv&lt;&lt;&lt;^&gt;&lt;&lt;&gt;&lt;&gt;&gt;v&lt;vvv&lt;&gt;^v^&gt;^&lt;&lt;&lt;&gt;&lt;&lt;v&lt;&lt;&lt;v^vv^v&gt;^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">vvv&lt;&lt;^&gt;^v^^&gt;&lt;&lt;&gt;&gt;&gt;&lt;&gt;^&lt;&lt;&gt;&lt;^vv^^&lt;&gt;vvv&lt;&gt;&gt;&lt;^^v&gt;^&gt;vv&lt;&gt;v&lt;&lt;&lt;&lt;v&lt;^v&gt;^&lt;^^&gt;&gt;&gt;^&lt;v&lt;v
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&gt;&lt;&gt;vv&gt;v^v^&lt;&gt;&gt;&lt;&gt;&gt;&gt;&gt;&lt;^^&gt;vv&gt;v&lt;^^^&gt;&gt;v^v^&lt;^^&gt;v^^&gt;v^&lt;^v&gt;v&lt;&gt;&gt;v^v^&lt;v&gt;v^^&lt;^^vv&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;&lt;v&lt;^&gt;&gt;^^^^&gt;&gt;&gt;v^&lt;&gt;vvv^&gt;&lt;v&lt;&lt;&lt;&gt;^^^vv^&lt;vvv&gt;^&gt;v&lt;^^^^v&lt;&gt;^&gt;vvvv&gt;&lt;&gt;&gt;v^&lt;&lt;^^^^^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^&gt;&lt;^&gt;&lt;&gt;&gt;&gt;&lt;&gt;^^&lt;&lt;^^v&gt;&gt;&gt;&lt;^&lt;v&gt;^&lt;vv&gt;&gt;v&gt;&gt;&gt;^v&gt;&lt;&gt;^v&gt;&lt;&lt;&lt;&lt;v&gt;&gt;v&lt;v&lt;v&gt;vvv&gt;^&lt;&gt;&lt;&lt;&gt;^&gt;&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^&gt;&gt;&lt;&gt;^v&lt;&gt;&lt;^vvv&lt;^^&lt;&gt;&lt;v&lt;&lt;&lt;&lt;&lt;&gt;&lt;^v&lt;&lt;&lt;&gt;&lt;&lt;&lt;^^&lt;v&lt;^^^&gt;&lt;^&gt;&gt;^&lt;v^&gt;&lt;&lt;&lt;^&gt;&gt;^v&lt;v^v&lt;v^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&gt;^&gt;&gt;^v&gt;vv&gt;^&lt;&lt;^v&lt;&gt;&gt;&lt;&lt;&gt;&lt;&lt;v&lt;&lt;v&gt;&lt;&gt;v&lt;^vv&lt;&lt;&lt;&gt;^^v^&gt;^^&gt;&gt;&gt;&lt;&lt;^v&gt;&gt;v^v&gt;&lt;^^&gt;&gt;^&lt;&gt;vv^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;&gt;&lt;^^&gt;^^^&lt;&gt;&lt;vvvvv^v&lt;v&lt;&lt;&gt;^v&lt;v&gt;v&lt;&lt;^&gt;&lt;&lt;&gt;&lt;&lt;&gt;&lt;&lt;&lt;^^&lt;&lt;&lt;^&lt;&lt;&gt;&gt;&lt;&lt;&gt;&lt;^^^&gt;^^&lt;&gt;^&gt;v&lt;&gt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^^&gt;vv&lt;^v^v&lt;vv&gt;^&lt;&gt;&lt;v&lt;^v&gt;^^^&gt;&gt;&gt;^^vvv^&gt;vvv&lt;&gt;&gt;&gt;^&lt;^&gt;&gt;&gt;&gt;&gt;^&lt;&lt;^v&gt;^vvv&lt;&gt;^&lt;&gt;&lt;&lt;v&gt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">v^^&gt;&gt;&gt;&lt;&lt;^^&lt;&gt;&gt;^v^&lt;v^vv&lt;&gt;v^&lt;&lt;&gt;^&lt;^v^v&gt;&lt;^&lt;&lt;&lt;&gt;&lt;&lt;^&lt;v&gt;&lt;v&lt;&gt;vv&gt;&gt;v&gt;&lt;v^&lt;vv&lt;&gt;v^&lt;&lt;^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">le_entrada</span>(entrada: str) <span style="color:#f92672">-&gt;</span> tuple[str, str]:
+</span></span><span style="display:flex;"><span> labirinto <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> comandos <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> linha <span style="color:#f92672">in</span> entrada<span style="color:#f92672">.</span>splitlines():
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> linha<span style="color:#f92672">.</span>strip():
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> linha<span style="color:#f92672">.</span>startswith(<span style="color:#e6db74">&#34;#&#34;</span>):
+</span></span><span style="display:flex;"><span> labirinto<span style="color:#f92672">.</span>append(linha)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>:
+</span></span><span style="display:flex;"><span> comandos<span style="color:#f92672">.</span>append(linha)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> labirinto, comandos
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Vector2dComSimbolo</span>(Vector2d):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x, símbolo):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>símbolo <span style="color:#f92672">=</span> símbolo
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __add__(self, other):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> Vector2dComSimbolo(self<span style="color:#f92672">.</span>y <span style="color:#f92672">+</span> other<span style="color:#f92672">.</span>y, self<span style="color:#f92672">.</span>x <span style="color:#f92672">+</span> other<span style="color:#f92672">.</span>x, self<span style="color:#f92672">.</span>símbolo)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __sub__(self, other):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> Vector2dComSimbolo(self<span style="color:#f92672">.</span>y <span style="color:#f92672">-</span> other<span style="color:#f92672">.</span>y, self<span style="color:#f92672">.</span>x <span style="color:#f92672">-</span> other<span style="color:#f92672">.</span>x, self<span style="color:#f92672">.</span>símbolo)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Caixas</span>(Vector2dComSimbolo):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x, símbolo<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;O&#34;</span>):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, símbolo)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Parede</span>(Vector2dComSimbolo):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, <span style="color:#e6db74">&#34;#&#34;</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Robô</span>(Vector2dComSimbolo):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, <span style="color:#e6db74">&#34;@&#34;</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Armazém</span>(Labirinto):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">inicie_desenho</span>(self, fundo<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;.&#34;</span>):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>fundo <span style="color:#f92672">=</span> fundo
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>desenho <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> _ <span style="color:#f92672">in</span> range(self<span style="color:#f92672">.</span>max_y):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>desenho<span style="color:#f92672">.</span>append([fundo] <span style="color:#f92672">*</span> self<span style="color:#f92672">.</span>max_x)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">desenhe</span>(self, objetos: list[Vector2dComSimbolo]):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> objeto <span style="color:#f92672">in</span> objetos:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>desenho[objeto<span style="color:#f92672">.</span>y][objeto<span style="color:#f92672">.</span>x] <span style="color:#f92672">=</span> objeto<span style="color:#f92672">.</span>símbolo
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">dump_desenho</span>(self, output<span style="color:#f92672">=</span>sys<span style="color:#f92672">.</span>stdout):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> linha <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>desenho:
+</span></span><span style="display:flex;"><span> print(<span style="color:#e6db74">&#34;&#34;</span><span style="color:#f92672">.</span>join(linha), file<span style="color:#f92672">=</span>output)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __iter__(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> iter(self<span style="color:#f92672">.</span>labirinto)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Problema</span>:
+</span></span><span style="display:flex;"><span> DIREÇÕES <span style="color:#f92672">=</span> {
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;&lt;&#34;</span>: Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;^&#34;</span>: Vector2d(<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;&gt;&#34;</span>: Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;v&#34;</span>: Vector2d(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>),
+</span></span><span style="display:flex;"><span> }
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, labirinto: list[str], comandos: list[str]):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>comandos <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;</span><span style="color:#f92672">.</span>join(comandos)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém <span style="color:#f92672">=</span> Armazém(labirinto)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>monta()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">monta</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>paredes <span style="color:#f92672">=</span> set()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas <span style="color:#f92672">=</span> dict()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> y, linha <span style="color:#f92672">in</span> enumerate(self<span style="color:#f92672">.</span>armazém):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> x, char <span style="color:#f92672">in</span> enumerate(linha):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;#&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>paredes<span style="color:#f92672">.</span>add(Parede(y, x))
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;O&#34;</span>:
+</span></span><span style="display:flex;"><span> caixa <span style="color:#f92672">=</span> Caixas(y, x)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas[caixa] <span style="color:#f92672">=</span> caixa
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;@&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">=</span> Robô(y, x)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">desenha</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>inicie_desenho()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>desenhe(self<span style="color:#f92672">.</span>paredes)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>desenhe(self<span style="color:#f92672">.</span>caixas)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>desenhe([self<span style="color:#f92672">.</span>robô])
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>dump_desenho()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">executa_comandos</span>(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> comando <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>comandos:
+</span></span><span style="display:flex;"><span> direção <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>DIREÇÕES[comando]
+</span></span><span style="display:flex;"><span> nova_posição <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">+</span> direção
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> nova_posição <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> self<span style="color:#f92672">.</span>empurra(nova_posição, direção):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">elif</span> nova_posição <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>paredes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">+=</span> direção
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">empurra</span>(self, posição: Vector2d, direção: Vector2d, executa: bool <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>):
+</span></span><span style="display:flex;"><span> nova_posição <span style="color:#f92672">=</span> posição <span style="color:#f92672">+</span> direção
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> nova_posição <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>paredes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> nova_posição <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> pode_empurrar <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>empurra(nova_posição, direção, executa)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> pode_empurrar:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> executa:
+</span></span><span style="display:flex;"><span> caixa <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>caixas<span style="color:#f92672">.</span>pop(posição)
+</span></span><span style="display:flex;"><span> caixa<span style="color:#f92672">.</span>y <span style="color:#f92672">=</span> nova_posição<span style="color:#f92672">.</span>y
+</span></span><span style="display:flex;"><span> caixa<span style="color:#f92672">.</span>x <span style="color:#f92672">=</span> nova_posição<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas[caixa] <span style="color:#f92672">=</span> caixa
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">gps_caixas</span>(self):
+</span></span><span style="display:flex;"><span> soma <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> caixas <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> soma <span style="color:#f92672">+=</span> caixas<span style="color:#f92672">.</span>y <span style="color:#f92672">*</span> <span style="color:#ae81ff">100</span> <span style="color:#f92672">+</span> caixas<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> soma
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">prompt</span>(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">while</span> <span style="color:#66d9ef">True</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>desenha()
+</span></span><span style="display:flex;"><span> comandos <span style="color:#f92672">=</span> input(<span style="color:#e6db74">&#34;Comandos: &#34;</span>)<span style="color:#f92672">.</span>lower()<span style="color:#f92672">.</span>strip()
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> comandos:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>comandos <span style="color:#f92672">=</span> comandos
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Problema2</span>(Problema):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, labirinto: list[str], comandos: list[str]):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(labirinto, comandos)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>max_x <span style="color:#f92672">*=</span> <span style="color:#ae81ff">2</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">monta</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>paredes <span style="color:#f92672">=</span> set()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas <span style="color:#f92672">=</span> dict()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> y, linha <span style="color:#f92672">in</span> enumerate(self<span style="color:#f92672">.</span>armazém):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> x, char <span style="color:#f92672">in</span> enumerate(linha):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;#&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>paredes<span style="color:#f92672">.</span>add(Parede(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>))
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>paredes<span style="color:#f92672">.</span>add(Parede(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>))
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;O&#34;</span>:
+</span></span><span style="display:flex;"><span> caixa1 <span style="color:#f92672">=</span> Caixas(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>, <span style="color:#e6db74">&#34;[&#34;</span>)
+</span></span><span style="display:flex;"><span> caixa2 <span style="color:#f92672">=</span> Caixas(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>, <span style="color:#e6db74">&#34;]&#34;</span>)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas[caixa1] <span style="color:#f92672">=</span> caixa1
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas[caixa2] <span style="color:#f92672">=</span> caixa2
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;@&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">=</span> Robô(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">empurra</span>(self, posição: Vector2d, direção: Vector2d, executa: bool <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>):
+</span></span><span style="display:flex;"><span> nova_posição <span style="color:#f92672">=</span> posição <span style="color:#f92672">+</span> direção
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> direção <span style="color:#f92672">in</span> [Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>), Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>)] <span style="color:#f92672">or</span> nova_posição <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>paredes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> super()<span style="color:#f92672">.</span>empurra(posição, direção, executa)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> posição <span style="color:#f92672">not</span> <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> caixa1, caixa2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>ache_caixa(posição)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> pode_empurrar1 <span style="color:#f92672">=</span> super()<span style="color:#f92672">.</span>empurra(caixa1, direção, <span style="color:#66d9ef">False</span>)
+</span></span><span style="display:flex;"><span> pode_empurrar2 <span style="color:#f92672">=</span> super()<span style="color:#f92672">.</span>empurra(caixa2, direção, <span style="color:#66d9ef">False</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> pode_empurrar1 <span style="color:#f92672">and</span> pode_empurrar2:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> executa:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>empurra_v(caixa1, caixa2, direção)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">ache_caixa</span>(self, posição):
+</span></span><span style="display:flex;"><span> caixa1 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>caixas[posição]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> caixa1<span style="color:#f92672">.</span>símbolo <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;[&#34;</span>:
+</span></span><span style="display:flex;"><span> caixa2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>caixas[posição <span style="color:#f92672">+</span> Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>)]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>:
+</span></span><span style="display:flex;"><span> caixa2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>caixas[posição <span style="color:#f92672">+</span> Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> caixa1, caixa2
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">empurra_v</span>(self, posição1, posição2, direção):
+</span></span><span style="display:flex;"><span> np1 <span style="color:#f92672">=</span> posição1 <span style="color:#f92672">+</span> direção
+</span></span><span style="display:flex;"><span> np2 <span style="color:#f92672">=</span> posição2 <span style="color:#f92672">+</span> direção
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> np1 <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>empurra_v(<span style="color:#f92672">*</span>self<span style="color:#f92672">.</span>ache_caixa(np1), direção)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> np2 <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>empurra_v(<span style="color:#f92672">*</span>self<span style="color:#f92672">.</span>ache_caixa(np2), direção)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> posição, nova_posição <span style="color:#f92672">in</span> zip([posição1, posição2], [np1, np2]):
+</span></span><span style="display:flex;"><span> caixa <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>caixas<span style="color:#f92672">.</span>pop(posição)
+</span></span><span style="display:flex;"><span> caixa<span style="color:#f92672">.</span>y <span style="color:#f92672">=</span> nova_posição<span style="color:#f92672">.</span>y
+</span></span><span style="display:flex;"><span> caixa<span style="color:#f92672">.</span>x <span style="color:#f92672">=</span> nova_posição<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas[caixa] <span style="color:#f92672">=</span> caixa
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">gps_caixas</span>(self):
+</span></span><span style="display:flex;"><span> soma <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> caixa <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> caixa<span style="color:#f92672">.</span>símbolo <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;[&#34;</span>:
+</span></span><span style="display:flex;"><span> soma <span style="color:#f92672">+=</span> caixa<span style="color:#f92672">.</span>y <span style="color:#f92672">*</span> <span style="color:#ae81ff">100</span> <span style="color:#f92672">+</span> caixa<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> soma
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problema <span style="color:#f92672">=</span> Problema(<span style="color:#f92672">*</span>le_entrada(ENTRADA_TESTE))
+</span></span><span style="display:flex;"><span>problema<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problema<span style="color:#f92672">.</span>gps_caixas()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Parte 1 - teste pequena entrada:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">2028</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problema <span style="color:#f92672">=</span> Problema(<span style="color:#f92672">*</span>le_entrada(ENTRADA))
+</span></span><span style="display:flex;"><span>problema<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problema<span style="color:#f92672">.</span>gps_caixas()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Parte 1 - teste entrada grande:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">10092</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problema <span style="color:#f92672">=</span> Problema(<span style="color:#f92672">*</span>le_entrada(le_arquivo_sem_linhas_em_branco(<span style="color:#e6db74">&#34;15/input.txt&#34;</span>)))
+</span></span><span style="display:flex;"><span>problema<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problema<span style="color:#f92672">.</span>gps_caixas()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Parte 1 - entrada grande:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">1552463</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#75715e"># Parte 2</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problema <span style="color:#f92672">=</span> Problema2(<span style="color:#f92672">*</span>le_entrada(ENTRADA))
+</span></span><span style="display:flex;"><span>problema<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problema<span style="color:#f92672">.</span>gps_caixas()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Parte 2 - teste entrada grande:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">9021</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problema <span style="color:#f92672">=</span> Problema2(<span style="color:#f92672">*</span>le_entrada(le_arquivo_sem_linhas_em_branco(<span style="color:#e6db74">&#34;15/input.txt&#34;</span>)))
+</span></span><span style="display:flex;"><span>problema<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problema<span style="color:#f92672">.</span>gps_caixas()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Parte 2 - entrada grande:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">1554058</span>
+</span></span></code></pre></div>
+
+ Nilo Ney Coutinho Menezes
+ https://blog.nilo.pro.br/tags/python/
+
+
+ Python on Nilo Menezes
+ Recent content in Python on Nilo Menezes
+
+ https://blog.nilo.pro.br/tags/python/index.xml
+ 2025-04-17T02:14:49+00:00
+ Nilo Ney Coutinho Menezes &#xa9;2007-2024
+
+
+
+
+ My First Game with Carimbo, My Homemade Engine, For my Son
+
+ https://nullonerror.org/2024/10/08/my-first-game-with-carimbo
+ 2024-10-08T00:00:00+00:00
+ <p><a href="https://youtu.be/nVRzCstyspQ"><img src="https://nullonerror.org/public/2024-10-08-my-first-game-with-carimbo/megarick.webp" alt="MegaRick Game" class="center" /></a>
+<a href="https://youtu.be/nVRzCstyspQ">https://youtu.be/nVRzCstyspQ</a></p>
+
+<h3 id="tldr">TL;DR</h3>
+
+<p>After a while, I decided to resume work on <a href="https://github.com/carimbolabs/carimbo">my game engine</a>.</p>
+
+<p>I made a game for my son. I could have used an existing engine, but I chose to write everything from scratch because code is like pasta—it’s much better and more enjoyable when homemade.</p>
+
+<p>This actually reminds me of when my father used to build my toys, from kites to wooden slides. Good memories. I have decided to do the same using what I know: programming.</p>
+
+<p>You can watch it here or <a href="https://carimbo.run">play it here</a>, (runs in the browser thanks to WebAssembly), use <code class="language-plaintext highlighter-rouge">A</code> and <code class="language-plaintext highlighter-rouge">D</code> for moving around and <code class="language-plaintext highlighter-rouge">space</code> to shoot.</p>
+
+<p>The engine was written in C++17, and the games are in Lua. The engine exposes some primitives to the Lua VM, which in turn coordinates the entire game.</p>
+
+<p><a href="https://github.com/willtobyte/megarick">Game source code</a> and <a href="https://github.com/willtobyte/carimbo">engine source code</a>.</p>
+
+<p>Artwork by <a href="https://www.fiverr.com/yuugenpixie">Aline Cardoso @yuugenpixie</a>.</p>
+
+<h3 id="result">Result</h3>
+
+<p><img src="https://nullonerror.org/public/2024-10-08-my-first-game-with-carimbo/play.jpeg" alt="Henrique Playing" class="center" /></p>
+
+<h3 id="going-deep">Going Deep</h3>
+
+<p>The entire game is scripted in <a href="https://www.lua.org">Lua</a>.</p>
+
+<p>First, we create the engine itself.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">local</span> <span class="n">engine</span> <span class="o">=</span> <span class="n">EngineFactory</span><span class="p">.</span><span class="n">new</span><span class="p">()</span>
+ <span class="p">:</span><span class="n">set_title</span><span class="p">(</span><span class="s2">"Mega Rick"</span><span class="p">)</span>
+ <span class="p">:</span><span class="n">set_width</span><span class="p">(</span><span class="mi">1920</span><span class="p">)</span>
+ <span class="p">:</span><span class="n">set_height</span><span class="p">(</span><span class="mi">1080</span><span class="p">)</span>
+ <span class="p">:</span><span class="n">set_fullscreen</span><span class="p">(</span><span class="kc">false</span><span class="p">)</span>
+ <span class="p">:</span><span class="n">create</span><span class="p">()</span>
+</code></pre></div></div>
+
+<p>Then we point to some resources for asset prefetching; they are loaded lazily to avoid impacting the <em>game loop</em>.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">engine</span><span class="p">:</span><span class="n">prefetch</span><span class="p">({</span>
+ <span class="s2">"blobs/bomb1.ogg"</span><span class="p">,</span>
+ <span class="s2">"blobs/bomb2.ogg"</span><span class="p">,</span>
+ <span class="s2">"blobs/bullet.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/candle.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/explosion.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/octopus.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/player.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/princess.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/ship.png"</span>
+<span class="p">})</span>
+</code></pre></div></div>
+
+<p>We created the postal service, which is something I borrowed from languages like <em>Erlang</em>, where an entity can send messages to any other. As we’ll see below, the bullet sends a hit message when it collides with the octopus, and the octopus, upon receiving the hit, decrements its own health.</p>
+
+<p>We also obtain the SoundManager to play sounds and spawn the main entities.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">local</span> <span class="n">postal</span> <span class="o">=</span> <span class="n">PostalService</span><span class="p">.</span><span class="n">new</span><span class="p">()</span>
+<span class="kd">local</span> <span class="n">soundmanager</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">soundmanager</span><span class="p">()</span>
+<span class="kd">local</span> <span class="n">octopus</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"octopus"</span><span class="p">)</span>
+<span class="kd">local</span> <span class="n">player</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"player"</span><span class="p">)</span>
+<span class="kd">local</span> <span class="n">princess</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"princess"</span><span class="p">)</span>
+<span class="kd">local</span> <span class="n">candle1</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"candle"</span><span class="p">)</span>
+<span class="kd">local</span> <span class="n">candle2</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"candle"</span><span class="p">)</span>
+</code></pre></div></div>
+
+<p>It’s not a triple-A title, but I’m very careful with resource management. A widely known technique is to create an object pool that you can reuse. In the code below, we limit it to a maximum of 3 bullets present on the screen.</p>
+
+<p>The <code class="language-plaintext highlighter-rouge">on_update</code> method is a callback called each loop in the engine. In the case of the bullet, I check if it has approached the green octopus. If so, I send a message to it indicating that it received a hit.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">_</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">3</span> <span class="k">do</span>
+ <span class="kd">local</span> <span class="n">bullet</span> <span class="o">=</span> <span class="n">entitymanager</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"bullet"</span><span class="p">)</span>
+ <span class="n">bullet</span><span class="p">.</span><span class="n">placement</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="o">-</span><span class="mi">128</span><span class="p">,</span> <span class="o">-</span><span class="mi">128</span><span class="p">)</span>
+ <span class="n">bullet</span><span class="p">:</span><span class="n">on_collision</span><span class="p">(</span><span class="s2">"octopus"</span><span class="p">,</span> <span class="k">function</span><span class="p">(</span><span class="n">self</span><span class="p">)</span>
+ <span class="n">self</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">unset</span><span class="p">()</span>
+ <span class="n">self</span><span class="p">.</span><span class="n">placement</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="o">-</span><span class="mi">128</span><span class="p">,</span> <span class="o">-</span><span class="mi">128</span><span class="p">)</span>
+ <span class="n">postalservice</span><span class="p">:</span><span class="n">post</span><span class="p">(</span><span class="n">Mail</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="n">octopus</span><span class="p">,</span> <span class="s2">"bullet"</span><span class="p">,</span> <span class="s2">"hit"</span><span class="p">))</span>
+ <span class="nb">table.insert</span><span class="p">(</span><span class="n">bullet_pool</span><span class="p">,</span> <span class="n">self</span><span class="p">)</span>
+ <span class="k">end</span><span class="p">)</span>
+ <span class="nb">table.insert</span><span class="p">(</span><span class="n">bullet_pool</span><span class="p">,</span> <span class="n">bullet</span><span class="p">)</span>
+ <span class="k">end</span>
+</code></pre></div></div>
+
+<p>On the octopus’s side, when it receives a “hit” message, the entity triggers an explosion animation, switches to attack mode, and decreases its health by 1. If it reaches 0, it changes the animation to “dead”.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">octopus</span> <span class="o">=</span> <span class="n">entitymanager</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"octopus"</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">.</span><span class="n">kv</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"life"</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">.</span><span class="n">placement</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="mi">1200</span><span class="p">,</span> <span class="mi">622</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"idle"</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">:</span><span class="n">on_mail</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="kd">local</span> <span class="n">behavior</span> <span class="o">=</span> <span class="n">behaviors</span><span class="p">[</span><span class="n">message</span><span class="p">]</span>
+ <span class="k">if</span> <span class="n">behavior</span> <span class="k">then</span>
+ <span class="n">behavior</span><span class="p">(</span><span class="n">self</span><span class="p">)</span>
+ <span class="k">end</span>
+<span class="k">end</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">.</span><span class="n">kv</span><span class="p">:</span><span class="n">subscribe</span><span class="p">(</span><span class="s2">"life"</span><span class="p">,</span> <span class="k">function</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
+ <span class="n">vitality</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="nb">string.format</span><span class="p">(</span><span class="s2">"%02d-"</span><span class="p">,</span> <span class="nb">math.max</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="mi">0</span><span class="p">)))</span>
+
+ <span class="k">if</span> <span class="n">value</span> <span class="o"><=</span> <span class="mi">0</span> <span class="k">then</span>
+ <span class="n">octopus</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"dead"</span><span class="p">)</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">timer</span> <span class="k">then</span>
+ <span class="n">timemanager</span><span class="p">:</span><span class="n">singleshot</span><span class="p">(</span><span class="mi">3000</span><span class="p">,</span> <span class="k">function</span><span class="p">()</span>
+ <span class="kd">local</span> <span class="k">function</span> <span class="nf">destroy</span><span class="p">(</span><span class="n">pool</span><span class="p">)</span>
+ <span class="k">for</span> <span class="n">i</span> <span class="o">=</span> <span class="o">#</span><span class="n">pool</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="k">do</span>
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">pool</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
+ <span class="nb">table.remove</span><span class="p">(</span><span class="n">pool</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
+ <span class="n">pool</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="kc">nil</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+
+ <span class="n">destroy</span><span class="p">(</span><span class="n">bullet_pool</span><span class="p">)</span>
+ <span class="n">destroy</span><span class="p">(</span><span class="n">explosion_pool</span><span class="p">)</span>
+ <span class="n">destroy</span><span class="p">(</span><span class="n">jet_pool</span><span class="p">)</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">octopus</span><span class="p">)</span>
+ <span class="n">octopus</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">player</span><span class="p">)</span>
+ <span class="n">player</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">princess</span><span class="p">)</span>
+ <span class="n">princess</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">candle1</span><span class="p">)</span>
+ <span class="n">candle1</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">candle2</span><span class="p">)</span>
+ <span class="n">candle2</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">floor</span><span class="p">)</span>
+ <span class="n">floor</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">overlay</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">vitality</span><span class="p">)</span>
+ <span class="n">vitality</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">overlay</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">online</span><span class="p">)</span>
+ <span class="n">online</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">scenemanager</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"gameover"</span><span class="p">)</span>
+
+ <span class="nb">collectgarbage</span><span class="p">(</span><span class="s2">"collect"</span><span class="p">)</span>
+
+ <span class="n">resourcemanager</span><span class="p">:</span><span class="n">flush</span><span class="p">()</span>
+ <span class="k">end</span><span class="p">)</span>
+ <span class="n">timer</span> <span class="o">=</span> <span class="kc">true</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+<span class="k">end</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">:</span><span class="n">on_animationfinished</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="n">self</span><span class="p">)</span>
+ <span class="n">self</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"idle"</span><span class="p">)</span>
+<span class="k">end</span><span class="p">)</span>
+</code></pre></div></div>
+
+<p>And finally, the player’s <code class="language-plaintext highlighter-rouge">on_update</code>, where I apply the velocity if any movement keys are pressed, or set the animation to idle if none are pressed.</p>
+
+<p>We also have the projectile firing, where the fire function is called, taking an instance of a bullet from the pool, placing it in a semi-random position, and applying velocity.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">function</span> <span class="nf">loop</span><span class="p">()</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">player</span> <span class="k">then</span>
+ <span class="k">return</span>
+ <span class="k">end</span>
+
+ <span class="n">player</span><span class="p">.</span><span class="n">velocity</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">0</span>
+
+ <span class="k">if</span> <span class="n">statemanager</span><span class="p">:</span><span class="n">is_keydown</span><span class="p">(</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">left</span><span class="p">)</span> <span class="k">then</span>
+ <span class="n">player</span><span class="p">.</span><span class="n">reflection</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="n">Reflection</span><span class="p">.</span><span class="n">horizontal</span><span class="p">)</span>
+ <span class="n">player</span><span class="p">.</span><span class="n">velocity</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="o">-</span><span class="mi">360</span>
+ <span class="k">elseif</span> <span class="n">statemanager</span><span class="p">:</span><span class="n">is_keydown</span><span class="p">(</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">right</span><span class="p">)</span> <span class="k">then</span>
+ <span class="n">player</span><span class="p">.</span><span class="n">reflection</span><span class="p">:</span><span class="n">unset</span><span class="p">()</span>
+ <span class="n">player</span><span class="p">.</span><span class="n">velocity</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">360</span>
+ <span class="k">end</span>
+
+ <span class="n">player</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="n">player</span><span class="p">.</span><span class="n">velocity</span><span class="p">.</span><span class="n">x</span> <span class="o">~=</span> <span class="mi">0</span> <span class="ow">and</span> <span class="s2">"run"</span> <span class="ow">or</span> <span class="s2">"idle"</span><span class="p">)</span>
+
+ <span class="k">if</span> <span class="n">statemanager</span><span class="p">:</span><span class="n">is_keydown</span><span class="p">(</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">space</span><span class="p">)</span> <span class="k">then</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">key_states</span><span class="p">[</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">space</span><span class="p">]</span> <span class="k">then</span>
+ <span class="n">key_states</span><span class="p">[</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">space</span><span class="p">]</span> <span class="o">=</span> <span class="kc">true</span>
+
+ <span class="c1">-- player.velocity.y = -360</span>
+
+ <span class="k">if</span> <span class="n">octopus</span><span class="p">.</span><span class="n">kv</span><span class="p">:</span><span class="n">get</span><span class="p">(</span><span class="s2">"life"</span><span class="p">)</span> <span class="o"><=</span> <span class="mi">0</span> <span class="k">then</span>
+ <span class="k">return</span>
+ <span class="k">end</span>
+
+ <span class="k">if</span> <span class="o">#</span><span class="n">bullet_pool</span> <span class="o">></span> <span class="mi">0</span> <span class="k">then</span>
+ <span class="kd">local</span> <span class="n">bullet</span> <span class="o">=</span> <span class="nb">table.remove</span><span class="p">(</span><span class="n">bullet_pool</span><span class="p">)</span>
+ <span class="kd">local</span> <span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="n">player</span><span class="p">.</span><span class="n">x</span> <span class="o">+</span> <span class="n">player</span><span class="p">.</span><span class="n">size</span><span class="p">.</span><span class="n">width</span><span class="p">)</span> <span class="o">+</span> <span class="mi">100</span>
+ <span class="kd">local</span> <span class="n">y</span> <span class="o">=</span> <span class="n">player</span><span class="p">.</span><span class="n">y</span> <span class="o">+</span> <span class="mi">10</span>
+ <span class="kd">local</span> <span class="n">offset_y</span> <span class="o">=</span> <span class="p">(</span><span class="nb">math.random</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span> <span class="o">*</span> <span class="mi">30</span>
+
+ <span class="n">bullet</span><span class="p">.</span><span class="n">placement</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="n">offset_y</span><span class="p">)</span>
+ <span class="n">bullet</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"default"</span><span class="p">)</span>
+ <span class="n">bullet</span><span class="p">.</span><span class="n">velocity</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">800</span>
+
+ <span class="kd">local</span> <span class="n">sound</span> <span class="o">=</span> <span class="s2">"bomb"</span> <span class="o">..</span> <span class="nb">math.random</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
+ <span class="n">soundmanager</span><span class="p">:</span><span class="n">play</span><span class="p">(</span><span class="n">sound</span><span class="p">)</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+ <span class="k">else</span>
+ <span class="n">key_states</span><span class="p">[</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">space</span><span class="p">]</span> <span class="o">=</span> <span class="kc">false</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</code></pre></div></div>
+
+<h3 id="in-the-end">In The End</h3>
+
+<p>In the end, he requested some <em>minor</em> tweaks, such as the projectile being the princess sprite, the target being the player, and the entity that shoots being the green octopus. 🤷♂️</p>
+
+<p><img src="https://nullonerror.org/public/2024-10-08-my-first-game-with-carimbo/tweaks.jpeg" alt="Tweaks" class="center" /></p>
+
+ Rodrigo Delduca
+ rodrigo@delduca.org
+ https://nullonerror.org/
+
+
+ NULL on error
+ NULL on error
+
+ https://nullonerror.org/feed.xml
+ 2025-04-17T02:14:57+00:00
+
+
+
+
+ How I saved a few dozen dollars by migrating my personal projects from the cloud to a Raspberry Pi
+
+ https://nullonerror.org/2024/06/05/how-i-saved-a-few-dozen-dollars-by-migrating-my-personal-projects-from-the-cloud-to-a-raspberry-pi
+ 2024-06-05T00:00:00+00:00
+ <h3 id="intro">Intro</h3>
+
+<p>Since the first URL shorteners emerged, that’s always drawn me in for some reason, perhaps because I figured out on my own that they encoded the primary key in base36 (maybe).</p>
+
+<p>So around ~2010, I decided to make my own. It was called “encurta.ae” (make it short there in Portuguese), and it was based on AppEngine. Even back then, I was quite fond of serverless and such.</p>
+
+<p>It worked great locally, but when I deployed it, the datastore IDs were too large, causing the URLs to be too long.</p>
+
+<p>Fast-forward to nowadays, I’ve decided to bring this project to life again. This time with a twist: when shortening the URL, the shortener would take a screenshot of the website and embed Open Graph tags in the redirect URL. This way, when shared on a social network, the link would display a title, description, and thumbnail that accurately represent what the user is about to open.</p>
+
+<h3 id="stack">Stack</h3>
+
+<p>Typically, I use serverless for my personal projects because they scale to zero, thus eliminating costs when not in use. However, after working with serverless for many years, I’ve been wanting to experiment with a more grounded and down-to-earth approach to development and deployment.</p>
+
+<ul>
+ <li>I opted to use <a href="https://go.dev/">Go</a>, with several <a href="https://en.wikipedia.org/wiki/Coroutine">goroutines</a> performing tasks in parallel, and a purely written work queue mechanism.</li>
+ <li><a href="https://playwright.dev/">Playwright</a> & Chromium for automation and screenshot.</li>
+ <li><a href="https://sqlite.org/">SQLite</a> for the database (it’s simple)</li>
+ <li><a href="https://www.backblaze.com/">Backblaze</a> for storage</li>
+ <li><a href="https://bunny.net/">BunnyCDN</a> for cotent delivery</li>
+ <li><a href="https://www.papertrail.com/">Papertrail</a> for logging</li>
+</ul>
+
+<p>Deployment is done using Docker Compose, via SSH using the DOCKER_HOST environment variable pointing directly to a <a href="https://www.raspberrypi.com/">Raspberry Pi</a> that I had bought and never used before. Now it saves me $5 per month, and I can keep a limited number of projects running on it.</p>
+
+<p>And then you might ask: How do you expose it to the internet? I use <a href="https://www.cloudflare.com/products/tunnel/">Cloudflare Tunnel</a>; the setup is simple and creates a direct connection between the Raspberry Pi and the nearest <a href="https://en.wikipedia.org/wiki/Point_of_presence">Point of Presence</a>.</p>
+
+<p>This type of hosting is extremely advantageous because the server’s IP and/or ports are never revealed; it stays behind my firewall. Everything goes through Cloudflare.</p>
+
+<p>I have more than one Docker Compose file, and that’s what’s coolest. Locally, I run one, for deployment I instruct the Compose to read two others for logging and tunneling.</p>
+
+<p><code class="language-plaintext highlighter-rouge">docker-compose.yaml</code></p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span>
+ <span class="na">app</span><span class="pi">:</span>
+ <span class="na">build</span><span class="pi">:</span> <span class="s">.</span>
+ <span class="na">env_file</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">.env</span>
+ <span class="na">ports</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s2">"</span><span class="s">8000:8000"</span>
+ <span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span>
+ <span class="na">volumes</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">data:/data</span>
+ <span class="na">tmpfs</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">/tmp</span>
+<span class="na">volumes</span><span class="pi">:</span>
+ <span class="na">data</span><span class="pi">:</span>
+</code></pre></div></div>
+
+<p><code class="language-plaintext highlighter-rouge">docker-compose.logging.yaml</code></p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span>
+ <span class="na">app</span><span class="pi">:</span>
+ <span class="na">logging</span><span class="pi">:</span>
+ <span class="na">driver</span><span class="pi">:</span> <span class="s">syslog</span>
+ <span class="na">options</span><span class="pi">:</span>
+ <span class="na">syslog-address</span><span class="pi">:</span> <span class="s2">"</span><span class="s">udp://logs2.papertrailapp.com:XXX"</span>
+</code></pre></div></div>
+
+<p><code class="language-plaintext highlighter-rouge">docker-compose.cloudflare.yaml</code></p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span>
+ <span class="na">tunnel</span><span class="pi">:</span>
+ <span class="na">image</span><span class="pi">:</span> <span class="s">cloudflare/cloudflared</span>
+ <span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span>
+ <span class="na">command</span><span class="pi">:</span> <span class="s">tunnel run</span>
+ <span class="na">environment</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">TUNNEL_TOKEN=yourtoken</span>
+</code></pre></div></div>
+
+<p>Then for deploying</p>
+
+<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">DOCKER_HOST</span><span class="o">=</span>ssh://pi@192.168.0.10 docker compose <span class="nt">--file</span> docker-compose.yaml <span class="nt">--file</span> docker-compose.logging.yaml <span class="nt">--file</span> docker-compose.cloudflare.yaml up <span class="nt">--build</span> <span class="nt">--detach</span>
+</code></pre></div></div>
+
+<p>Example of the “worker queue”</p>
+
+<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">functions</span>
+
+<span class="k">import</span> <span class="p">(</span>
+ <span class="s">"context"</span>
+ <span class="s">"database/sql"</span>
+ <span class="s">"fmt"</span>
+ <span class="s">"os"</span>
+ <span class="s">"os/exec"</span>
+ <span class="s">"path/filepath"</span>
+ <span class="s">"sync"</span>
+ <span class="s">"time"</span>
+
+ <span class="n">google</span> <span class="s">"cloud.google.com/go/vision/apiv1"</span>
+ <span class="n">visionpb</span> <span class="s">"cloud.google.com/go/vision/v2/apiv1/visionpb"</span>
+ <span class="s">"github.com/martinlindhe/base36"</span>
+ <span class="s">"github.com/minio/minio-go/v7"</span>
+ <span class="s">"github.com/minio/minio-go/v7/pkg/credentials"</span>
+ <span class="s">"github.com/playwright-community/playwright-go"</span>
+ <span class="s">"go.uber.org/zap"</span>
+ <span class="s">"google.golang.org/api/option"</span>
+ <span class="n">log</span> <span class="s">"skhaz.dev/urlshortnen/logging"</span>
+<span class="p">)</span>
+
+<span class="k">var</span> <span class="p">(</span>
+ <span class="n">accessKeyID</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"BACKBLAZE_ACCESS_ID"</span><span class="p">)</span>
+ <span class="n">secretAccessKey</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"BACKBLAZE_APPLICATION_KEY"</span><span class="p">)</span>
+ <span class="n">bucket</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"BACKBLAZE_BUCKET"</span><span class="p">)</span>
+ <span class="n">endpoint</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"BACKBLAZE_ENDPOINT"</span><span class="p">)</span>
+ <span class="n">useSSL</span> <span class="o">=</span> <span class="no">true</span>
+ <span class="n">extension</span> <span class="o">=</span> <span class="s">"webp"</span>
+ <span class="n">mimetype</span> <span class="o">=</span> <span class="s">"image/webp"</span>
+ <span class="n">quality</span> <span class="o">=</span> <span class="s">"50"</span>
+<span class="p">)</span>
+
+<span class="k">type</span> <span class="n">WorkerFunctions</span> <span class="k">struct</span> <span class="p">{</span>
+ <span class="n">db</span> <span class="o">*</span><span class="n">sql</span><span class="o">.</span><span class="n">DB</span>
+ <span class="n">vision</span> <span class="o">*</span><span class="n">google</span><span class="o">.</span><span class="n">ImageAnnotatorClient</span>
+ <span class="n">mc</span> <span class="o">*</span><span class="n">minio</span><span class="o">.</span><span class="n">Client</span>
+ <span class="n">browser</span> <span class="n">playwright</span><span class="o">.</span><span class="n">BrowserContext</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="n">Worker</span><span class="p">(</span><span class="n">db</span> <span class="o">*</span><span class="n">sql</span><span class="o">.</span><span class="n">DB</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">defer</span> <span class="k">func</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">if</span> <span class="n">r</span> <span class="o">:=</span> <span class="nb">recover</span><span class="p">();</span> <span class="n">r</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"worker panic"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Any</span><span class="p">(</span><span class="s">"error"</span><span class="p">,</span> <span class="n">r</span><span class="p">))</span>
+ <span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span> <span class="o">*</span> <span class="m">10</span><span class="p">)</span>
+ <span class="k">go</span> <span class="n">Worker</span><span class="p">(</span><span class="n">db</span><span class="p">)</span>
+ <span class="p">}</span>
+ <span class="p">}()</span>
+
+ <span class="n">ctx</span> <span class="o">:=</span> <span class="n">context</span><span class="o">.</span><span class="n">Background</span><span class="p">()</span>
+
+ <span class="n">vision</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">google</span><span class="o">.</span><span class="n">NewImageAnnotatorClient</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">option</span><span class="o">.</span><span class="n">WithCredentialsJSON</span><span class="p">([]</span><span class="kt">byte</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"GOOGLE_CREDENTIALS"</span><span class="p">))))</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to create vision client"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">vision</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="n">mc</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">minio</span><span class="o">.</span><span class="n">New</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="o">&</span><span class="n">minio</span><span class="o">.</span><span class="n">Options</span><span class="p">{</span>
+ <span class="n">Creds</span><span class="o">:</span> <span class="n">credentials</span><span class="o">.</span><span class="n">NewStaticV4</span><span class="p">(</span><span class="n">accessKeyID</span><span class="p">,</span> <span class="n">secretAccessKey</span><span class="p">,</span> <span class="s">""</span><span class="p">),</span>
+ <span class="n">Secure</span><span class="o">:</span> <span class="n">useSSL</span><span class="p">,</span>
+ <span class="p">})</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to create minio client"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">pw</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">playwright</span><span class="o">.</span><span class="n">Run</span><span class="p">()</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to launch playwright"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="c">//nolint:golint,errcheck</span>
+ <span class="k">defer</span> <span class="n">pw</span><span class="o">.</span><span class="n">Stop</span><span class="p">()</span>
+
+ <span class="n">userDataDir</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">MkdirTemp</span><span class="p">(</span><span class="s">""</span><span class="p">,</span> <span class="s">"chromium"</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to create temporary directory"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">os</span><span class="o">.</span><span class="n">RemoveAll</span><span class="p">(</span><span class="n">userDataDir</span><span class="p">)</span>
+
+ <span class="n">browser</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">pw</span><span class="o">.</span><span class="n">Chromium</span><span class="o">.</span><span class="n">LaunchPersistentContext</span><span class="p">(</span><span class="n">userDataDir</span><span class="p">,</span> <span class="n">playwright</span><span class="o">.</span><span class="n">BrowserTypeLaunchPersistentContextOptions</span><span class="p">{</span>
+ <span class="n">Args</span><span class="o">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span>
+ <span class="s">"--headless=new"</span><span class="p">,</span>
+ <span class="s">"--no-zygote"</span><span class="p">,</span>
+ <span class="s">"--no-sandbox"</span><span class="p">,</span>
+ <span class="s">"--disable-gpu"</span><span class="p">,</span>
+ <span class="s">"--hide-scrollbars"</span><span class="p">,</span>
+ <span class="s">"--disable-setuid-sandbox"</span><span class="p">,</span>
+ <span class="s">"--disable-dev-shm-usage"</span><span class="p">,</span>
+ <span class="s">"--disable-extensions-except=/opt/extensions/ublock,/opt/extensions/isdncac"</span><span class="p">,</span>
+ <span class="s">"--load-extension=/opt/extensions/ublock,/opt/extensions/isdncac"</span><span class="p">,</span>
+ <span class="p">},</span>
+ <span class="n">DeviceScaleFactor</span><span class="o">:</span> <span class="n">playwright</span><span class="o">.</span><span class="n">Float</span><span class="p">(</span><span class="m">4.0</span><span class="p">),</span>
+ <span class="n">Headless</span><span class="o">:</span> <span class="n">playwright</span><span class="o">.</span><span class="n">Bool</span><span class="p">(</span><span class="no">false</span><span class="p">),</span>
+ <span class="n">Viewport</span><span class="o">:</span> <span class="o">&</span><span class="n">playwright</span><span class="o">.</span><span class="n">Size</span><span class="p">{</span>
+ <span class="n">Width</span><span class="o">:</span> <span class="m">1200</span><span class="p">,</span>
+ <span class="n">Height</span><span class="o">:</span> <span class="m">630</span><span class="p">,</span>
+ <span class="p">},</span>
+ <span class="n">UserAgent</span><span class="o">:</span> <span class="n">playwright</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="s">"Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Googlebot/2.1; +http://www.google.com/bot.html) Chrome/125.0.0.0 Safari/537.36"</span><span class="p">),</span>
+ <span class="p">})</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to launch chromium browser"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">browser</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="n">wf</span> <span class="o">:=</span> <span class="n">WorkerFunctions</span><span class="p">{</span>
+ <span class="n">db</span><span class="o">:</span> <span class="n">db</span><span class="p">,</span>
+ <span class="n">vision</span><span class="o">:</span> <span class="n">vision</span><span class="p">,</span>
+ <span class="n">mc</span><span class="o">:</span> <span class="n">mc</span><span class="p">,</span>
+ <span class="n">browser</span><span class="o">:</span> <span class="n">browser</span><span class="p">,</span>
+ <span class="p">}</span>
+
+ <span class="k">for</span> <span class="p">{</span>
+ <span class="n">start</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">Now</span><span class="p">()</span>
+
+ <span class="k">func</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">wg</span> <span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span>
+ <span class="n">rows</span> <span class="o">*</span><span class="n">sql</span><span class="o">.</span><span class="n">Rows</span>
+ <span class="n">err</span> <span class="kt">error</span>
+ <span class="p">)</span>
+
+ <span class="n">rows</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Query</span><span class="p">(</span><span class="s">"SELECT id, url FROM data WHERE ready = 0 ORDER BY created_at LIMIT 6"</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"error executing query"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">rows</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="k">for</span> <span class="n">rows</span><span class="o">.</span><span class="n">Next</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">var</span> <span class="n">id</span> <span class="kt">int64</span>
+ <span class="k">var</span> <span class="n">url</span> <span class="kt">string</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">=</span> <span class="n">rows</span><span class="o">.</span><span class="n">Scan</span><span class="p">(</span><span class="o">&</span><span class="n">id</span><span class="p">,</span> <span class="o">&</span><span class="n">url</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"error scanning row"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">wg</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
+ <span class="k">go</span> <span class="n">wf</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="o">&</span><span class="n">wg</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">id</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">rows</span><span class="o">.</span><span class="n">Err</span><span class="p">();</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"error during rows iteration"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">wg</span><span class="o">.</span><span class="n">Wait</span><span class="p">()</span>
+ <span class="p">}()</span>
+
+ <span class="n">elapsed</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">Since</span><span class="p">(</span><span class="n">start</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">remaining</span> <span class="o">:=</span> <span class="m">5</span><span class="o">*</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span> <span class="o">-</span> <span class="n">elapsed</span><span class="p">;</span> <span class="n">remaining</span> <span class="o">></span> <span class="m">0</span> <span class="p">{</span>
+ <span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">remaining</span><span class="p">)</span>
+ <span class="p">}</span>
+ <span class="p">}</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="p">(</span><span class="n">wf</span> <span class="o">*</span><span class="n">WorkerFunctions</span><span class="p">)</span> <span class="n">run</span><span class="p">(</span><span class="n">wg</span> <span class="o">*</span><span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span><span class="p">,</span> <span class="n">url</span> <span class="kt">string</span><span class="p">,</span> <span class="n">id</span> <span class="kt">int64</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">defer</span> <span class="n">wg</span><span class="o">.</span><span class="n">Done</span><span class="p">()</span>
+
+ <span class="k">var</span> <span class="n">message</span> <span class="kt">string</span>
+
+ <span class="k">if</span> <span class="n">id</span> <span class="o"><</span> <span class="m">0</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="s">"invalid id: id must be non-negative"</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">ctx</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="n">Background</span><span class="p">()</span>
+ <span class="n">short</span> <span class="o">=</span> <span class="n">base36</span><span class="o">.</span><span class="n">Encode</span><span class="p">(</span><span class="kt">uint64</span><span class="p">(</span><span class="n">id</span><span class="p">))</span>
+ <span class="p">)</span>
+
+ <span class="n">dir</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">MkdirTemp</span><span class="p">(</span><span class="s">""</span><span class="p">,</span> <span class="s">"screenshot"</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to create temporary directory: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">os</span><span class="o">.</span><span class="n">RemoveAll</span><span class="p">(</span><span class="n">dir</span><span class="p">)</span>
+
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">fileName</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"%s.%s"</span><span class="p">,</span> <span class="n">short</span><span class="p">,</span> <span class="n">extension</span><span class="p">)</span>
+ <span class="n">filePath</span> <span class="o">=</span> <span class="n">filepath</span><span class="o">.</span><span class="n">Join</span><span class="p">(</span><span class="n">dir</span><span class="p">,</span> <span class="n">fileName</span><span class="p">)</span>
+ <span class="p">)</span>
+
+ <span class="n">page</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">wf</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">NewPage</span><span class="p">()</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to create new page: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">page</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">page</span><span class="o">.</span><span class="n">Goto</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">playwright</span><span class="o">.</span><span class="n">PageGotoOptions</span><span class="p">{</span>
+ <span class="n">WaitUntil</span><span class="o">:</span> <span class="n">playwright</span><span class="o">.</span><span class="n">WaitUntilStateDomcontentloaded</span><span class="p">,</span>
+ <span class="p">});</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to navigate to url: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span> <span class="o">*</span> <span class="m">5</span><span class="p">)</span>
+
+ <span class="n">title</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">page</span><span class="o">.</span><span class="n">Title</span><span class="p">()</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"could not get title: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">description</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">page</span><span class="o">.</span><span class="n">Locator</span><span class="p">(</span><span class="s">`meta[name="description"]`</span><span class="p">)</span><span class="o">.</span><span class="n">GetAttribute</span><span class="p">(</span><span class="s">"content"</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Info</span><span class="p">(</span><span class="s">"could not get meta description"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="n">description</span> <span class="o">=</span> <span class="s">""</span>
+ <span class="p">}</span>
+
+ <span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">page</span><span class="o">.</span><span class="n">Screenshot</span><span class="p">(</span><span class="n">playwright</span><span class="o">.</span><span class="n">PageScreenshotOptions</span><span class="p">{</span>
+ <span class="n">Path</span><span class="o">:</span> <span class="n">playwright</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="n">filePath</span><span class="p">),</span>
+ <span class="p">});</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to create screenshot: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">fp</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">Open</span><span class="p">(</span><span class="n">filePath</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to open screenshot: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">fp</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="n">image</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">google</span><span class="o">.</span><span class="n">NewImageFromReader</span><span class="p">(</span><span class="n">fp</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to load screenshot: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">annotations</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">wf</span><span class="o">.</span><span class="n">vision</span><span class="o">.</span><span class="n">DetectSafeSearch</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">image</span><span class="p">,</span> <span class="no">nil</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to detect labels: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="k">if</span> <span class="n">annotations</span><span class="o">.</span><span class="n">Adult</span> <span class="o">>=</span> <span class="n">visionpb</span><span class="o">.</span><span class="n">Likelihood_POSSIBLE</span> <span class="o">||</span> <span class="n">annotations</span><span class="o">.</span><span class="n">Violence</span> <span class="o">>=</span> <span class="n">visionpb</span><span class="o">.</span><span class="n">Likelihood_POSSIBLE</span> <span class="o">||</span> <span class="n">annotations</span><span class="o">.</span><span class="n">Racy</span> <span class="o">>=</span> <span class="n">visionpb</span><span class="o">.</span><span class="n">Likelihood_POSSIBLE</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"site is not safe %v"</span><span class="p">,</span> <span class="n">annotations</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">cmd</span> <span class="o">:=</span> <span class="n">exec</span><span class="o">.</span><span class="n">Command</span><span class="p">(</span><span class="s">"convert"</span><span class="p">,</span> <span class="n">filePath</span><span class="p">,</span> <span class="s">"-resize"</span><span class="p">,</span> <span class="s">"50%"</span><span class="p">,</span> <span class="s">"-filter"</span><span class="p">,</span> <span class="s">"Lanczos"</span><span class="p">,</span> <span class="s">"-quality"</span><span class="p">,</span> <span class="n">quality</span><span class="p">,</span> <span class="n">filePath</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">cmd</span><span class="o">.</span><span class="n">Run</span><span class="p">();</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"error during image conversion: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">wf</span><span class="o">.</span><span class="n">mc</span><span class="o">.</span><span class="n">FPutObject</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">bucket</span><span class="p">,</span> <span class="n">fileName</span><span class="p">,</span> <span class="n">filePath</span><span class="p">,</span> <span class="n">minio</span><span class="o">.</span><span class="n">PutObjectOptions</span><span class="p">{</span><span class="n">ContentType</span><span class="o">:</span> <span class="n">mimetype</span><span class="p">})</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to upload file to minio: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">Exec</span><span class="p">(</span><span class="s">"UPDATE data SET ready = 1, title = ?, description = ? WHERE url = ?"</span><span class="p">,</span> <span class="n">title</span><span class="p">,</span> <span class="n">description</span><span class="p">,</span> <span class="n">url</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to update database record: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="k">go</span> <span class="n">warmup</span><span class="p">(</span><span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"%s/%s"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"DOMAIN"</span><span class="p">),</span> <span class="n">short</span><span class="p">))</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="n">setError</span><span class="p">(</span><span class="n">db</span> <span class="o">*</span><span class="n">sql</span><span class="o">.</span><span class="n">DB</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">db</span><span class="o">.</span><span class="n">Exec</span><span class="p">(</span><span class="s">"UPDATE data SET ready = 1, error = ? WHERE url = ?"</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">url</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to update database error record"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="p">}</span>
+<span class="p">}</span>
+</code></pre></div></div>
+
+<p>It’s noticeable that it took a bit more effort than if I had used a task queue. However, it turned out to be quite robust and easy to debug, and for that reason alone, it was worth it.</p>
+
+<h3 id="conclusion">Conclusion</h3>
+
+<p>In sharing the shortened link (<a href="https://takealook.pro/4MP">https://takealook.pro/4MP</a>) for my company <a href="https://ultratech.software/">Ultratech Software</a> on social networks, you can have a really nice preview, as seen below.</p>
+
+<p><img src="https://nullonerror.org/public/2024-06-05-how-i-saved-a-few-dozen-dollars-by-migrating-my-personal-projects-from-the-cloud-to-a-raspberry-pi/takealook.pro.avif" alt="takealook.pro" class="center" /></p>
+
+<p><a href="https://takealook.pro/">Take A Look</a></p>
+
+ Rodrigo Delduca
+ rodrigo@delduca.org
+ https://nullonerror.org/
+
+
+ NULL on error
+ NULL on error
+
+ https://nullonerror.org/feed.xml
+ 2025-04-17T02:14:57+00:00
+
+
+
+
+ My 2024 Setup
+
+ https://nullonerror.org/2024/03/20/my-2024-setup
+ 2024-03-20T00:00:00+00:00
+ <h3 id="software--productivity">Software & Productivity</h3>
+
+<ol>
+ <li><a href="https://brew.sh/">brew</a> as package manager</li>
+ <li><a href="https://asdf-vm.com/">asdf</a> as language & tools management</li>
+ <li><a href="https://zed.dev/">Zed</a> as code editor</li>
+ <li><a href="https://bear.app/">Bear</a> as notes keeping</li>
+ <li><a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> as browser</li>
+ <li><a href="https://kagi.com/">Kagi</a> as search engine</li>
+ <li><a href="https://chat.openai.com/">ChatGPT</a> as AI assistant</li>
+ <li><a href="https://www.usebruno.com/">Bruno</a> as API client</li>
+ <li><a href="https://orbstack.dev/">OrbStack</a> as Docker desktop replacement, plus virtual machines</li>
+ <li>Apple ecosystem for mail, calendar, reminders, music, messaging, and more.</li>
+</ol>
+
+<h3 id="degoogling">Degoogling</h3>
+
+<p>As can be seen, I am using Kagi, which delivers excellent results without any garbage or ads, instead of Google. I also use email with my own domain through Apple’s iCloud email system, which allows for custom domains, as well as calendar and other features, all outside Google’s domain. Unfortunately, I have to maintain my Google account to use YouTube and occasionally for sites that do not allow creating accounts with a username and password, but only through Single Sign-On (SSO).</p>
+
+ Rodrigo Delduca
+ rodrigo@delduca.org
+ https://nullonerror.org/
+
+
+ NULL on error
+ NULL on error
+
+ https://nullonerror.org/feed.xml
+ 2025-04-17T02:14:57+00:00
+
+
+
+
+ Mastering Atomic Complex Operations in Redis: Unlocking High-Performance Data Handling With Lua
+
+ https://nullonerror.org/2024/03/06/mastering-atomic-complex-operations-in-redis-unlocking-high-performance-data-handling-with-lua
+ 2024-03-06T00:00:00+00:00
+ <p>I have been using Redis for its most basic operations such as caching with expiration and counters, as well as slightly more complex operations like <a href="https://redis.io/docs/data-types/sorted-sets/">zsets</a>, <a href="https://redis.io/commands/zrangebyscore/">zrangebyscore</a>, and <a href="https://redis.io/commands/scan/">scan</a>, or as a <a href="https://redis.io/commands/rpoplpush/">queue</a> for probably more than 6 years on a daily basis.</p>
+
+<p>I won’t discuss Postgres today, as we can achieve similar results. Today, I will introduce something different: the possibility of scripting Redis with <a href="https://en.wikipedia.org/wiki/Lua_%28programming_language%29">Lua</a>.</p>
+
+<p>Months ago, I found myself facing the following problem: I needed to retrieve a JSON string from Redis using a specific key, merge it with the local JSON, and then submit it back to Redis as a string.</p>
+
+<p>This would have been a trivial task if not for the following scenario: On the “other” side, there was a process that consumed the same JSON and then, in an atomic pipeline operation, deleted it.</p>
+
+<p>Given this context, I could not perform the JSON merge on the client side, as it would not be atomic. This is where my native language, Lua (incidentally, I have a dog with the same name, and another dog named Python, yes, I’m a nerd, how did you guess?), comes into play.</p>
+
+<p>With Lua, operations are atomic and accelerated with JIT, making it possible to do a myriad of things, one very classic example being a rate limiter by IP address.</p>
+
+<p>Let’s see how my solution turned out:</p>
+
+<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">script</span> <span class="o">=</span> <span class="s2">`
+ local key = KEYS[1]
+ local input = ARGV[1]
+ local existing = redis.call('GET', key)
+
+ if existing then
+ local existingJson = cjson.decode(existing)
+ local inputJson = cjson.decode(input)
+ for _, v in ipairs(inputJson) do
+ table.insert(existingJson, v)
+ end
+ input = cjson.encode(existingJson)
+ end
+
+ redis.call('SET', key, input)
+ return input
+ `</span><span class="p">;</span>
+
+<span class="kd">const</span> <span class="nx">key</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">...</span><span class="dl">"</span><span class="p">;</span>
+
+<span class="k">await</span> <span class="nx">redis</span><span class="p">.</span><span class="nb">eval</span><span class="p">(</span><span class="nx">script</span><span class="p">,</span> <span class="p">{</span>
+ <span class="na">keys</span><span class="p">:</span> <span class="p">[</span><span class="nx">key</span><span class="p">],</span>
+ <span class="na">arguments</span><span class="p">:</span> <span class="p">[</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">schema</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">data</span><span class="p">))],</span>
+<span class="p">});</span>
+</code></pre></div></div>
+
+<p>And on the other side, the consumer can retrieve the JSON and delete it, atomically within a pipeline:</p>
+
+<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">pipeline</span> <span class="o">=</span> <span class="nx">redis</span><span class="p">.</span><span class="nx">multi</span><span class="p">();</span>
+<span class="nx">pipeline</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">...</span><span class="dl">"</span><span class="p">);</span>
+<span class="nx">pipeline</span><span class="p">.</span><span class="nx">del</span><span class="p">(</span><span class="dl">"</span><span class="s2">...</span><span class="dl">"</span><span class="p">);</span>
+<span class="kd">const</span> <span class="p">[</span><span class="nx">jsonStr</span><span class="p">]</span> <span class="o">=</span> <span class="nx">pipeline</span><span class="p">.</span><span class="nx">exec</span><span class="p">();</span>
+</code></pre></div></div>
+
+<p>In this way, all operations are atomic.</p>
+
+<p><img src="https://nullonerror.org/public/2024-03-06-mastering-atomic-complex-operations-in-redis-unlocking-high-performance-data-handling-with-lua/elmo.jpg" alt="Elmo reacting to the atomicity of combining Lua with Redis" class="center" /></p>
+
+ Rodrigo Delduca
+ rodrigo@delduca.org
+ https://nullonerror.org/
+
+
+ NULL on error
+ NULL on error
+
+ https://nullonerror.org/feed.xml
+ 2025-04-17T02:14:57+00:00
+
+
+
+
+ 2023 was a productive year
+
+ https://nullonerror.org/2023/12/18/2023-was-a-productive-year
+ 2023-12-18T00:00:00+00:00
+ <p>The year started and I joined <a href="https://fueled.com/">Fueled</a>, an agency specializing in mobile apps and web systems, as a lead engineer.</p>
+
+<p>At Fueled, so far I’ve had the opportunity to work with three of their biggest clients to date. One of them is the world’s most famous lingerie brand, for which I developed a ‘social network’ that currently has about 5 million users. Then, I worked on a credit platform for elders in the United States that garnered 500,000 users at launch. Currently, I am working on a personal time management project for one of the top coaches in the field.</p>
+
+<p>In parallel, I wrote five articles for Fueled’s technical blog. They are:</p>
+
+<ul>
+ <li><a href="https://fueled.com/the-cache/posts/backend/working-with-compressed-binary-data-on-aws-iot-core/">Working With Compressed Binary Data on AWS Iot Core</a></li>
+ <li><a href="https://fueled.com/the-cache/posts/backend/combating-telegram-spam-with-a-serverless-bot/">Combating Telegram Spam With a Serverless Bot</a></li>
+ <li><a href="https://fueled.com/the-cache/posts/backend/clean-architecture-with-fastapi/">Clean Architecture with FastAPI</a></li>
+ <li><a href="https://fueled.com/the-cache/posts/backend/devops/cloud-workspace/">Secure Cloud Workspace with VSCode and Tailscale</a></li>
+ <li><a href="https://fueled.com/the-cache/posts/backend/golang/lint-all-the-things-with-golangci-lint/">Lint all the things with golangci-lint!</a></li>
+</ul>
+
+<p>I also wrote another five for my personal blog (this one). They are:</p>
+
+<ul>
+ <li><a href="https://nullonerror.org/2023/01/19/gotta-go-fast-with-docker-on-macos/">Gotta go fast with Docker on macOS</a></li>
+ <li><a href="https://nullonerror.org/2023/10/17/working-with-compressed-binary-data-on-aws-iot-core/">Working with compressed binary data on AWS IoT Core</a></li>
+ <li><a href="https://nullonerror.org/2023/11/01/what-i-ve-been-automating-with-github-actions-an-automated-life/">What I’ve been automating with GitHub Actions, an automated life</a></li>
+ <li><a href="https://nullonerror.org/2023/11/12/flipping-bits-whilst-updating-pixels/">Flipping Bits Whilst Updating Pixels</a></li>
+ <li><a href="https://nullonerror.org/2023/12/01/executing-untrusted-code-in-serverless-environments-a-telegram-bot-for-running-c-and-c++-code-on-cloud-run/">Executing Untrusted Code in Serverless Environments: A Telegram Bot for Running C and C++ Code on Cloud Run</a></li>
+</ul>
+
+<p>As if that wasn’t enough, I also developed and maintained personal projects, one of which, <a href="https://github.com/flippingpixels/carimbo">Carimbo</a>, was the one I devoted most of my time to. They are:</p>
+
+<ul>
+ <li><a href="https://github.com/flippingpixels/carimbo">Carimbo</a> - A 2D engine written in C++ using SDL and Lua as a scripting language.</li>
+ <li><a href="https://github.com/flippingpixels/play">Carimbo Play</a> - A web server to serve a combination of Carimbo releases and games.</li>
+ <li><a href="https://github.com/skhaz/grammateus">Grammateus</a> - An assistant that summarizes Twitch streams using OpenAI, written in Go.</li>
+ <li><a href="https://github.com/skhaz/neo-compiler-and-runner">Neo Compiler Bot</a> - A Telegram bot capable of compiling and executing unsafe code in the cloud.</li>
+ <li><a href="https://github.com/skhaz/freudian-slip">Freudian Slip Bot</a> - A Telegram bot designed to detect slip-ups between the lines.</li>
+</ul>
+
+<p>And many other small projects.</p>
+
+<p>As a result.</p>
+
+<p><img src="https://nullonerror.org/public/2023-12-18-2023-was-a-productive-year/github.png" alt="GitHub Stats" /></p>
+
+<p>Let’s see if 2024 will surpass it. Happy new year everyone!</p>
+
+ Rodrigo Delduca
+ rodrigo@delduca.org
+ https://nullonerror.org/
+
+
+ NULL on error
+ NULL on error
+
+ https://nullonerror.org/feed.xml
+ 2025-04-17T02:14:57+00:00
+
+
+
+
+ Executing Untrusted Code in Serverless Environments: A Telegram Bot for Running C and C++ Code on Cloud Run
+
+ https://nullonerror.org/2023/12/01/executing-untrusted-code-in-serverless-environments-a-telegram-bot-for-running-c-and-c++-code-on-cloud-run
+ 2023-12-01T00:00:00+00:00
+ <h2 id="intro">Intro</h2>
+
+<p>I enjoy experimenting and writing Telegram bots for programming groups I participate in. In two groups, people frequently ask about C or C++ code, seeking help, examples, and more. Instead of using online tools like <a href="https://godbolt.org/">Godbolt</a> (Compiler Explorer), they prefer sending their code directly in messages.</p>
+
+<p>I had previously created such a bot using a Flask webserver, which communicated with another container through <a href="https://www.jsonrpc.org/">JSON-RPC</a>. It worked well but occasionally had issues.</p>
+
+<p>With the rise of <a href="https://en.wikipedia.org/wiki/Large_language_model">LLM</a>, I switched to using OpenAI, but many users complained about the unconventional results, which was amusing.</p>
+
+<p>Recently, while working on a project named <a href="https://github.com/flippingpixels/carimbo">Carimbo</a>, I started exploring <a href="https://webassembly.org/">WebAssembly</a>. I realized it could be ideal for running untrusted code. Initially, I considered using <code class="language-plaintext highlighter-rouge">isolated-vm</code> with WebAssembly, but I was quite satisfied with <a href="https://wasmtime.dev/">Wasmtime</a>. It offered options to limit CPU time and RAM usage, among other features.</p>
+
+<h2 id="cgroups">Cgroups</h2>
+
+<p>Any experienced developer would likely suggest using <a href="https://en.wikipedia.org/wiki/Cgroups">cgroups</a> and <a href="https://en.wikipedia.org/wiki/Linux_namespaces">namespaces</a>, which are indeed superior options. However, I prefer not to incur the costs of VMs or keep a machine running 24/7 at my home. This is primarily because <a href="https://cloud.google.com/run">Cloud Run</a>, based on <a href="https://www.docker.com/">Docker</a>, already utilizes cgroups, and to my knowledge, nested cgroups aren’t possible.</p>
+
+<p>Cloud Run offers me several advantages. Without delving into too much detail, it’s a serverless platform built on top of <a href="https://kubernetes.io/">Kubernetes</a>, employing <a href="https://gvisor.dev/">gVisor</a> for an added security layer. You don’t need to handle Kubernetes directly, but the option for fine-tuning is available, which I will discuss in this article.</p>
+
+<h2 id="the-bot">The Bot</h2>
+
+<p>Unlike in my previous work <a href="https://nullonerror.org/2021/01/08/hosting-telegram-bots-on-google-cloud-run/">Hosting Telegram bots on Cloud Run for free</a>, this time I will not use <a href="https://flask.palletsprojects.com">Flask</a>, but instead, I will directly employ <a href="https://www.starlette.io/">Starlette</a>. Starlette is an asynchronous framework for Python. One of the main reasons for this migration is to utilize <a href="https://docs.python.org/3/library/asyncio.html">asyncio</a>, which will enable handling more requests. Additionally, the python-telegram-robot library has shifted to this asynchronous model, aligning with this change.</p>
+
+<p>Let’s start with the Dockerfile.</p>
+
+<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="w"> </span><span class="s">python:3.12-slim-bookworm</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">base</span>
+
+<span class="k">ENV</span><span class="s"> PIP_DISABLE_PIP_VERSION_CHECK 1</span>
+<span class="k">ENV</span><span class="s"> PYTHONUNBUFFERED 1</span>
+<span class="k">ENV</span><span class="s"> PYTHONDONTWRITEBYTECODE 1</span>
+<span class="k">ENV</span><span class="s"> EMSDK=/emsdk</span>
+<span class="k">ENV</span><span class="s"> PATH=/emsdk:/emsdk/upstream/emscripten:/opt/venv/bin:$PATH</span>
+
+<span class="k">FROM</span><span class="w"> </span><span class="s">base</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">builder</span>
+<span class="k">RUN </span>python <span class="nt">-m</span> venv /opt/venv
+<span class="k">COPY</span><span class="s"> requirements.txt .</span>
+<span class="k">RUN </span>pip <span class="nb">install</span> <span class="nt">--no-cache-dir</span> <span class="nt">--requirement</span> requirements.txt
+
+<span class="k">FROM</span><span class="s"> base</span>
+<span class="k">WORKDIR</span><span class="s"> /opt/app</span>
+
+<span class="c"># Let's steal this entire directory from the official Emscripten image.</span>
+<span class="k">COPY</span><span class="s"> --from=emscripten/emsdk:3.1.49 /emsdk /emsdk</span>
+<span class="k">COPY</span><span class="s"> --from=builder /opt/venv /opt/venv</span>
+<span class="k">COPY</span><span class="s"> . .</span>
+
+<span class="k">RUN </span>useradd <span class="nt">-r</span> user
+<span class="k">USER</span><span class="s"> user</span>
+
+<span class="c"># Instead of Gunicorn, we will use Uvicorn, which is an ASGI web server implementation for Python.</span>
+<span class="k">CMD</span><span class="s"> exec uvicorn main:app --host 0.0.0.0 --port $PORT --workers 8 --timeout-keep-alive 600 --timeout-graceful-shutdown 600</span>
+</code></pre></div></div>
+
+<p>The main differences are that we steal an entire directory from the Emscripten Docker image, which saves us from having to build in the image, which is excellent. We also use Uvicorn, an ASGI web server that allows direct use of asyncio.</p>
+
+<p>Now let’s see how it goes with handling the incoming requests.</p>
+
+<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">equals</span><span class="p">(</span><span class="n">left</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="bp">None</span><span class="p">,</span> <span class="n">right</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="bp">None</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
+ <span class="s">"""
+ Compare two strings using a consistent amount of time to avoid timing attacks.
+ """</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">left</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">right</span><span class="p">:</span>
+ <span class="k">return</span> <span class="bp">False</span>
+
+ <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">left</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">right</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">False</span>
+
+ <span class="k">for</span> <span class="n">c1</span><span class="p">,</span> <span class="n">c2</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">left</span><span class="p">,</span> <span class="n">right</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">c1</span> <span class="o">!=</span> <span class="n">c2</span><span class="p">:</span>
+ <span class="k">return</span> <span class="bp">False</span>
+
+ <span class="k">return</span> <span class="bp">True</span>
+
+
+<span class="k">async</span> <span class="k">def</span> <span class="nf">webhook</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">Request</span><span class="p">):</span>
+ <span class="s">"""
+ Entry point for requests coming from Telegram.
+ """</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">equals</span><span class="p">(</span>
+ <span class="n">request</span><span class="p">.</span><span class="n">headers</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"X-Telegram-Bot-Api-Secret-Token"</span><span class="p">),</span>
+ <span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">[</span><span class="s">"SECRET"</span><span class="p">],</span>
+ <span class="p">):</span>
+ <span class="c1"># This section prevents false calls, only this application and Telegram know the secret.
+</span> <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">401</span><span class="p">)</span>
+
+ <span class="n">payload</span> <span class="o">=</span> <span class="k">await</span> <span class="n">request</span><span class="p">.</span><span class="n">json</span><span class="p">()</span>
+
+ <span class="c1"># Where the bot becomes operational, the JSON is passed to the application, which in turn processes the request.
+</span> <span class="k">async</span> <span class="k">with</span> <span class="n">application</span><span class="p">:</span>
+ <span class="k">await</span> <span class="n">application</span><span class="p">.</span><span class="n">process_update</span><span class="p">(</span><span class="n">Update</span><span class="p">.</span><span class="n">de_json</span><span class="p">(</span><span class="n">payload</span><span class="p">,</span> <span class="n">application</span><span class="p">.</span><span class="n">bot</span><span class="p">))</span>
+
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">200</span><span class="p">)</span>
+
+
+<span class="n">app</span> <span class="o">=</span> <span class="n">Starlette</span><span class="p">(</span>
+ <span class="n">routes</span><span class="o">=</span><span class="p">[</span>
+ <span class="n">Route</span><span class="p">(</span><span class="s">"/"</span><span class="p">,</span> <span class="n">webhook</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">"POST"</span><span class="p">]),</span>
+ <span class="p">],</span>
+<span class="p">)</span>
+</code></pre></div></div>
+
+<p>Finally, we have the handler for messages that start with <code class="language-plaintext highlighter-rouge">/run</code>.</p>
+
+<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="k">def</span> <span class="nf">on_run</span><span class="p">(</span><span class="n">update</span><span class="p">:</span> <span class="n">Update</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="n">ContextTypes</span><span class="p">.</span><span class="n">DEFAULT_TYPE</span><span class="p">)</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">update</span><span class="p">.</span><span class="n">message</span><span class="p">.</span><span class="n">reply_to_message</span> <span class="ow">or</span> <span class="n">update</span><span class="p">.</span><span class="n">message</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">message</span><span class="p">:</span>
+ <span class="k">return</span>
+
+ <span class="n">text</span> <span class="o">=</span> <span class="n">message</span><span class="p">.</span><span class="n">text</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">text</span><span class="p">:</span>
+ <span class="k">return</span>
+
+ <span class="n">text</span> <span class="o">=</span> <span class="n">text</span><span class="p">.</span><span class="n">lstrip</span><span class="p">(</span><span class="s">"/run"</span><span class="p">)</span>
+
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">text</span><span class="p">:</span>
+ <span class="k">await</span> <span class="n">message</span><span class="p">.</span><span class="n">reply_text</span><span class="p">(</span><span class="s">"Luke, I need the code for the Death Star's system."</span><span class="p">)</span>
+ <span class="k">return</span>
+
+ <span class="k">try</span><span class="p">:</span>
+ <span class="c1"># All the code is asynchronous, while the 'run' function is not. Therefore, we execute it in a thread.
+</span> <span class="n">coro</span> <span class="o">=</span> <span class="n">asyncio</span><span class="p">.</span><span class="n">to_thread</span><span class="p">(</span><span class="n">run</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span>
+
+ <span class="c1"># We execute the thread as a coroutine and limit its execution to 30 seconds.
+</span> <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">asyncio</span><span class="p">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">coro</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span>
+
+ <span class="c1"># Below, we prevent flooding in groups by placing very long messages into a bucket and returning the public URL.
+</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">></span> <span class="mi">64</span><span class="p">:</span>
+ <span class="n">blob</span> <span class="o">=</span> <span class="n">bucket</span><span class="p">.</span><span class="n">blob</span><span class="p">(</span><span class="n">hashlib</span><span class="p">.</span><span class="n">sha256</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">text</span><span class="p">).</span><span class="n">encode</span><span class="p">()).</span><span class="n">hexdigest</span><span class="p">())</span>
+ <span class="n">blob</span><span class="p">.</span><span class="n">upload_from_string</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
+ <span class="n">blob</span><span class="p">.</span><span class="n">make_public</span><span class="p">()</span>
+
+ <span class="n">result</span> <span class="o">=</span> <span class="n">blob</span><span class="p">.</span><span class="n">public_url</span>
+
+ <span class="c1"># Respond to the message with the result, which can be either an error or a success.
+</span> <span class="k">await</span> <span class="n">message</span><span class="p">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">asyncio</span><span class="p">.</span><span class="nb">TimeoutError</span><span class="p">:</span>
+ <span class="c1"># If the code exceeds the time limit or takes too long to compile, we return some emojis.
+</span> <span class="k">await</span> <span class="n">message</span><span class="p">.</span><span class="n">reply_text</span><span class="p">(</span><span class="s">"⏰😮💨"</span><span class="p">)</span>
+</code></pre></div></div>
+
+<h2 id="running-untrusted-code">Running Untrusted Code</h2>
+
+<p>Each request to execute code is compiled using em++, an ‘alias’ for clang++, targeting WebAssembly, and then executed with the <a href="https://wasi.dev/">WASI</a> runtime. Each execution runs separately and in a thread-safe manner in its own directory. While I could limit CPU usage (fuel) and memory usage, as indicated by the commented lines, in my case I opted for a container with 4GB of RAM and 4 vCPUs, which is more than sufficient given that I configured Run to accept only 8 connections per instance.</p>
+
+<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="n">source</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
+ <span class="k">with</span> <span class="n">TemporaryDirectory</span><span class="p">()</span> <span class="k">as</span> <span class="n">path</span><span class="p">:</span>
+ <span class="n">os</span><span class="p">.</span><span class="n">chdir</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
+
+ <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"main.cpp"</span><span class="p">,</span> <span class="s">"w+t"</span><span class="p">)</span> <span class="k">as</span> <span class="n">main</span><span class="p">:</span>
+ <span class="n">main</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">source</span><span class="p">)</span>
+ <span class="n">main</span><span class="p">.</span><span class="n">flush</span><span class="p">()</span>
+
+ <span class="k">try</span><span class="p">:</span>
+ <span class="c1"># Compile it.
+</span> <span class="n">result</span> <span class="o">=</span> <span class="n">subprocess</span><span class="p">.</span><span class="n">run</span><span class="p">(</span>
+ <span class="p">[</span>
+ <span class="s">"em++"</span><span class="p">,</span>
+ <span class="s">"-s"</span><span class="p">,</span>
+ <span class="s">"ENVIRONMENT=node"</span><span class="p">,</span>
+ <span class="s">"-s"</span><span class="p">,</span>
+ <span class="s">"WASM=1"</span><span class="p">,</span>
+ <span class="s">"-s"</span><span class="p">,</span>
+ <span class="s">"PURE_WASI=1"</span><span class="p">,</span>
+ <span class="s">"main.cpp"</span><span class="p">,</span>
+ <span class="p">],</span>
+ <span class="n">capture_output</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
+ <span class="n">text</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
+ <span class="n">check</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
+ <span class="p">)</span>
+
+ <span class="k">if</span> <span class="n">result</span><span class="p">.</span><span class="n">returncode</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">result</span><span class="p">.</span><span class="n">stderr</span>
+
+ <span class="c1"># Run it.
+</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"a.out.wasm"</span><span class="p">,</span> <span class="s">"rb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">binary</span><span class="p">:</span>
+ <span class="n">wasi</span> <span class="o">=</span> <span class="n">WasiConfig</span><span class="p">()</span>
+ <span class="c1"># Store the output in a file.
+</span> <span class="n">wasi</span><span class="p">.</span><span class="n">stdout_file</span> <span class="o">=</span> <span class="s">"a.out.stdout"</span>
+ <span class="c1"># Store the errors in a file.
+</span> <span class="n">wasi</span><span class="p">.</span><span class="n">stderr_file</span> <span class="o">=</span> <span class="s">"a.out.stderr"</span>
+
+ <span class="n">config</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
+ <span class="c1"># config.consume_fuel = True
+</span> <span class="n">engine</span> <span class="o">=</span> <span class="n">Engine</span><span class="p">(</span><span class="n">config</span><span class="p">)</span>
+ <span class="n">store</span> <span class="o">=</span> <span class="n">Store</span><span class="p">(</span><span class="n">engine</span><span class="p">)</span>
+ <span class="n">store</span><span class="p">.</span><span class="n">set_wasi</span><span class="p">(</span><span class="n">wasi</span><span class="p">)</span>
+ <span class="c1"># Limits the RAM.
+</span> <span class="c1"># store.set_limits(16 * 1024 * 1024)
+</span> <span class="c1"># Limits the CPU.
+</span> <span class="c1"># store.set_fuel(10_000_000_000)
+</span>
+ <span class="n">linker</span> <span class="o">=</span> <span class="n">Linker</span><span class="p">(</span><span class="n">engine</span><span class="p">)</span>
+ <span class="n">linker</span><span class="p">.</span><span class="n">define_wasi</span><span class="p">()</span>
+ <span class="n">module</span> <span class="o">=</span> <span class="n">Module</span><span class="p">(</span><span class="n">store</span><span class="p">.</span><span class="n">engine</span><span class="p">,</span> <span class="n">binary</span><span class="p">.</span><span class="n">read</span><span class="p">())</span>
+ <span class="n">instance</span> <span class="o">=</span> <span class="n">linker</span><span class="p">.</span><span class="n">instantiate</span><span class="p">(</span><span class="n">store</span><span class="p">,</span> <span class="n">module</span><span class="p">)</span>
+
+ <span class="c1"># `_start` is the binary entrypoint, also known as main.
+</span> <span class="n">start</span> <span class="o">=</span> <span class="n">instance</span><span class="p">.</span><span class="n">exports</span><span class="p">(</span><span class="n">store</span><span class="p">)[</span><span class="s">"_start"</span><span class="p">]</span>
+ <span class="k">assert</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">Func</span><span class="p">)</span>
+
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">start</span><span class="p">(</span><span class="n">store</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">ExitTrap</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
+ <span class="c1"># If exit code is not 0, we return the errors.
+</span> <span class="k">if</span> <span class="n">e</span><span class="p">.</span><span class="n">code</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
+ <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"a.out.stderr"</span><span class="p">,</span> <span class="s">"rt"</span><span class="p">)</span> <span class="k">as</span> <span class="n">stderr</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">stderr</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
+
+ <span class="c1"># If no errors, we return the output.
+</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"a.out.stdout"</span><span class="p">,</span> <span class="s">"rt"</span><span class="p">)</span> <span class="k">as</span> <span class="n">stdout</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">stdout</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
+ <span class="k">except</span> <span class="n">subprocess</span><span class="p">.</span><span class="n">CalledProcessError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">e</span><span class="p">.</span><span class="n">stderr</span>
+ <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span> <span class="c1"># noqa
+</span> <span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
+</code></pre></div></div>
+
+<h2 id="deploy">Deploy</h2>
+
+<p>In the past, I always used Google’s tools for deployment, but this time I tried building the Docker image in GitHub Action, which gave me two huge advantages.</p>
+
+<ol>
+ <li>Cache: I don’t know why, but I never got the cache to work in Cloud Build. With GitHub, it’s just a matter of using a flag.</li>
+ <li>Modern Docker syntax usage: In Cloud Build, it’s not possible to use <a href="https://www.docker.com/blog/introduction-to-heredocs-in-dockerfiles/">heredoc</a>, for example.</li>
+ <li>Speed: I know it’s possible to upgrade the Cloud Build machine, but that costs money, and on GitHub, I have a quite generous free quota.</li>
+</ol>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Deploy on Google Cloud Platform</span>
+
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">push</span><span class="pi">:</span>
+ <span class="na">branches</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">main</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">deploy</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Authenticate to Google Cloud</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">google-github-actions/auth@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">credentials_json</span><span class="pi">:</span> <span class="s">$</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Google Cloud SDK</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">google-github-actions/setup-gcloud@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">project_id</span><span class="pi">:</span> <span class="s">$</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Docker Buildx</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">docker/setup-buildx-action@v3</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Authenticate Docker</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">gcloud auth configure-docker --quiet $-docker.pkg.dev</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build And Push Telegram Service</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">docker/build-push-action@v5</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">context</span><span class="pi">:</span> <span class="s">.</span>
+ <span class="na">push</span><span class="pi">:</span> <span class="no">true</span>
+ <span class="na">tags</span><span class="pi">:</span> <span class="s">$/$:$</span>
+ <span class="na">cache-from</span><span class="pi">:</span> <span class="s">type=gha</span>
+ <span class="na">cache-to</span><span class="pi">:</span> <span class="s">type=gha,mode=max</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy Telegram Service to Cloud Run</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">TELEGRAM_SERVICE_NAME</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">REGION</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">REGISTRY</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">GITHUB_SHA</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">TELEGRAM_TOKEN</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">SECRET</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">BUCKET</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">cat <<EOF | envsubst > service.yaml</span>
+ <span class="s">apiVersion: serving.knative.dev/v1</span>
+ <span class="s">kind: Service</span>
+ <span class="s">metadata:</span>
+ <span class="s">name: "$TELEGRAM_SERVICE_NAME"</span>
+ <span class="s">labels:</span>
+ <span class="s">cloud.googleapis.com/location: "$REGION"</span>
+ <span class="s">spec:</span>
+ <span class="s">template:</span>
+ <span class="s">metadata:</span>
+ <span class="s">annotations:</span>
+ <span class="s">run.googleapis.com/execution-environment: "gen2"</span>
+ <span class="s">run.googleapis.com/startup-cpu-boost: "true"</span>
+ <span class="s">run.googleapis.com/cpu-throttling: "true"</span>
+ <span class="s">autoscaling.knative.dev/maxScale: "16"</span>
+ <span class="s">spec:</span>
+ <span class="s">containerConcurrency: "1"</span>
+ <span class="s">timeoutSeconds: "60"</span>
+ <span class="s">containers:</span>
+ <span class="s">- image: "$REGISTRY/$TELEGRAM_SERVICE_NAME:$GITHUB_SHA"</span>
+ <span class="s">name: "$TELEGRAM_SERVICE_NAME"</span>
+ <span class="s">resources:</span>
+ <span class="s">limits:</span>
+ <span class="s">cpu: "4000m"</span>
+ <span class="s">memory: "4Gi"</span>
+ <span class="s">env:</span>
+ <span class="s">- name: TELEGRAM_TOKEN</span>
+ <span class="s">value: "$TELEGRAM_TOKEN"</span>
+ <span class="s">- name: SECRET</span>
+ <span class="s">value: "$SECRET"</span>
+ <span class="s">- name: BUCKET</span>
+ <span class="s">value: "$BUCKET"</span>
+ <span class="s">EOF</span>
+
+ <span class="s">gcloud run services replace service.yaml</span>
+ <span class="s">rm -f service.yaml</span>
+</code></pre></div></div>
+
+<h2 id="conclusion">Conclusion</h2>
+
+<p>Try here: <a href="https://t.me/neo_compiler_bot">https://t.me/neo_compiler_bot</a> or <code class="language-plaintext highlighter-rouge">@neo_compiler_bot</code> on Telegram.</p>
+
+<p>Source code: <a href="https://github.com/skhaz/neo-compiler-and-runner">https://github.com/skhaz/neo-compiler-and-runner</a>.</p>
+
+<p><img src="https://nullonerror.org/public/2023-12-01-executing-untrusted-code-in-serverless-environments-a-telegram-bot-for-running-c-and-c++-code-on-cloud-run/telegram.png" alt="Telegram" class="center" /></p>
+
+ Rodrigo Delduca
+ rodrigo@delduca.org
+ https://nullonerror.org/
+
+
+ NULL on error
+ NULL on error
+
+ https://nullonerror.org/feed.xml
+ 2025-04-17T02:14:57+00:00
+
+
+
+
+ Flipping Bits Whilst Updating Pixels
+
+ https://nullonerror.org/2023/11/12/flipping-bits-whilst-updating-pixels
+ 2023-11-12T00:00:00+00:00
+ <blockquote>
+ <p>I have less than 16 milliseconds to do more than thousands of operations.</p>
+</blockquote>
+
+<p>Roughly 15 years ago, during an extended summer holiday, in a shared room of a student residence, I found myself endeavoring to port <a href="https://github.com/skhaz/wintermoon">my 2D game</a> engine built on top of <a href="https://en.wikipedia.org/wiki/Simple_DirectMedia_Layer">SDL</a> to the <a href="https://en.wikipedia.org/wiki/Google_Native_Client">Google Native Client (NaCl)</a>. NaCl served as a sandboxing mechanism for Chrome, enabling the execution of native code within the browser, specifically within the Chrome browser. It’s safe to assert that NaCl can be considered the progenitor of <a href="https://webassembly.org">WebAssembly</a>.</p>
+
+<p>A considerable amount of time has elapsed, and many changes have transpired. I transitioned from game development to web development, yet low-level programming has always coursed through my veins. Consequently, I resolved to revive the dream of crafting my own game engine running on the web. Today, with the advent of WebAssembly, achieving this goal is significantly more feasible and portable.</p>
+
+<p>Therefore, I created Carimbo 🇧🇷, meaning “stamp” in English. The name encapsulates the notion that 2D engines are continually stamping sprites onto the screen.</p>
+
+
+ <img src="https://nullonerror.org/public/2023-11-12-flipping-bits-whilst-updating-pixels/carimbo.webp" alt="Carimbo" class="center" />
+ Artwork by <a href="https://www.fiverr.com/yuugenpixie">@yuugenpixie</a>
+
+
+<p>This engine shares the foundational principles of <em>Wintermoon</em>; it is built upon the SDL library, employs Lua for scripting, and consolidates all assets within a compressed LZMA file, which is <em>mounted</em> when the engine initializes.</p>
+
+<p>In essence, it operates as follows: video, audio, joystick, network, and file system components are initialized. Then, the <code class="language-plaintext highlighter-rouge">bundle.zip</code> file is mounted. When I say ‘mounted,’ I employ <a href="https://icculus.org/physfs/">a library</a> that makes read and write operations within the compressed file entirely transparent, eliminating the need for decompression, which is excellent. Subsequently, the ‘main.lua’ file is executed. This file should utilize the factory to construct the engine, which is the cornerstone. Following this, the script must spawn entities and other objects to be used within the game. Finally, with the game defined, the script should invoke the ‘run’ method of the engine, which will block and initiate the event loop.</p>
+
+<p>However, this time around, the tooling for C++ has significantly improved compared to that era. Numerous package managers now exist, and compilers, along with the standard library, have matured considerably. Notably, there’s no longer a necessity to employ Boost for advanced features; many functionalities, including smart pointers and others formerly associated with Boost, are now integral parts of the standard library.</p>
+
+<p>Speaking of package managers, in Carimbo, I opted for <a href="https://conan.io/">Conan</a>, which, in my opinion, is an excellent package manager.</p>
+
+<p>It was during this exploration that I discovered Conan’s support for various toolings, including <a href="https://github.com/emscripten-core/emsdk">emsdk</a>—the Software Development Kit (SDK) and compiler for the Emscripten project. Emscripten is an LLVM/Clang-based compiler designed to translate C and C++ source code into WebAssembly.</p>
+
+<p>With the <code class="language-plaintext highlighter-rouge">emsdk</code>, I could finally fulfill my long-held aspiration.</p>
+
+<p><img src="https://nullonerror.org/public/2023-11-12-flipping-bits-whilst-updating-pixels/blank.jpeg" alt="" class="center" /></p>
+
+<p>Yes, a black screen and an overwhelming sense of victory. I had successfully ported my code to run in the browser. Now, all that remained was to figure out how to load the assets (at that time). However, the event loop was running just a tad below 60 frames per second due to a bug in the counter.</p>
+
+<p>And you might be wondering, ‘How do you debug this?’ Firstly, for various reasons that can burden the final binary, a lot is stripped away. Therefore, we need to recompile everything and all dependencies with sanitizers and debugging symbols. Secondly, WebAssembly should be linked with <strong>-sASSERTIONS=2</strong>, <strong>-sRUNTIME_DEBUG</strong>, and <strong>–profiling</strong>. This way, it’s possible to see the stack trace in the browser console as if by magic. Additionally, <a href="https://developer.chrome.com/blog/wasm-debugging-2020/">Chrome has a debugger</a> that allows you to insert breakpoints within your source code and inspect step by step.</p>
+
+<p>By the way, a binary and all its dependencies compiled with all sanitizers and debugging symbols can easily surpass 300 megabytes! So, I recommend compiling with -Os or -O1.</p>
+
+<p><a href="https://github.com/carimbolabs/carimbo">The source code for the Carimbo</a></p>
+
+<p>Check out the engine running below; this is just a concept without audio and joystick support.</p>
+
+ Rodrigo Delduca
+ rodrigo@delduca.org
+ https://nullonerror.org/
+
+
+ NULL on error
+ NULL on error
+
+ https://nullonerror.org/feed.xml
+ 2025-04-17T02:14:57+00:00
+
+
+
+
+ What I’ve been automating with GitHub Actions, an automated life
+
+ https://nullonerror.org/2023/11/01/what-i-ve-been-automating-with-github-actions-an-automated-life
+ 2023-11-01T00:00:00+00:00
+ <h1 id="automate-all-the-things">Automate all the things!</h1>
+
+<p>Programmers, like no other beings on the planet, are completely obsessed with automating things, from the simplest to the most complex, and I’m no different.</p>
+
+<p>I have automated several things using GitHub Actions, and today I will show some of the things I’ve done.</p>
+
+<h2 id="readmemd">README.md</h2>
+
+<p>In my GitHub README, I periodically fetch the RSS feed from my blog (the one you are currently reading) and populate it with the latest articles, like this:</p>
+
+<p>Ah, you can use any source of RSS, like your YouTube channel!</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Latest blog post workflow</span>
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">schedule</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0</span><span class="nv"> </span><span class="s">*/6</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*"</span>
+ <span class="na">workflow_dispatch</span><span class="pi">:</span> <span class="c1"># Run workflow manually</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">update-readme-with-blog</span><span class="pi">:</span>
+ <span class="na">name</span><span class="pi">:</span> <span class="s">Update this repo's README with latest blog posts</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v2</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Pull NULL on Error posts</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">gautamkrishnar/blog-post-workflow@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">comment_tag_name</span><span class="pi">:</span> <span class="s">BLOG</span>
+ <span class="na">commit_message</span><span class="pi">:</span> <span class="s">Update with the latest blog posts</span>
+ <span class="na">committer_username</span><span class="pi">:</span> <span class="s">Rodrigo Delduca</span>
+ <span class="na">committer_email</span><span class="pi">:</span> <span class="s">46259+skhaz@users.noreply.github.com</span>
+ <span class="na">max_post_count</span><span class="pi">:</span> <span class="m">6</span>
+ <span class="na">feed_list</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://nullonerror.org/feed"</span>
+</code></pre></div></div>
+
+<h2 id="resume">Resume</h2>
+
+<p>My resume is public. I have an action in the repository that compiles and uploads it to a Google Cloud bucket and sets the object as public, like this:</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">on</span><span class="pi">:</span> <span class="s">push</span>
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">build</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">container</span><span class="pi">:</span> <span class="s">texlive/texlive</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">xelatex resume.tex</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Authenticate to Google Cloud</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">google-github-actions/auth@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">credentials_json</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Upload to Google Cloud Storage</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">google-github-actions/upload-cloud-storage@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">path</span><span class="pi">:</span> <span class="s">resume.pdf</span>
+ <span class="na">destination</span><span class="pi">:</span> <span class="s">gcs.skhaz.dev</span>
+ <span class="na">predefinedAcl</span><span class="pi">:</span> <span class="s">publicRead</span>
+</code></pre></div></div>
+
+<p>You can check it out at <a href="https://gcs.skhaz.dev/resume.pdf">https://gcs.skhaz.dev/resume.pdf</a>.</p>
+
+<h2 id="github-stars">GitHub Stars</h2>
+
+<p>I believe everyone enjoys starring repositories, but GitHub’s interface doesn’t help much when it comes to finding or organizing them.</p>
+
+<p>To do this, I use <a href="https://github.com/maguowei/starred">starred</a>, which generates a README file categorizing by language. You can check it out at the following address: <a href="https://github.com/skhaz/stars">https://github.com/skhaz/stars</a></p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Update Stars</span>
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">workflow_dispatch</span><span class="pi">:</span>
+ <span class="na">schedule</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s">0 0 * * *</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">stars</span><span class="pi">:</span>
+ <span class="na">name</span><span class="pi">:</span> <span class="s">Update stars</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v3</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Python</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-python@v3</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">python-version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3.10"</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install dependencies</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">python -m pip install --upgrade pip</span>
+ <span class="s">pip install starred</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Get repository name</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">echo "REPOSITORY_NAME=${GITHUB_REPOSITORY#*/}" >> $GITHUB_ENV</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Update repository category by language</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">GITHUB_TOKEN</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">REPOSITORY</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">USERNAME</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">starred --username ${USERNAME} --repository ${REPOSITORY} --sort --token ${GITHUB_TOKEN} --message 'awesome-stars category by language update by github actions cron, created by starred'</span>
+</code></pre></div></div>
+
+<h2 id="healthchecks">Healthchecks</h2>
+
+<p>I imagine that, just like me, you also have several websites. In my case, all of them are simple and do not generate any income. However, I want to make sure that everything is in order, so I use https://healthchecks.io in conjunction with GitHub Actions. Healthchecks.io operates passively; it does not make requests to your site. On the contrary, you must make a request to it, and then it marks it as healthy. If there are no pings for a certain amount of time (configurable), it will notify you through various means that the site or application is not functioning as it should. Think of it as a Kubernetes probe.</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Health Check</span>
+
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">workflow_dispatch</span><span class="pi">:</span>
+ <span class="na">schedule</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*"</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">health</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Check health</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">STATUSCODE=$(curl -s -o /dev/null --write-out "%{http_code}" "${SITE_URL}")</span>
+
+ <span class="s">if test $STATUSCODE -ne 200; then</span>
+ <span class="s">exit 1</span>
+ <span class="s">fi</span>
+
+ <span class="s">curl -fsS -m 10 --retry 5 -o /dev/null "https://hc-ping.com/${HEALTH_UUID}"</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">HEALTH_UUID</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">SITE_URL</span><span class="pi">:</span> <span class="s">$</span>
+</code></pre></div></div>
+
+<h2 id="salary">Salary</h2>
+
+<p>This one is a bit more complex, as it goes beyond just an action. I have a repository called ‘salary’, where there’s an action that runs every hour. What this action does is essentially run a Go code, and the result updates the README file. This way, I can simply access the URL and get an estimate of how much I’ll receive.</p>
+
+<p>In the program, I have two goroutines running in parallel. In one of them, I query the number of hours on Toggl, multiply it by my rate, and return it through a channel. The other does the same, but it uses an API to convert dollars to Brazilian reais, and in the end, the values are summed up.</p>
+
+<p><code class="language-plaintext highlighter-rouge">main.go</code></p>
+
+<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>
+
+<span class="k">import</span> <span class="p">(</span>
+ <span class="s">"bytes"</span>
+ <span class="s">"encoding/json"</span>
+ <span class="s">"fmt"</span>
+ <span class="s">"io"</span>
+ <span class="s">"log"</span>
+ <span class="s">"math"</span>
+ <span class="s">"net/http"</span>
+ <span class="s">"os"</span>
+ <span class="s">"strconv"</span>
+ <span class="s">"sync"</span>
+ <span class="s">"time"</span>
+<span class="p">)</span>
+
+<span class="k">type</span> <span class="n">DateRange</span> <span class="k">struct</span> <span class="p">{</span>
+ <span class="n">StartDate</span> <span class="kt">string</span> <span class="s">`json:"start_date"`</span>
+ <span class="n">EndDate</span> <span class="kt">string</span> <span class="s">`json:"end_date"`</span>
+<span class="p">}</span>
+
+<span class="k">type</span> <span class="n">Summary</span> <span class="k">struct</span> <span class="p">{</span>
+ <span class="n">TrackedSecond</span> <span class="kt">int</span> <span class="s">`json:"tracked_seconds"`</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="n">toggl</span><span class="p">(</span><span class="n">result</span> <span class="k">chan</span><span class="o"><-</span> <span class="kt">int</span><span class="p">,</span> <span class="n">wg</span> <span class="o">*</span><span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">defer</span> <span class="n">wg</span><span class="o">.</span><span class="n">Done</span><span class="p">()</span>
+
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">now</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">Now</span><span class="p">()</span>
+ <span class="n">firstDay</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">Date</span><span class="p">(</span><span class="n">now</span><span class="o">.</span><span class="n">Year</span><span class="p">(),</span> <span class="n">now</span><span class="o">.</span><span class="n">Month</span><span class="p">(),</span> <span class="m">1</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">time</span><span class="o">.</span><span class="n">UTC</span><span class="p">)</span>
+ <span class="n">lastDay</span> <span class="o">=</span> <span class="n">firstDay</span><span class="o">.</span><span class="n">AddDate</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="o">-</span><span class="m">1</span><span class="p">)</span>
+
+ <span class="n">url</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"https://api.track.toggl.com/reports/api/v3/workspace/%s/projects/summary"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"TOGGL_WORKSPACE_ID"</span><span class="p">))</span>
+ <span class="n">dataRange</span> <span class="o">=</span> <span class="n">DateRange</span><span class="p">{</span>
+ <span class="n">StartDate</span><span class="o">:</span> <span class="n">firstDay</span><span class="o">.</span><span class="n">Format</span><span class="p">(</span><span class="s">"2006-01-02"</span><span class="p">),</span>
+ <span class="n">EndDate</span><span class="o">:</span> <span class="n">lastDay</span><span class="o">.</span><span class="n">Format</span><span class="p">(</span><span class="s">"2006-01-02"</span><span class="p">),</span>
+ <span class="p">}</span>
+ <span class="p">)</span>
+
+ <span class="n">payload</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">json</span><span class="o">.</span><span class="n">Marshal</span><span class="p">(</span><span class="n">dataRange</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">req</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">http</span><span class="o">.</span><span class="n">NewRequest</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">MethodPost</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">bytes</span><span class="o">.</span><span class="n">NewBuffer</span><span class="p">(</span><span class="n">payload</span><span class="p">))</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">req</span><span class="o">.</span><span class="n">Header</span><span class="o">.</span><span class="n">Set</span><span class="p">(</span><span class="s">"Content-Type"</span><span class="p">,</span> <span class="s">"application/json"</span><span class="p">)</span>
+ <span class="n">req</span><span class="o">.</span><span class="n">SetBasicAuth</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"TOGGL_EMAIL"</span><span class="p">),</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"TOGGL_PASSWORD"</span><span class="p">))</span>
+
+ <span class="n">client</span> <span class="o">:=</span> <span class="o">&</span><span class="n">http</span><span class="o">.</span><span class="n">Client</span><span class="p">{}</span>
+ <span class="n">resp</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">client</span><span class="o">.</span><span class="n">Do</span><span class="p">(</span><span class="n">req</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">resp</span><span class="o">.</span><span class="n">Body</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="n">body</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">io</span><span class="o">.</span><span class="n">ReadAll</span><span class="p">(</span><span class="n">resp</span><span class="o">.</span><span class="n">Body</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="k">var</span> <span class="n">summaries</span> <span class="p">[]</span><span class="n">Summary</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">Unmarshal</span><span class="p">(</span><span class="n">body</span><span class="p">,</span> <span class="o">&</span><span class="n">summaries</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">total</span> <span class="o">:=</span> <span class="m">0</span>
+ <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">summary</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">summaries</span> <span class="p">{</span>
+ <span class="n">total</span> <span class="o">+=</span> <span class="n">summary</span><span class="o">.</span><span class="n">TrackedSecond</span>
+ <span class="p">}</span>
+
+ <span class="n">hourlyRate</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">strconv</span><span class="o">.</span><span class="n">Atoi</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"TOGGL_HOURLY_RATE"</span><span class="p">))</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">result</span> <span class="o"><-</span> <span class="p">(</span><span class="n">total</span> <span class="o">/</span> <span class="m">3600</span><span class="p">)</span> <span class="o">*</span> <span class="n">hourlyRate</span>
+<span class="p">}</span>
+
+<span class="k">type</span> <span class="n">CurrencyData</span> <span class="k">struct</span> <span class="p">{</span>
+ <span class="n">Quotes</span> <span class="k">struct</span> <span class="p">{</span>
+ <span class="n">USDBRL</span> <span class="kt">float64</span> <span class="s">`json:"USDBRL"`</span>
+ <span class="p">}</span> <span class="s">`json:"quotes"`</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="n">husky</span><span class="p">(</span><span class="n">result</span> <span class="k">chan</span><span class="o"><-</span> <span class="kt">int</span><span class="p">,</span> <span class="n">wg</span> <span class="o">*</span><span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">defer</span> <span class="n">wg</span><span class="o">.</span><span class="n">Done</span><span class="p">()</span>
+
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">currency</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"HUSKY_CURRENCY"</span><span class="p">)</span>
+ <span class="n">url</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"https://api.apilayer.com/currency_data/live?base=USD&symbols=%s&currencies=%s"</span><span class="p">,</span> <span class="n">currency</span><span class="p">,</span> <span class="n">currency</span><span class="p">)</span>
+ <span class="p">)</span>
+
+ <span class="n">req</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">http</span><span class="o">.</span><span class="n">NewRequest</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">MethodGet</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="no">nil</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">req</span><span class="o">.</span><span class="n">Header</span><span class="o">.</span><span class="n">Set</span><span class="p">(</span><span class="s">"apikey"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"APILAYER_APIKEY"</span><span class="p">))</span>
+
+ <span class="n">client</span> <span class="o">:=</span> <span class="o">&</span><span class="n">http</span><span class="o">.</span><span class="n">Client</span><span class="p">{}</span>
+ <span class="n">resp</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">client</span><span class="o">.</span><span class="n">Do</span><span class="p">(</span><span class="n">req</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">resp</span><span class="o">.</span><span class="n">Body</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="n">body</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">io</span><span class="o">.</span><span class="n">ReadAll</span><span class="p">(</span><span class="n">resp</span><span class="o">.</span><span class="n">Body</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="k">var</span> <span class="n">data</span> <span class="n">CurrencyData</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">Unmarshal</span><span class="p">(</span><span class="n">body</span><span class="p">,</span> <span class="o">&</span><span class="n">data</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">monthlySalary</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">strconv</span><span class="o">.</span><span class="n">ParseFloat</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"HUSKY_MONTHLY_SALARY"</span><span class="p">),</span> <span class="m">64</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">gross</span> <span class="o">:=</span> <span class="kt">int</span><span class="p">(</span><span class="n">math</span><span class="o">.</span><span class="n">Floor</span><span class="p">(</span><span class="n">monthlySalary</span> <span class="o">*</span> <span class="n">data</span><span class="o">.</span><span class="n">Quotes</span><span class="o">.</span><span class="n">USDBRL</span><span class="p">))</span>
+ <span class="n">deduction</span> <span class="o">:=</span> <span class="n">gross</span> <span class="o">*</span> <span class="m">1</span> <span class="o">/</span> <span class="m">100</span>
+ <span class="n">result</span> <span class="o"><-</span> <span class="n">gross</span> <span class="o">-</span> <span class="n">deduction</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">ch</span> <span class="o">=</span> <span class="nb">make</span><span class="p">(</span><span class="k">chan</span> <span class="kt">int</span><span class="p">)</span>
+ <span class="n">wg</span> <span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span>
+ <span class="n">funcs</span> <span class="o">=</span> <span class="p">[]</span><span class="k">func</span><span class="p">(</span><span class="k">chan</span><span class="o"><-</span> <span class="kt">int</span><span class="p">,</span> <span class="o">*</span><span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span><span class="p">){</span><span class="n">toggl</span><span class="p">,</span> <span class="n">husky</span><span class="p">}</span>
+ <span class="p">)</span>
+
+ <span class="n">wg</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">funcs</span><span class="p">))</span>
+
+ <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">fun</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">funcs</span> <span class="p">{</span>
+ <span class="k">go</span> <span class="n">fun</span><span class="p">(</span><span class="n">ch</span><span class="p">,</span> <span class="o">&</span><span class="n">wg</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="k">go</span> <span class="k">func</span><span class="p">()</span> <span class="p">{</span>
+ <span class="n">wg</span><span class="o">.</span><span class="n">Wait</span><span class="p">()</span>
+ <span class="nb">close</span><span class="p">(</span><span class="n">ch</span><span class="p">)</span>
+ <span class="p">}()</span>
+
+ <span class="k">var</span> <span class="n">sum</span> <span class="kt">int</span>
+ <span class="k">for</span> <span class="n">result</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">ch</span> <span class="p">{</span>
+ <span class="n">sum</span> <span class="o">+=</span> <span class="n">result</span>
+ <span class="p">}</span>
+
+ <span class="n">fmt</span><span class="o">.</span><span class="n">Print</span><span class="p">(</span><span class="n">sum</span><span class="p">)</span>
+<span class="p">}</span>
+</code></pre></div></div>
+
+<p>So in the action, all you have to do is run and update the README periodically.</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Run</span>
+
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">workflow_dispatch</span><span class="pi">:</span>
+ <span class="na">schedule</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*"</span>
+
+<span class="na">permissions</span><span class="pi">:</span>
+ <span class="na">contents</span><span class="pi">:</span> <span class="s">write</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">run</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup Go</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-go@v4</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Update Markdown</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">salary=$(make run)</span>
+
+ <span class="s">cat > README.md <<EOF</span>
+ <span class="s">### Salary</span>
+
+ <span class="s">EOF</span>
+
+ <span class="s">printf "%s\n" "$salary" >> README.md</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">APILAYER_APIKEY</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">HUSKY_MONTHLY_SALARY</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">HUSKY_CURRENCY</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">TOGGL_WORKSPACE_ID</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">TOGGL_EMAIL</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">TOGGL_PASSWORD</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">TOGGL_HOURLY_RATE</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Commit report</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">git config --global user.name 'github-actions[bot]'</span>
+ <span class="s">git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com'</span>
+ <span class="s">git add README.md</span>
+ <span class="s">git commit -am "Automated Salary Update" || true</span>
+ <span class="s">git push</span>
+</code></pre></div></div>
+
+<h2 id="blog">Blog</h2>
+
+<p>The next step is to automate the publishing of new blog posts using ChatGPT ;-).</p>
+
+ Rodrigo Delduca
+ rodrigo@delduca.org
+ https://nullonerror.org/
+
+
+ NULL on error
+ NULL on error
+
+ https://nullonerror.org/feed.xml
+ 2025-04-17T02:14:57+00:00
+
+
+
+
+ Working with compressed binary data on AWS IoT Core
+
+ https://nullonerror.org/2023/10/17/working-with-compressed-binary-data-on-aws-iot-core
+ 2023-10-17T00:00:00+00:00
+ <h3 id="objective">Objective</h3>
+
+<p>Today we will see how to send compressed CBOR with ZSTD from an ESP32 microcontroller through an MQTT topic, passing through AWS IoT Core, and finally being decoded in a TypeScript-written Lambda function.</p>
+
+<h3 id="intro">Intro</h3>
+
+<p>Firstly, what is a microcontroller (MCU)? In this article, we will be using the ESP32 microcontroller from Espressif. It is a highly affordable and inexpensive microcontroller that comes with built-in WiFi and Bluetooth, making it ideal for IoT projects as we will see today. It also boasts a generous amount of flash memory and RAM, as well as a powerful dual-core 32-bit CPU.</p>
+
+<p>Another tool we will be using is PlatformIO, a development framework that functions as a series of plugins and command-line tools for VSCode. With PlatformIO, we have everything we need, from project setup, board selection, serial port speed, compilation flags (yes, we’ll be compiling code in C and C++), and more. Installing it is quite simple; just visit https://platformio.org/ and follow the instructions.</p>
+
+<p>Lastly, to streamline our project and infrastructure, we will be using the Serverless Framework, a framework designed for developing serverless solutions. In our case, we will be using a Lambda function to receive messages sent to the topic. The Serverless Framework is a perfect fit for this scenario and works seamlessly with AWS Amazon.</p>
+
+<h3 id="compressed-binary-data">Compressed Binary Data</h3>
+
+<p>We could certainly use JSON. In fact, with JSON, it is possible to perform queries using the WHERE clause (Yes, IoT Core supports SQL, and we will delve into that later). However, our objective here is to save as many bytes as possible. Imagine that our application will send and receive data via a costly and unreliable telephone connection. Therefore, we need to compress the data as much as possible.</p>
+
+<p>Firstly, let’s construct the raw payload in CBOR. CBOR is an interchangeable binary format specified in an RFC and supported by various programming languages (it is derived from MsgPack, in case you’ve heard of it before).</p>
+
+<p>Since this part will be done on the microcontroller side, we will be using the C++ language with the <code class="language-plaintext highlighter-rouge">ssilverman/libCBOR</code> library. To do this, we open the <code class="language-plaintext highlighter-rouge">platformio.ini</code> file and add the dependency under <code class="language-plaintext highlighter-rouge">lib_deps</code>.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf"><CBOR.h></span><span class="cp">
+#include</span> <span class="cpf"><CBOR_parsing.h></span><span class="cp">
+#include</span> <span class="cpf"><CBOR_streams.h></span><span class="cp">
+</span>
+<span class="k">namespace</span> <span class="n">cbor</span> <span class="o">=</span> <span class="o">::</span><span class="n">qindesign</span><span class="o">::</span><span class="n">cbor</span><span class="p">;</span>
+</code></pre></div></div>
+
+<p>Since we are developing for a microcontroller, memory allocation is a critical concern as it can lead to fragmentation and other issues. Therefore, we will define a buffer of 256 bytes for reading and writing CBOR messages, which is more than sufficient for our current application.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">constexpr</span> <span class="kt">size_t</span> <span class="n">kBytesSize</span> <span class="o">=</span> <span class="mi">256</span><span class="p">;</span>
+<span class="kt">uint8_t</span> <span class="n">bytes</span><span class="p">[</span><span class="n">kBytesSize</span><span class="p">]{</span><span class="mi">0</span><span class="p">};</span>
+<span class="n">cbor</span><span class="o">::</span><span class="n">BytesStream</span> <span class="n">bs</span><span class="p">{</span><span class="n">bytes</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">bytes</span><span class="p">)};</span>
+<span class="n">cbor</span><span class="o">::</span><span class="n">BytesPrint</span> <span class="n">bp</span><span class="p">{</span><span class="n">bytes</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">bytes</span><span class="p">)};</span>
+</code></pre></div></div>
+
+<p>Next, let’s prepare to use ZSTD. The steps are the same as with CBOR.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf"><zstd.h></span><span class="cp">
+</span>
+<span class="k">constexpr</span> <span class="kt">size_t</span> <span class="n">kZBytesSize</span> <span class="o">=</span> <span class="mi">256</span><span class="p">;</span>
+<span class="kt">uint8_t</span> <span class="n">zbytes</span><span class="p">[</span><span class="n">kZBytesSize</span><span class="p">]{</span><span class="mi">0</span><span class="p">};</span>
+</code></pre></div></div>
+
+<p>Finally, and not least importantly, let’s prepare to send messages to a topic on IoT Core. The setup is a bit complex and requires attention. Firstly, we need to add the <code class="language-plaintext highlighter-rouge">knolleary/PubSubClient</code> library.</p>
+
+<p>We will need three things from AWS IoT Core: the Certificate Authority (CA) certificate, the Thing certificate, and the private key certificate of the Thing. We will see how to create rules in IoT Core to allow things to subscribe and publish only to specific topics for security reasons. One more thing, in our project, we will hardcode these values in the flash memory of the ESP32 using the <em>PROGMEM</em> directive.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf"><WiFiClientSecure.h></span><span class="cp">
+#include</span> <span class="cpf"><PubSubClient.h></span><span class="cp">
+</span>
+<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">AWS_IOT_ENDPOINT</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"....amazonaws.com"</span><span class="p">;</span>
+<span class="k">static</span> <span class="k">const</span> <span class="kt">uint16_t</span> <span class="n">AWS_IOT_PORT</span> <span class="o">=</span> <span class="mi">8883</span><span class="p">;</span>
+<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">THINGNAME</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"My ESP32"</span><span class="p">;</span>
+
+<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">AWS_CERT_CA</span><span class="p">[]</span> <span class="n">PROGMEM</span> <span class="o">=</span> <span class="s">R"EOF(
+-----BEGIN CERTIFICATE-----
+... AWS Amazon Certificate Authority (CA)
+-----END CERTIFICATE-----
+)EOF"</span><span class="p">;</span>
+
+<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">AWS_CERT_CRT</span><span class="p">[]</span> <span class="n">PROGMEM</span> <span class="o">=</span> <span class="s">R"KEY(
+-----BEGIN CERTIFICATE-----
+... Thing's certificate
+-----END CERTIFICATE-----
+)KEY"</span><span class="p">;</span>
+
+<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">AWS_CERT_PRIVATE</span><span class="p">[]</span> <span class="n">PROGMEM</span> <span class="o">=</span> <span class="s">R"KEY(
+-----BEGIN RSA PRIVATE KEY-----
+... Thing's private certificate
+-----END RSA PRIVATE KEY-----
+)KEY"</span><span class="p">;</span>
+</code></pre></div></div>
+
+<p>We need an instance of PubSub to publish and subscribe to topics, as well as a WiFi client to configure the aforementioned keys.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">WiFiClientSecure</span> <span class="n">net</span><span class="p">;</span>
+<span class="n">PubSubClient</span> <span class="nf">pubsub</span><span class="p">(</span><span class="n">AWS_IOT_ENDPOINT</span><span class="p">,</span> <span class="n">AWS_IOT_PORT</span><span class="p">,</span> <span class="n">net</span><span class="p">);</span>
+</code></pre></div></div>
+
+<p>Now let’s proceed with a typical Arduino program structure, with the setup and loop functions. In the setup function, we will connect to the WiFi network, and then we will configure the keys in the WiFiClientSecure object so that the PubSubClient can use them to connect to IoT Core. Without these keys, the connection will be rejected by the AWS Amazon servers.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span>
+<span class="p">{</span>
+ <span class="c1">// For debugging.</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span>
+
+ <span class="c1">// Connect to WiFi.</span>
+ <span class="n">WiFi</span><span class="p">.</span><span class="n">mode</span><span class="p">(</span><span class="n">WIFI_STA</span><span class="p">);</span>
+ <span class="n">WiFi</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="n">WIFI_SSID</span><span class="p">,</span> <span class="n">WIFI_PASSWORD</span><span class="p">);</span>
+
+ <span class="c1">// Wait for the connection.</span>
+ <span class="k">while</span> <span class="p">(</span><span class="n">WiFi</span><span class="p">.</span><span class="n">status</span><span class="p">()</span> <span class="o">!=</span> <span class="n">WL_CONNECTED</span><span class="p">)</span>
+ <span class="p">{</span>
+ <span class="n">delay</span><span class="p">(</span><span class="mi">500</span><span class="p">);</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">"."</span><span class="p">);</span>
+ <span class="p">}</span>
+
+ <span class="c1">// Show WiFi information.</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">();</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">"Connected to "</span><span class="p">);</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="n">WIFI_SSID</span><span class="p">);</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">"IP address: "</span><span class="p">);</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="n">WiFi</span><span class="p">.</span><span class="n">localIP</span><span class="p">());</span>
+
+ <span class="c1">// Setup the certificates.</span>
+ <span class="n">net</span><span class="p">.</span><span class="n">setCACert</span><span class="p">(</span><span class="n">AWS_CERT_CA</span><span class="p">);</span>
+ <span class="n">net</span><span class="p">.</span><span class="n">setCertificate</span><span class="p">(</span><span class="n">AWS_CERT_CRT</span><span class="p">);</span>
+ <span class="n">net</span><span class="p">.</span><span class="n">setPrivateKey</span><span class="p">(</span><span class="n">AWS_CERT_PRIVATE</span><span class="p">);</span>
+
+ <span class="c1">// Connect to IoT Core.</span>
+ <span class="n">pubsub</span><span class="p">.</span><span class="n">connect</span><span class="p">(</span><span class="n">THINGNAME</span><span class="p">);</span>
+<span class="p">}</span>
+</code></pre></div></div>
+
+<p>If everything goes well, the <code class="language-plaintext highlighter-rouge">connect</code> method of PubSubClient will return <em>true</em>. We won’t perform that check; instead, we’ll attempt to publish directly and monitor the results on the IoT Core dashboard.</p>
+
+<p>Now comes the interesting part. We will assemble our payload in CBOR format, compress it using ZSTD, and publish it to a topic.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">on_sensor</span><span class="p">(</span><span class="k">const</span> <span class="kt">uint64_t</span> <span class="o">*</span><span class="n">sensor_data</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">sensor_data_size</span><span class="p">)</span> <span class="p">{</span>
+ <span class="c1">// Create a new instance of the CBOR writer.</span>
+ <span class="n">cbor</span><span class="o">::</span><span class="n">Writer</span> <span class="n">cbor</span><span class="p">{</span><span class="n">bp</span><span class="p">};</span>
+
+ <span class="c1">// Reset BytesPrint instance.</span>
+ <span class="n">bp</span><span class="p">.</span><span class="n">reset</span><span class="p">();</span>
+
+ <span class="c1">// Indicates that what follows is an array of a certain size.</span>
+ <span class="n">cbor</span><span class="p">.</span><span class="n">beginArray</span><span class="p">(</span><span class="n">sensor_data_size</span><span class="p">);</span>
+ <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">sensor_data_size</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
+ <span class="p">{</span>
+ <span class="c1">// For each item in the array, write it to CBOR.</span>
+ <span class="n">cbor</span><span class="p">.</span><span class="n">writeUnsignedInt</span><span class="p">(</span><span class="n">sensor_data</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
+ <span class="p">}</span>
+
+ <span class="c1">// Get the final CBOR size.</span>
+ <span class="k">const</span> <span class="kt">size_t</span> <span class="n">lenght</span> <span class="o">=</span> <span class="n">cbor</span><span class="p">.</span><span class="n">getWriteSize</span><span class="p">();</span>
+
+ <span class="c1">// Compress the CBOR buffer using ZSTD.</span>
+ <span class="kt">size_t</span> <span class="n">compressedSize</span> <span class="o">=</span> <span class="n">ZSTD_compress</span><span class="p">(</span><span class="n">zbytes</span><span class="p">,</span> <span class="n">kZBytesSize</span><span class="p">,</span> <span class="n">bytes</span><span class="p">,</span> <span class="n">lenght</span><span class="p">,</span> <span class="n">ZSTD_CLEVEL_DEFAULT</span><span class="p">);</span>
+
+ <span class="c1">// Publish the binary compressed data onto the topic.</span>
+ <span class="kt">char</span> <span class="n">topic</span><span class="p">[</span><span class="mi">128</span><span class="p">];</span>
+ <span class="n">sprintf</span><span class="p">(</span><span class="n">topic</span><span class="p">,</span> <span class="s">"sensors/%s/v1"</span><span class="p">,</span> <span class="n">THINGNAME</span><span class="p">);</span>
+ <span class="n">pubsub</span><span class="p">.</span><span class="n">publish</span><span class="p">(</span><span class="n">topic</span><span class="p">,</span> <span class="n">zbytes</span><span class="p">,</span> <span class="n">compressedSize</span><span class="p">,</span> <span class="nb">false</span><span class="p">);</span>
+<span class="p">}</span>
+</code></pre></div></div>
+
+<h3 id="on-the-cloud-side">On the cloud side</h3>
+
+<p>As I mentioned before, we will be using the Serverless Framework, which follows the Infrastructure as Code (IaC) approach. So what we’ll do is create the rules for the Things, create a lambda function, and define the trigger for that lambda as IoT Core. Since the data is in binary format, we will encode it in Base64 in the IoT Core SQL.</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">service</span><span class="pi">:</span> <span class="s">myiot</span>
+
+<span class="na">configValidationMode</span><span class="pi">:</span> <span class="s">error</span>
+
+<span class="na">frameworkVersion</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3"</span>
+
+<span class="na">provider</span><span class="pi">:</span>
+ <span class="na">name</span><span class="pi">:</span> <span class="s">aws</span>
+ <span class="na">runtime</span><span class="pi">:</span> <span class="s">nodejs18.x</span>
+ <span class="na">architecture</span><span class="pi">:</span> <span class="s">arm64</span>
+ <span class="na">stage</span><span class="pi">:</span> <span class="s">development</span>
+
+<span class="na">resources</span><span class="pi">:</span>
+ <span class="na">Resources</span><span class="pi">:</span>
+ <span class="na">IoTPolicy</span><span class="pi">:</span>
+ <span class="na">Type</span><span class="pi">:</span> <span class="s">AWS::IoT::Policy</span>
+ <span class="na">Properties</span><span class="pi">:</span>
+ <span class="na">PolicyName</span><span class="pi">:</span> <span class="s">IoTPolicy</span>
+ <span class="na">PolicyDocument</span><span class="pi">:</span>
+ <span class="na">Version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2012-10-17"</span>
+ <span class="na">Statement</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">Effect</span><span class="pi">:</span> <span class="s">Allow</span>
+ <span class="na">Action</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">iot:Connect</span>
+ <span class="na">Resource</span><span class="pi">:</span> <span class="s">arn:aws:iot:*:*:client/\${iot:Connection.Thing.ThingName}</span>
+ <span class="pi">-</span> <span class="na">Effect</span><span class="pi">:</span> <span class="s">Allow</span>
+ <span class="na">Action</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">iot:Publish</span>
+ <span class="pi">-</span> <span class="s">iot:Receive</span>
+ <span class="na">Resource</span><span class="pi">:</span> <span class="s">arn:aws:iot:*:*:topic/*/\${iot:Connection.Thing.ThingName}/*</span>
+ <span class="pi">-</span> <span class="na">Effect</span><span class="pi">:</span> <span class="s">Allow</span>
+ <span class="na">Action</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">iot:Subscribe</span>
+ <span class="na">Resource</span><span class="pi">:</span> <span class="s">arn:aws:iot:*:*:topicfilter/*/\${iot:Connection.Thing.ThingName}/*</span>
+
+<span class="na">functions</span><span class="pi">:</span>
+ <span class="na">tracker</span><span class="pi">:</span>
+ <span class="na">handler</span><span class="pi">:</span> <span class="s">app/mylambda.handler</span>
+ <span class="na">events</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">iot</span><span class="pi">:</span>
+ <span class="na">sql</span><span class="pi">:</span> <span class="s2">"</span><span class="s">SELECT</span><span class="nv"> </span><span class="s">timestamp()</span><span class="nv"> </span><span class="s">AS</span><span class="nv"> </span><span class="s">timestamp,</span><span class="nv"> </span><span class="s">topic()</span><span class="nv"> </span><span class="s">AS</span><span class="nv"> </span><span class="s">topic,</span><span class="nv"> </span><span class="s">encode(*,</span><span class="nv"> </span><span class="s">'base64')</span><span class="nv"> </span><span class="s">AS</span><span class="nv"> </span><span class="s">data</span><span class="nv"> </span><span class="s">FROM</span><span class="nv"> </span><span class="s">'sensors/+/v1'"</span>
+ <span class="na">sqlVersion</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2016-03-23"</span>
+
+<span class="na">plugins</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">serverless-plugin-typescript</span>
+</code></pre></div></div>
+
+<p>The YAML file for serverless may seem a bit intimidating at first, but it’s simple. It essentially does two things. Firstly, it creates an IoTPolicy for the things. A Thing can only publish or subscribe to its own topic with IoTPolicies, allowing for a granular level of security.</p>
+
+<p>Also, it’s worth noting that we are using ARM64 architecture. Lambdas running on the ARM architecture are not only more cost-effective but also more efficient compared to x86_64.</p>
+
+<p>The second part is the definition of the lambda function and its trigger. It uses a query in IoT Core, which is the key to working with binary data in IoT Core. You need to encode the data in <strong>base64</strong> before sending it to the lambda function; otherwise, it won’t work. This lambda function is “listening” to the sensors topic from any Thing, hence the plus symbol in the topic. By default, I prefer to version APIs, and a topic shouldn’t be an exception. Therefore, this is version 1 of my project.</p>
+
+<p>Now let’s take a look at the lambda function itself. For this project, I chose to use TypeScript, but AWS Lambda and the Serverless Framework support various programming languages.</p>
+
+<p>The code is quite straightforward. It receives the binary payload in the <code class="language-plaintext highlighter-rouge">data</code> parameter (as defined in the SQL statement above). First, it decodes the payload from base64 to binary. Then, it decompresses it using ZSTD and, finally, utilizes the CBOR library to parse it into a JavaScript object, ready to be used.</p>
+
+<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">decompressSync</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@skhaz/zstd</span><span class="dl">"</span><span class="p">;</span>
+<span class="k">import</span> <span class="p">{</span> <span class="nx">decodeFirstSync</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">cbor</span><span class="dl">"</span><span class="p">;</span>
+
+<span class="k">export</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">handler</span><span class="p">(</span><span class="nx">event</span><span class="p">:</span> <span class="p">{</span> <span class="nl">timestamp</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span> <span class="nl">topic</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="nl">data</span><span class="p">:</span> <span class="kr">string</span> <span class="p">})</span> <span class="p">{</span>
+ <span class="c1">// Extract from event some variables.</span>
+ <span class="kd">const</span> <span class="p">{</span> <span class="nx">timestamp</span><span class="p">,</span> <span class="nx">topic</span><span class="p">,</span> <span class="nx">data</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">event</span><span class="p">;</span>
+
+ <span class="c1">// Decode from base64 to binary.</span>
+ <span class="kd">const</span> <span class="nx">buffer</span> <span class="o">=</span> <span class="nx">Buffer</span><span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="dl">"</span><span class="s2">base64</span><span class="dl">"</span><span class="p">);</span>
+ <span class="c1">// Decompress using ZSTD algorithm.</span>
+ <span class="kd">const</span> <span class="nx">cbor</span> <span class="o">=</span> <span class="nx">decompressSync</span><span class="p">(</span><span class="nx">buffer</span><span class="p">);</span>
+ <span class="c1">// Parse the binary CBOR to JavaScript object.</span>
+ <span class="kd">const</span> <span class="nx">payload</span> <span class="o">=</span> <span class="nx">decodeFirstSync</span><span class="p">(</span><span class="nx">cbor</span><span class="p">);</span>
+
+ <span class="c1">// Print the sensor array data.</span>
+ <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">payload</span><span class="p">);</span>
+<span class="p">}</span>
+</code></pre></div></div>
+
+<h3 id="conclusion">Conclusion</h3>
+
+<p>In conclusion, it is possible to save a few bytes or even more with these techniques while utilizing cloud solutions like IoT Core in your projects. It allows for efficient data handling and enables the integration of cloud services into your applications.</p>
+
+<p>The compression ratio can vary dramatically, depending solely on the input. In my experiments with numerical data, it ranged from around 30% to 40%.</p>
+
+ Rodrigo Delduca
+ rodrigo@delduca.org
+ https://nullonerror.org/
+
+
+ NULL on error
+ NULL on error
+
+ https://nullonerror.org/feed.xml
+ 2025-04-17T02:14:57+00:00
+
+
+
+
+ Gentlemen’s Club vs Strip Club: Is There Really a Difference?
+
+ http://feeds.feedburner.com/5e0a2504-6b2a-4c40-9a68-30b5d8d58eb0
+ 2023-08-15T13:21:00+00:00
+ <p>Confused about the distinctions between Strip Clubs and Gentlemen&#39;s Clubs in Las Vegas? In this episode, we&#39;re going to dissect what sets these venues apart and what you can anticipate from the very best of both. Be sure to listen in as we demystify the Las Vegas club scene.</p>
+<p><br></p>
+<p><a href="https://www.stripclubconcierge.com/" target="_blank" rel="noopener noreferer">https://www.stripclubconcierge.com/</a></p>
+<p><br></p>
+<p><a href="https://www.stripclubconcierge.com/gentlemens-club-vs-strip-club/" target="_blank" rel="noopener noreferer">https://www.stripclubconcierge.com/gentlemens-club-vs-strip-club/</a></p>
+<p><br></p>
+<p>Tags: Gentlemens Clubs Las Vegas, Best Gentlemens Clubs Las Vegas, Treasures Gentlemen’s Club, Crazy Horse III Gentlemen’s Club</p>
+
+ Thiago Avelino
+ https://www.stripclubconcierge.com/
+
+
+ The Afterparty Podcast /w Strip Club Concierge Las Vegas!
+ Looking for more a little more info about the naughty side of Las Vegas? Insider reviews on the hottest venues? The best strip clubs worth visiting in Vegas? You'll find it all here at the Strip Club Concierge podcast! And when you're ready, you can book
+
+ http://feeds.feedburner.com/pyavelino
+ 2025-04-17T02:14:58+00:00
+ Strip Club Concierge
+
+
+
+
+ Las Vegas Strippers 101: Your 2023 Guide to "Strippers to the Room" (Private Bachelor Party Bookings)
+
+ http://feeds.feedburner.com/90e07b6c-bdce-437a-98b9-f8df1b0fca61
+ 2023-08-12T13:18:00+00:00
+ <p>Are you mulling over hiring Las Vegas strippers for a bachelor party? In this episode, we help you weigh up the pros and cons between opting for private in-room entertainers and taking the gang to a strip club. Make sure you tune in to get all the information you need for planning the ultimate bachelor party.</p>
+<p><br></p>
+<p><a href="https://www.stripclubconcierge.com/" target="_blank" rel="noopener noreferer">https://www.stripclubconcierge.com/</a></p>
+<p><br></p>
+<p><a href="https://www.stripclubconcierge.com/las-vegas-strippers/" target="_blank" rel="noopener noreferer">https://www.stripclubconcierge.com/las-vegas-strippers/</a></p>
+<p><br></p>
+<p>Tags: Las Vegas Bachelor Party, Las Vegas Guides, Las Vegas Strippers, Outcall Strippers, Private Strippers</p>
+
+ Thiago Avelino
+ https://www.stripclubconcierge.com/
+
+
+ The Afterparty Podcast /w Strip Club Concierge Las Vegas!
+ Looking for more a little more info about the naughty side of Las Vegas? Insider reviews on the hottest venues? The best strip clubs worth visiting in Vegas? You'll find it all here at the Strip Club Concierge podcast! And when you're ready, you can book
+
+ http://feeds.feedburner.com/pyavelino
+ 2025-04-17T02:14:58+00:00
+ Strip Club Concierge
+
+
+
+
+ Gerando documentação vimdoc de plugins Neovim com Github Actions
+
+ https://dev.to/ellisonleao/gerando-documentacao-vimdoc-de-plugins-neovim-com-github-actions-22if
+ 2023-07-26T20:40:59+00:00
+ <p><em>Dificuldade: intermediário</em></p>
+
+<p>Escrever documentação nem sempre é uma das tarefas mais prazerosas, mas ter uma boa documentação nos nossos projetos é o que realmente faz diferença. Ainda mais quando usamos ferramentas que nos auxiliam nessa escrita.</p>
+
+<p>Nesse post vamos ensinar como automatizar a geração da documentação específica para plugins <strong>Neovim</strong> utilizando <strong>Github Actions</strong></p>
+
+<h2>
+
+
+ Entendendo o fluxo
+</h2>
+
+<p>Assumindo que entendemos o fluxo básico das <strong>Github Actions</strong>, temos o seguinte fluxo para a geração da doc:</p>
+
+<ol>
+<li>Fazemos um push na branch principal (geralmente chamada de <code>main</code>)</li>
+<li>Um job chamado <code>docs</code> é iniciado com os seguintes passos:
+
+<ul>
+<li>Conversão do <code>README.md</code> do seu projeto em um arquivo .txt no formato <a href="https://vimdoc.sourceforge.net/htmldoc/helphelp.html#help-writing" rel="noopener noreferrer">vimdoc</a>
+</li>
+<li>Criamos um commit e fazemos o push na mesma branch com o resultado da conversão</li>
+</ul>
+</li>
+</ol>
+
+<p>Convertendo isso para o <strong>Github Actions</strong>, temos:</p>
+
+<ol>
+<li>Crie um arquivo <code>.github/workflows/docs.yml</code> com o seguinte conteúdo:
+</li>
+</ol>
+
+<div class="highlight js-code-highlight">
+<pre class="highlight yaml"><code><span class="na">on</span><span class="pi">:</span>
+ <span class="na">push</span><span class="pi">:</span>
+ <span class="na">branches</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">main</span> <span class="c1"># aqui nossa branch principal se chama `main`</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">docs</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">needs</span><span class="pi">:</span> <span class="s">test</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v3</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">panvimdoc</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">kdheepak/panvimdoc@main</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">vimdoc</span><span class="pi">:</span> <span class="s">nome-do-seu-projeto</span> <span class="c1"># sem extensão .txt</span>
+ <span class="na">version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Neovim</span><span class="nv"> </span><span class="s">>=</span><span class="nv"> </span><span class="s">0.8.0"</span>
+ <span class="na">demojify</span><span class="pi">:</span> <span class="kc">true</span> <span class="c1"># coloque false caso queira manter os emojis</span>
+ <span class="na">treesitter</span><span class="pi">:</span> <span class="kc">true</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Push changes</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">stefanzweifel/git-auto-commit-action@v4</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">commit_message</span><span class="pi">:</span> <span class="s2">"</span><span class="s">auto-generate</span><span class="nv"> </span><span class="s">vimdoc"</span>
+ <span class="na">commit_user_name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">github-actions[bot]"</span>
+ <span class="na">commit_user_email</span><span class="pi">:</span> <span class="s2">"</span><span class="s">github-actions[bot]@users.noreply.github.com"</span>
+ <span class="na">commit_author</span><span class="pi">:</span> <span class="s2">"</span><span class="s">github-actions[bot]</span><span class="nv"> </span><span class="s"><github-actions[bot]@users.noreply.github.com>"</span>
+</code></pre>
+
+</div>
+
+
+
+<h2>
+
+
+ Preparando seu README
+</h2>
+
+<p>Para um bom resultado de uma documentação vimdoc, recomendamos que seu README tenha pelo menos:</p>
+
+<ul>
+<li>Uma seção <code>Sobre</code>
+</li>
+<li>Uma seção <code>Instalação</code>
+</li>
+<li>Uma seção <code>Como usar</code>
+</li>
+</ul>
+
+<p>O uso de tabelas, bullet points também é suportado e recomendado. Quanto mais detalhes conseguir colocar na documentação, melhor ficará o resultado (até emojis são suportados!)</p>
+
+<h2>
+
+
+ Colocando tudo para funcionar
+</h2>
+
+<p>Antes de commitar e rodar a action pela primeira vez, crie o arquivo vazio da doc:<br />
+</p>
+
+<div class="highlight js-code-highlight">
+<pre class="highlight shell"><code><span class="nv">$ </span><span class="nb">touch </span>doc/nome-do-seu-projeto.txt
+<span class="nv">$ </span>git add doc/nome-do-seu-projeto.txt
+<span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s1">'adicionando arquivo vimdoc'</span>
+<span class="nv">$ </span>git push
+</code></pre>
+
+</div>
+
+
+
+<p>Agora envie o novo worflow:<br />
+</p>
+
+<div class="highlight js-code-highlight">
+<pre class="highlight shell"><code><span class="nv">$ </span>git add .github/workflows/docs.yml
+<span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s1">'adicionando docs workflow'</span>
+<span class="nv">$ </span>git push
+</code></pre>
+
+</div>
+
+
+
+<p>Se tudo deu certo, você já poderá ver o resultado do workflow com seu vimdoc gerado na branch principal. Exemplo do resultado na imagem abaixo</p>
+
+<p><a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pre86sg64pzp5y69bdg.png" class="article-body-image-wrapper"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pre86sg64pzp5y69bdg.png" alt="resultado do job" width="800" height="377" /></a></p>
+
+<p>Exemplo pode ser visto <a href="https://github.com/ellisonleao/carbon-now.nvim/actions/runs/5673377787" rel="noopener noreferrer">aqui</a></p>
+
+<h2>
+
+
+ Links úteis
+</h2>
+
+<ul>
+<li>Documentação da <a href="https://kdheepak.com/panvimdoc/" rel="noopener noreferrer">github action</a>
+</li>
+<li>Exemplo de um bom <a href="https://github.com/folke/lazy.nvim/blob/main/README.md" rel="noopener noreferrer">README</a> vs o <a href="https://github.com/folke/lazy.nvim/blob/main/doc/lazy.nvim.txt" rel="noopener noreferrer">resultado</a> em vimdoc</li>
+</ul>
+
+<p>Se gostou do artigo, não esqueça de compartilhar em outras redes e aproveita e me segue também! <a href="https://linktr.ee/ellisonleao" rel="noopener noreferrer">https://linktr.ee/ellisonleao</a></p>
+
+ Ellison Leão
+ https://dev.to/ellisonleao
+
+
+ DEV Community: Ellison Leão
+ The latest articles on DEV Community by Ellison Leão (@ellisonleao).
+
+ https://dev.to/feed/ellisonleao
+ 2025-04-17T02:14:10+00:00
+
+
+
+
+ Debugando em Python
+
+ https://paulohrpinheiro.xyz/texts/python/2022-12-17-debugando-em-python.html
+ 2022-12-17T00:00:00+00:00
+ Iniciando a usar o módulo pdb do Python para debugar com maestria, na linha de comando
+
+ Blog do PauloHRPinheiro
+ https://paulohrpinheiro.xyz
+
+
+ Paulo Henrique Rodrigues Pinheiro
+ Blog sobre programação para programadores
+
+ https://paulohrpinheiro.xyz/texts/python/feed.xml
+ 2025-04-17T02:14:01+00:00
+
+
+
+
+ Questões para estudo de algoritmos
+
+ tag:pythonclub.com.br,2022-10-17:/questoes-para-estudo-de-algoritmos.html
+ 2022-10-17T18:00:00+00:00
+ <p>Recentemente li o texto do Maycon Alves, <a href="https://mayconbalves.com.br/3-algoritmos-para-voc%C3%AA-sua-l%C3%B3gica/">"3 algoritmos para você sua lógica"</a>, onde são apresentados 3 problemas para treinar a lógica e escrita de algoritmos: <a href="https://pt.wikipedia.org/wiki/Fatorial">cálculo de fatorial</a>, <a href="https://pt.wikipedia.org/wiki/N%C3%BAmero_primo">identificar se um número é primo</a>, e <a href="https://pt.wikipedia.org/wiki/Sequ%C3%AAncia_de_Fibonacci">calcular os valores da sequência de Fibonacci</a>. São problemas interessantes, e após resolvê-los, pode-se fazer outras perguntas que levam a um entendimento mais profundo desses algoritmos. Eu recomendo que leiam o texto do Maycon primeiro e tentem implementar uma solução para esses problemas propostos, e com isso feito, vamos discutir um pouco sobre eles.</p>
+<h2>Analisando as soluções</h2>
+<p>No texto do Maycon, tem uma dica sobre o problema da sequência de Fibonacci, onde é dito que ele pode ser resolvido usando recursividade ou <em>loops</em>. Vamos analisar essas opções.</p>
+<p>Uma solução recursiva pode ser implementada como a baixo. O código dessa solução é simples e se aproxima bastante da descrição matemática do problema.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">n</span> <span class="o"><=</span> <span class="mi">1</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">n</span>
+ <span class="k">return</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Enquanto uma solução iterativa (com <em>loop</em>) pode ser um pouco mais complicada de se ler:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
+ <span class="n">a</span> <span class="o">=</span> <span class="mi">0</span>
+ <span class="n">b</span> <span class="o">=</span> <span class="mi">1</span>
+ <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
+ <span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> <span class="n">b</span><span class="p">,</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span>
+ <span class="k">return</span> <span class="n">b</span>
+</pre></div>
+
+
+<p>Essas mesmas técnicas podem ser utilizadas para resolver o cálculo do fatorial. Onde uma implementação recursiva e outra iterativa podem ser vistas a baixo:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">fatorial</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">n</span> <span class="o"><=</span> <span class="mi">1</span><span class="p">:</span>
+ <span class="k">return</span> <span class="mi">1</span>
+ <span class="k">return</span> <span class="n">n</span> <span class="o">*</span> <span class="n">fatorial</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">fatorial</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="mi">0</span>
+ <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
+ <span class="n">valor</span> <span class="o">*=</span> <span class="n">i</span>
+ <span class="k">return</span> <span class="n">valor</span>
+</pre></div>
+
+
+<p>Com essas soluções implementadas, vem uma pergunta: Existe alguma diferença entre elas, além da diferença de como isso está expresso no código? Um primeiro teste que pode ser feito é de desempenho visando observar quanto tempo cada implementação leva para calcular a resposta. Os testes foram executados em um notebook com processador Intel Core i7-6500U CPU @ 2.50GHz, memória RAM DDR4 2133MHz, no Python 3.9.2 do Debian 11, desativamente o <em>garbage collector</em> do Python durante a execução das funções para ter um resultado com menos variação, apresentados como uma média de 10 execuções (<a href="https://github.com/eduardoklosowski/blog/tree/main/content/2022-10-17-questoes-para-estudo-de-algoritmos">código utilizado</a>).</p>
+<p>O gráfico a baixo mostra o tempo de execução das implementações que calculam os valores da sequência de Fibonacci, onde é possível observar que a implementação iterativa mantém uma linearidade do tempo conforme vai se pedindo números maiores da sequência, diferente da implementação recursiva, que a cada valor quase dobra o tempo de execução.</p>
+<p><img alt="Gráfico do tempo de execução Fibonacci" src="https://pythonclub.com.br/feeds/images/eduardoklosowski/questoes-para-estudo-de-algoritmos/fibonacci.png" /></p>
+<p>E a baixo pode-se observar o gráfico para as implementações que calculam o fatorial, que devido aos tempos serem mais baixos, possui uma variação um pouco maior, e é possível observar uma tendência de reta para as duas implementações, com a implementação recursiva tendo um ângulo um pouco mais íngreme, implicando em um algoritmo mais lento.</p>
+<p><img alt="Gráfico do tempo de execução fatorial" src="https://pythonclub.com.br/feeds/images/eduardoklosowski/questoes-para-estudo-de-algoritmos/fatorial.png" /></p>
+<p>A partir desses dois gráficos algumas perguntas podem ser feitas: Por que a implementação recursiva do Fibonacci apresentou uma curva que se aproxima de uma exponencial e não de uma reta como as demais? Qual a diferença para a implementação recursiva do fatorial que explicar isso? Implementações recursivas são sempre piores que as implementações iterativas, ou existem casos em elas superem ou se equivalem as iterativas?</p>
+<p>Saindo um pouco desses gráficos, outras questões podem ser levantadas, como: Existe mais algum aspecto ou característica além do tempo de execução (e facilidade de leitura do código) para a escolha entre uma implementação ou outra? Considerando que essas funções vão rodar em um servidor, existe alguma coisa que possa ser feita para melhorar a performance dessas funções, como reaproveitar resultados já calculados para se calcular novos resultados? Como isso poderia ser feito? Quais são as desvantagens dessa abordagem?</p>
+<p>Olhando para o problema de verificar se um número é primo, existe o <a href="https://pt.wikipedia.org/wiki/Crivo_de_Erat%C3%B3stenes">crivo de Eratóstenes</a>, ele é uma implementação eficiente? Existem casos em que ele pode ser uma boa solução ou não? O exemplo a baixo (retirado da Wikipédia) mostra o processo para encontrar todos os números primos até 120, existe alguma forma de adaptá-lo para executar diversas vezes reaproveitando o que já foi calculado, como sempre retornar o próximo número primo?</p>
+<p><img alt="Exemplo do crivo de Eratóstenes" src="https://upload.wikimedia.org/wikipedia/commons/8/8c/New_Animation_Sieve_of_Eratosthenes.gif" /></p>
+<h2>Considerações</h2>
+<p>Se você nunca se deparou com perguntas desse tipo, seja bem-vindo a área de análise de algoritmos, onde após se ter uma solução, busca-se analisar e descrever o comportamento do algoritmo, e até a busca de algoritmos mais eficientes. E trazendo para o dia a dia de um desenvolvedor, essas questões podem ser a resposta do motivo do código funcionar muito bem no computador de quem desenvolveu, mas demorar muito ou apresentar problemas para rodar no servidor de produção, ou com o tempo (e crescimento do volume de dados) começar a dar problemas.</p>
+<p>Nesse artigo eu apenas levantei as perguntas, deixo que cada um busque as respostas, que existem. Sintam-se livres para me procurar para discutir alguma questão ou orientações para encontrar as respostas, seja nos comentários do texto no <a href="https://dev.to/eduardoklosowski/questoes-para-estudo-de-algoritmos-5dab">dev.to</a> ou no <a href="https://twitter.com/eduklosowski">Twitter</a>.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+
+ Eduardo Klosowski
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Meus 10 plugins favoritos para o Neovim em 2022
+
+ https://dev.to/ellisonleao/meus-10-plugins-favoritos-para-o-neovim-em-2022-43g2
+ 2022-05-13T17:56:41+00:00
+ <blockquote>
+<p>Uma breve lista dos melhores plugins pra usar agora e aumentar sua produtividade</p>
+</blockquote>
+
+<p>Quando comecei a usar o Neovim, lá por 2017, depois de passar 6 anos usando Vim, não tinha sentido uma diferença tão grande porque primeiro a API lua era praticamente inexistente e minha configuração ainda era puramente vimscript e meus plugins também. </p>
+
+<p>Mas tudo mudou quando fiz o upgrade pras versões 0.4.x e gradualmente comecei a me aventurar no mundo do Lua, no final de 2019. De lá pra ca já consegui migrar minhas configurações 100% para Lua e 99% dos plugins que uso hoje também são feitos na linguagem. Aqui vai minha lista dos 10 melhores plugins que acho indispensáveis hoje:</p>
+
+<h2>
+
+
+ #10
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/wbthomason" rel="noopener noreferrer">
+ wbthomason
+ </a> / <a href="https://github.com/wbthomason/packer.nvim" rel="noopener noreferrer">
+ packer.nvim
+ </a>
+ </h2>
+ <h3>
+ A use-package inspired plugin manager for Neovim. Uses native packages, supports Luarocks dependencies, written in Lua, allows for expressive config
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<p><strong>NOTICE:</strong></p>
+<p>This repository is currently unmaintained. For the time being (as of August, 2023), it is recommended to use one of the following plugin managers instead:</p>
+<ul>
+<li>
+<a href="https://github.com/folke/lazy.nvim" rel="noopener noreferrer">lazy.nvim</a>: Most stable and maintained plugin manager for Nvim.</li>
+<li>
+<a href="https://github.com/lewis6991/pckr.nvim" rel="noopener noreferrer">pckr.nvim</a>: Spiritual successor of packer.nvim. Functional but not as stable as lazy.nvim.</li>
+</ul>
+<div class="markdown-heading">
+<h1 class="heading-element">packer.nvim</h1>
+</div>
+<p><a href="https://gitter.im/packer-nvim/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/335c9ee0b540d19a29323d900502be99a012414abdc965f6e4bd7047842f6541/68747470733a2f2f6261646765732e6769747465722e696d2f7061636b65722d6e76696d2f636f6d6d756e6974792e737667" alt="Gitter" /></a></p>
+<p><a href="https://github.com/jwiegley/use-package" rel="noopener noreferrer"><code>use-package</code></a> inspired plugin/package management for
+Neovim.</p>
+<p>Have questions? Start a <a href="https://github.com/wbthomason/packer.nvim/discussions" rel="noopener noreferrer">discussion</a>.</p>
+<p>Have a problem or idea? Make an <a href="https://github.com/wbthomason/packer.nvim/issues" rel="noopener noreferrer">issue</a> or a <a href="https://github.com/wbthomason/packer.nvim/pulls" rel="noopener noreferrer">PR</a>.</p>
+<p><strong>Packer is built on native packages. You may wish to read <code>:h packages</code> before continuing</strong></p>
+<div class="markdown-heading">
+<h2 class="heading-element">Table of Contents</h2>
+</div>
+<ol>
+<li><a href="https://github.com/wbthomason/packer.nvim#features" rel="noopener noreferrer">Features</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#requirements" rel="noopener noreferrer">Requirements</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#quickstart" rel="noopener noreferrer">Quickstart</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#bootstrapping" rel="noopener noreferrer">Bootstrapping</a></li>
+<li>
+<a href="https://github.com/wbthomason/packer.nvim#usage" rel="noopener noreferrer">Usage</a>
+<ol>
+<li><a href="https://github.com/wbthomason/packer.nvim#the-startup-function" rel="noopener noreferrer">The startup function</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#custom-initialization" rel="noopener noreferrer">Custom Initialization</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#specifying-plugins" rel="noopener noreferrer">Specifying Plugins</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#performing-plugin-management-operations" rel="noopener noreferrer">Performing plugin management operations</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#extending-packer" rel="noopener noreferrer">Extending packer</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#compiling-lazy-loaders" rel="noopener noreferrer">Compiling Lazy-Loaders</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#user-autocommands" rel="noopener noreferrer">User autocommands</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#using-a-floating-window" rel="noopener noreferrer">Using a floating window</a></li>
+</ol>
+</li>
+<li><a href="https://github.com/wbthomason/packer.nvim#profiling" rel="noopener noreferrer">Profiling</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#debugging" rel="noopener noreferrer">Debugging</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#compatibility-and-known-issues" rel="noopener noreferrer">Compatibility and known issues</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#contributors" rel="noopener noreferrer">Contributors</a></li>
+</ol>
+<div class="markdown-heading">
+<h2 class="heading-element">Features</h2>
+</div>
+<ul>
+<li>Declarative plugin specification</li>
+<li>Support for dependencies</li>
+<li>Support for Luarocks dependencies</li>
+<li>Expressive configuration and lazy-loading options</li>
+<li>Automatically compiles efficient lazy-loading code to improve startup time</li>
+<li>…</li>
+</ul>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/wbthomason/packer.nvim" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Começando por esse incrível gerenciador de plugins, que vai deixar sua vida bem mais fácil na hora de instalar, atualizar e configurar seus plugins, principalmente se você utiliza lua nas suas configurações. Tem suporte a instalação de pacotes do luarocks, <em>lazy loading</em> e mais.</p>
+
+<h2>
+
+
+ #9
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/numToStr" rel="noopener noreferrer">
+ numToStr
+ </a> / <a href="https://github.com/numToStr/Comment.nvim" rel="noopener noreferrer">
+ Comment.nvim
+ </a>
+ </h2>
+ <h3>
+ 🧠 💪 // Smart and powerful comment plugin for neovim. Supports treesitter, dot repeat, left-right/up-down motions, hooks, and more
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">// Comment.nvim </h1>
+</div>
+<p><sup>⚡ Smart and Powerful commenting plugin for neovim ⚡</sup></p>
+<p><a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/42532967/136532939-926a8350-84b7-4e78-b045-fe21b5947388.gif"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F42532967%2F136532939-926a8350-84b7-4e78-b045-fe21b5947388.gif" alt="Comment.nvim" title="Commenting go brrrr" /></a></p>
+<div class="markdown-heading">
+<h3 class="heading-element">✨ Features</h3>
+</div>
+<ul>
+<li>Supports treesitter. <a href="https://github.com/numToStr/Comment.nvim#treesitter" rel="noopener noreferrer">Read more</a>
+</li>
+<li>Supports <code>commentstring</code>. Read <code>:h comment.commentstring</code>
+</li>
+<li>Supports line (<code>//</code>) and block (<code>/* */</code>) comments</li>
+<li>Dot (<code>.</code>) repeat support for <code>gcc</code>, <code>gbc</code> and friends</li>
+<li>Count support for <code>[count]gcc</code> and <code>[count]gbc</code>
+</li>
+<li>Left-right (<code>gcw</code> <code>gc$</code>) and Up-Down (<code>gc2j</code> <code>gc4k</code>) motions</li>
+<li>Use with text-objects (<code>gci{</code> <code>gbat</code>)</li>
+<li>Supports pre and post hooks</li>
+<li>Ignore certain lines, powered by Lua regex</li>
+</ul>
+<div class="markdown-heading">
+<h3 class="heading-element">🚀 Installation</h3>
+</div>
+<ul>
+<li>With <a href="https://github.com/folke/lazy.nvim" rel="noopener noreferrer">lazy.nvim</a>
+</li>
+</ul>
+<div class="highlight highlight-source-lua notranslate position-relative overflow-auto js-code-highlight">
+<pre><span class="pl-c"><span class="pl-c">--</span> add this to your lua/plugins.lua, lua/plugins/init.lua, or the file you keep your other plugins:</span>
+{
+ <span class="pl-s"><span class="pl-pds">'</span>numToStr/Comment.nvim<span class="pl-pds">'</span></span>,
+ <span class="pl-smi">opts</span> <span class="pl-k">=</span> {
+ <span class="pl-c"><span class="pl-c">--</span> add any options here</span>
+ }
+}
+</pre>
+
+</div>
+<ul>
+<li>With <a href="https://github.com/wbthomason/packer.nvim" rel="noopener noreferrer">packer.nvim</a>
+</li>
+</ul>
+<div class="highlight highlight-source-lua notranslate position-relative overflow-auto js-code-highlight">
+<pre><span class="pl-c1">use</span> {
+ <span class="pl-s"><span class="pl-pds">'</span>numToStr/Comment.nvim<span class="pl-pds">'</span></span>,
+ <span class="pl-en">config</span> <span class="pl-k">=</span> <span class="pl-k">function</span>()
+ <span class="pl-c1">require</span>(<span class="pl-s"><span class="pl-pds">'</span>Comment<span class="pl-pds">'</span></span>).<span class="pl-c1">setup</span>()
+ <span class="pl-k">end</span>
+}</pre>
+
+</div>
+<ul>
+<li>With <a href="https://github.com/junegunn/vim-plug" rel="noopener noreferrer">vim-plug</a>
+</li>
+</ul>
+<div class="highlight highlight-source-viml notranslate position-relative overflow-auto js-code-highlight">
+<pre>Plug <span class="pl-s"><span class="pl-pds">'</span>numToStr/Comment.nvim<span class="pl-pds">'</span></span>
+<span class="pl-c"><span class="pl-c">"</span> Somewhere after plug#end()</span></pre>…
+</div>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/numToStr/Comment.nvim" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Um poderoso plugin para inserir e remover comentários em códigos. Tem suporte ao treesitter, que vamos falar mais pra frente. </p>
+
+<h2>
+
+
+ #8
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/neovim" rel="noopener noreferrer">
+ neovim
+ </a> / <a href="https://github.com/neovim/nvim-lspconfig" rel="noopener noreferrer">
+ nvim-lspconfig
+ </a>
+ </h2>
+ <h3>
+ Quickstart configs for Nvim LSP
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">nvim-lspconfig</h1>
+</div>
+<p>nvim-lspconfig is a "data only" repo, providing basic, default <a href="https://neovim.io/doc/user/lsp.html" rel="nofollow noopener noreferrer">Nvim LSP client</a>
+configurations for various LSP servers.</p>
+<p>View <a href="https://github.com/neovim/nvim-lspconfigdoc/configs.md" rel="noopener noreferrer">all configs</a> or <code>:help lspconfig-all</code> from Nvim.</p>
+<div class="markdown-heading">
+<h2 class="heading-element">Important ⚠️
+</h2>
+</div>
+<ul>
+<li>These configs are <strong>best-effort and supported by the community (you).</strong> See <a href="https://github.com/neovim/nvim-lspconfig#contributions" rel="noopener noreferrer">contributions</a>.</li>
+<li>If you found a bug in Nvim LSP (<code>:help lsp</code>), <a href="https://github.com/neovim/neovim/issues/new?assignees=&labels=bug%2Clsp&template=lsp_bug_report.yml" rel="noopener noreferrer">report it to Neovim core</a>
+<ul>
+<li>
+<strong>Do not</strong> report it here. Only configuration data lives here.</li>
+</ul>
+</li>
+<li>This repo only provides <em>configurations</em>. Its programmatic API is deprecated and must not be used externally.
+<ul>
+<li>The "framework" parts (<em>not</em> the configs) of nvim-lspconfig <a href="https://github.com/neovim/neovim/issues/28479" rel="noopener noreferrer">will be upstreamed to Nvim core</a>.</li>
+</ul>
+</li>
+</ul>
+<div class="markdown-heading">
+<h2 class="heading-element">Install</h2>
+
+</div>
+<p><a href="https://luarocks.org/modules/neovim/nvim-lspconfig" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/57aa64ed10a79399509246a1ae220f761b1c35e4d44c6c07595faf4463019fd8/68747470733a2f2f696d672e736869656c64732e696f2f6c7561726f636b732f762f6e656f76696d2f6e76696d2d6c7370636f6e6669673f6c6f676f3d6c756126636f6c6f723d707572706c65" alt="LuaRocks" /></a></p>
+<ul>
+<li>Requires Nvim 0.10 above. Update Nvim and nvim-lspconfig before reporting an issue.</li>
+<li>Install nvim-lspconfig using Vim's "packages" feature:
+<div class="snippet-clipboard-content notranslate position-relative overflow-auto"><pre class="notranslate"><code>git clone https://github.com/neovim/nvim-lspconfig ~/.config/nvim/pack/nvim/start/nvim-lspconfig
+</code></pre></div>
+</li>
+<li>Or use a 3rd-party plugin manager (consult the documentation for your plugin manager).</li>
+</ul>
+<div class="markdown-heading">
+<h2 class="heading-element">Quickstart</h2>
+
+</div>
+<ol>
+<li>Install a language server, e.g. <a href="https://github.com/neovim/nvim-lspconfigdoc/configs.md#pyright" rel="noopener noreferrer">pyright</a>
+<div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight">
+<pre>npm i -g pyright</pre>
+
+</div>
+</li>
+<li>Add…</li>
+</ol>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/neovim/nvim-lspconfig" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Plugin obrigatório para quem quer utilizar o maravilhoso cliente nativo LSP. Com esse plugin a configuração dos LSP servers das linguagens fica extremamente fácil.</p>
+
+<h2>
+
+
+ #7
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/williamboman" rel="noopener noreferrer">
+ williamboman
+ </a> / <a href="https://github.com/williamboman/nvim-lsp-installer" rel="noopener noreferrer">
+ nvim-lsp-installer
+ </a>
+ </h2>
+ <h3>
+ Further development has moved to https://github.com/williamboman/mason.nvim!
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h2 class="heading-element">ℹ️ Project status</h2>
+</div>
+<blockquote>
+<p>This is an excerpt from the <a href="https://github.com/williamboman/nvim-lsp-installer/discussions/876" rel="noopener noreferrer">announcement here</a>.</p>
+</blockquote>
+<p><em><code>nvim-lsp-installer</code> is no longer maintained.</em></p>
+<p><a href="https://github.com/williamboman/mason.nvim" rel="noopener noreferrer"><code>mason.nvim</code></a> is the next generation version of <code>nvim-lsp-installer</code>. It
+builds on top of the very same foundation as <code>nvim-lsp-installer</code> (which means it's easy to migrate), but with a
+majority of internals refactored to improve extensibility and testability.</p>
+<p>More importantly, the scope of <code>mason.nvim</code> has also been widened to target more than just LSP servers. <code>mason.nvim</code>
+supports DAP servers, linters, formatters, and more. As of writing, <code>mason.nvim</code> provides 150+ packages for 100+
+languages. It can be thought of as a general-purpose package manager, native to Neovim, that runs everywhere Neovim runs
+(Windows, macOS, Linux, etc.).</p>
+<p>Another big change with <code>mason.nvim</code> is that executables are now linked to a single, shared, location, allowing seamless
+access from Neovim builtins (shell, terminal, etc.) as well as other 3rd party plugins.</p>
+<p>There have also been other improvements…</p>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/williamboman/nvim-lsp-installer" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Plugin que fornece todo suporte para uma rápida instalação dos LSP servers com uma excelente UX. É um plugin que funciona em conjunto com o <strong>nvim-lspconfig</strong></p>
+
+<h2>
+
+
+ #6
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/jose-elias-alvarez" rel="noopener noreferrer">
+ jose-elias-alvarez
+ </a> / <a href="https://github.com/jose-elias-alvarez/null-ls.nvim" rel="noopener noreferrer">
+ null-ls.nvim
+ </a>
+ </h2>
+ <h3>
+ Use Neovim as a language server to inject LSP diagnostics, code actions, and more via Lua.
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">ARCHIVED</h1>
+</div>
+<p>null-ls is now archived and will no longer receive updates. Please see
+<a href="https://github.com/jose-elias-alvarez/null-ls.nvim/issues/1621" rel="noopener noreferrer">this issue</a> for
+details.</p>
+<div class="markdown-heading">
+<h1 class="heading-element">null-ls.nvim</h1>
+</div>
+<p>Use Neovim as a language server to inject LSP diagnostics, code actions, and
+more via Lua.</p>
+<div class="markdown-heading">
+<h2 class="heading-element">Motivation</h2>
+</div>
+<p>Neovim's LSP ecosystem is growing, and plugins like
+<a href="https://github.com/nvim-telescope/telescope.nvim" rel="noopener noreferrer">telescope.nvim</a> and
+<a href="https://github.com/folke/trouble.nvim" rel="noopener noreferrer">trouble.nvim</a> make it a joy to work with
+LSP features like code actions and diagnostics.</p>
+<p>Unlike the VS Code and coc.nvim ecosystems, Neovim doesn't provide a way for
+non-LSP sources to hook into its LSP client. null-ls is an attempt to bridge
+that gap and simplify the process of creating, sharing, and setting up LSP
+sources using pure Lua.</p>
+<p>null-ls is also an attempt to reduce the boilerplate required to set up
+general-purpose language servers and improve performance by removing the need
+for external processes.</p>
+<div class="markdown-heading">
+<h2 class="heading-element">Status</h2>
+
+</div>
+<p>null-ls is in <strong>beta status</strong>. Please see below for steps to follow if something
+doesn't work the way…</p>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/jose-elias-alvarez/null-ls.nvim" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Gere diagnósticos, formate, realize ações de código e mais, utilizando esse plugin que utiliza o neovim como um servidor LSP, e faz a ponte entre ferramentas que não fazem uma conexão direta com o cliente nativo LSP. Entre os exemplos podemos citar:</p>
+
+<ul>
+<li>Formatar código ao salvar o buffer com o prettier</li>
+<li>Gerar relatório de erros e warnings com golangci-lint para Go</li>
+<li>Formatar código Lua com stylua</li>
+</ul>
+
+<p>e <a href="https://github.com/jose-elias-alvarez/null-ls.nvim/blob/main/doc/BUILTINS.md" rel="noopener noreferrer">mais</a>! </p>
+
+<h2>
+
+
+ #5
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/nvim-treesitter" rel="noopener noreferrer">
+ nvim-treesitter
+ </a> / <a href="https://github.com/nvim-treesitter/nvim-treesitter" rel="noopener noreferrer">
+ nvim-treesitter
+ </a>
+ </h2>
+ <h3>
+ Nvim Treesitter configurations and abstraction layer
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div>
+ <div class="markdown-heading">
+<h1 class="heading-element">nvim-treesitter</h1>
+</div>
+ <p>
+ <a href="https://matrix.to/#/#nvim-treesitter:matrix.org" rel="nofollow noopener noreferrer">
+ <img alt="Matrix Chat" src="https://camo.githubusercontent.com/954e3fc1a9172d1c08e4e307b5361d27a5a48d45818951fffab09bdc86fa13e9/68747470733a2f2f696d672e736869656c64732e696f2f6d61747269782f6e76696d2d747265657369747465723a6d61747269782e6f7267" />
+ </a>
+ <a href="https://github.com/nvim-treesitter/nvim-treesitter/actions?query=workflow%3A%22Linting+and+style+checking%22+branch%3Amaster" rel="noopener noreferrer">
+ <img alt="Linting and Style" src="https://github.com/nvim-treesitter/nvim-treesitter/workflows/Linting%20and%20style%20checking/badge.svg" />
+ </a>
+ <a href="https://github.com/nvim-treesitter/nvim-treesitter/actions?query=workflow%3A%22Check+loading+of+syntax+files%22+branch%3Amaster" rel="noopener noreferrer">
+ <img alt="Syntax files" src="https://github.com/nvim-treesitter/nvim-treesitter/workflows/Check%20loading%20of%20syntax%20files/badge.svg" />
+ </a>
+ </p>
+</div>
+<div>
+ <p>
+ <a rel="noopener noreferrer" href="https://github.com/nvim-treesitter/nvim-treesitterassets/logo.png"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fnvim-treesitter%2Fnvim-treesitterassets%2Flogo.png" alt="Logo" /></a>
+ </p>
+ <p>
+ <a href="https://github.com/tree-sitter/tree-sitter" rel="noopener noreferrer">Treesitter</a>
+ configurations and abstraction layer for
+ <a href="https://github.com/neovim/neovim/" rel="noopener noreferrer">Neovim</a>
+ </p>
+ <p>
+ <i>
+ Logo by <a href="https://github.com/steelsojka" rel="noopener noreferrer">@steelsojka</a>
+ </i>
+ </p>
+</div>
+<p>The goal of <code>nvim-treesitter</code> is both to provide a simple and easy way to use the interface for <a href="https://github.com/tree-sitter/tree-sitter" rel="noopener noreferrer">tree-sitter</a> in Neovim and to provide some basic functionality such as highlighting based on it:</p>
+<p><a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/2361214/202753610-e923bf4e-e88f-494b-bb1e-d22a7688446f.png"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F2361214%2F202753610-e923bf4e-e88f-494b-bb1e-d22a7688446f.png" alt="example-cpp" /></a></p>
+<p>Traditional highlighting (left) vs Treesitter-based highlighting (right)
+More examples can be found in <a href="https://github.com/nvim-treesitter/nvim-treesitter/wiki/Gallery" rel="noopener noreferrer">our gallery</a>.</p>
+<p><strong>Warning: Treesitter and nvim-treesitter highlighting are an experimental feature of Neovim.
+Please consider the experience with this plug-in as experimental until Tree-Sitter support in Neovim is stable!
+We recommend using the nightly builds of Neovim if possible.
+You can find the current roadmap <a href="https://github.com/nvim-treesitter/nvim-treesitter/issues/4767" rel="noopener noreferrer">here</a>.
+The roadmap and all features of this plugin are open to change, and any suggestion will be highly appreciated!</strong></p>
+<p>Nvim-treesitter is based on three interlocking features: <a href="https://github.com/nvim-treesitter/nvim-treesitter#language-parsers" rel="noopener noreferrer"><strong>language parsers</strong></a>, <a href="https://github.com/nvim-treesitter/nvim-treesitter#adding-queries" rel="noopener noreferrer"><strong>queries</strong></a>, and <a href="https://github.com/nvim-treesitter/nvim-treesitter#available-modules" rel="noopener noreferrer"><strong>modules</strong></a>, where <em>modules</em> provide features – e.g., highlighting – based on…</p>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/nvim-treesitter/nvim-treesitter" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Um plugin que faz interface com o excelente projeto <a href="https://github.com/tree-sitter/tree-sitter" rel="noopener noreferrer">tree-sitter</a> e traz funcionalides como _syntax highlighting _, indentação, seleção incremental, movimentação, folding, entre outros. </p>
+
+<h2>
+
+
+ #4
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/L3MON4D3" rel="noopener noreferrer">
+ L3MON4D3
+ </a> / <a href="https://github.com/L3MON4D3/LuaSnip" rel="noopener noreferrer">
+ LuaSnip
+ </a>
+ </h2>
+ <h3>
+ Snippet Engine for Neovim written in Lua.
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<p><a href="https://matrix.to/#/%23luasnip:matrix.org" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/7e4c0c2d22ff5f4cb470267e74a6e420bc201b085070d0ff43c6626e84dfb09a/68747470733a2f2f696d672e736869656c64732e696f2f6d61747269782f6c7561736e69703a6d61747269782e6f72673f6c6162656c3d4d6174726978266c6f676f3d6d6174726978" alt="LuaSnip" /></a></p>
+<div class="markdown-heading">
+<h1 class="heading-element">LuaSnip</h1>
+</div>
+
+
+
+
+ <span class="m-1">javadoc.mp4</span>
+
+
+
+
+
+
+
+<div class="markdown-heading">
+<h1 class="heading-element">Features</h1>
+
+</div>
+<ul>
+<li>Tabstops</li>
+<li>Text-Transformations using Lua functions</li>
+<li>Conditional Expansion</li>
+<li>Defining nested Snippets</li>
+<li>Filetype-specific Snippets</li>
+<li>Choices</li>
+<li>Dynamic Snippet creation</li>
+<li>Regex-Trigger</li>
+<li>Autotriggered Snippets</li>
+<li>Easy Postfix Snippets</li>
+<li>Fast</li>
+<li>Parse <a href="https://microsoft.github.io/language-server-protocol/specification#snippet_syntax" rel="nofollow noopener noreferrer">LSP-Style</a> Snippets either directly in Lua, as a VSCode package or a SnipMate snippet collection.</li>
+<li>Expand LSP-Snippets with <a href="https://github.com/hrsh7th/nvim-compe" rel="noopener noreferrer">nvim-compe</a> (or its' successor, <a href="https://github.com/hrsh7th/nvim-cmp" rel="noopener noreferrer">nvim-cmp</a> (requires <a href="https://github.com/saadparwaiz1/cmp_luasnip" rel="noopener noreferrer">cmp_luasnip</a>))</li>
+<li>Snippet history (jump back into older snippets)</li>
+<li>Resolve filetype at the cursor using Treesitter</li>
+</ul>
+<div class="markdown-heading">
+<h1 class="heading-element">Drawbacks</h1>
+
+</div>
+<ul>
+<li>Snippets that make use of the entire functionality of this plugin have to be defined in Lua (but 95% of snippets can be written in LSP-syntax).</li>
+</ul>
+<div class="markdown-heading">
+<h1 class="heading-element">Requirements</h1>
+
+</div>
+<p>Neovim >= 0.7 (extmarks)
+<code>jsregexp</code> for <code>lsp-snippet-transformations</code> (see <a href="https://github.com/L3MON4D3/LuaSnip/blob/master/DOC.md#transformations" rel="noopener noreferrer">here</a> for some tips on installing it).</p>
+<div class="markdown-heading">
+<h1 class="heading-element">Setup</h1>
+
+</div>
+<div class="markdown-heading">
+<h2 class="heading-element">Install</h2>
+
+</div>
+<ul>
+<li>
+<p>With your preferred plugin manager i.e. <a href="https://github.com/junegunn/vim-plug" rel="noopener noreferrer">vim-plug</a>, <a href="https://github.com/wbthomason/packer.nvim" rel="noopener noreferrer">Packer</a> or <a href="https://github.com/folke/lazy.nvim" rel="noopener noreferrer">lazy</a><br />
+<strong>Packer</strong>:</p>
+<div class="highlight highlight-source-lua notranslate position-relative overflow-auto js-code-highlight">
+<pre><span class="pl-c1">use</span>({
+ <span class="pl-s"><span class="pl-pds">"</span>L3MON4D3/LuaSnip<span class="pl-pds">"</span></span>
+ <span class="pl-c"><span class="pl-c">--</span> follow latest release.</span>
+ <span class="pl-smi">tag</span> <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span>v2.*<span class="pl-pds">"</span></span>, <span class="pl-c"><span class="pl-c">--</span> Replace <CurrentMajor> by the latest released major (first</span></pre>…
+</div>
+</li>
+</ul>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/L3MON4D3/LuaSnip" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>O melhor script para criação de snippets do Neovim. Simples assim. Tem uma curva de aprendizado um pouco alta, mas quando se pega o jeito, você não vai querer largar nunca. <a href="https://www.youtube.com/watch?v=Dn800rlPIho" rel="noopener noreferrer">Aqui</a> um vídeo explicando mais sobre as funcionalidades dele (Em inglês)</p>
+
+<h2>
+
+
+ #3
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/hrsh7th" rel="noopener noreferrer">
+ hrsh7th
+ </a> / <a href="https://github.com/hrsh7th/nvim-cmp" rel="noopener noreferrer">
+ nvim-cmp
+ </a>
+ </h2>
+ <h3>
+ A completion plugin for neovim coded in Lua.
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">nvim-cmp</h1>
+</div>
+<p>A completion engine plugin for neovim written in Lua
+Completion sources are installed from external repositories and "sourced".</p>
+
+
+
+
+ <span class="m-1">demo.mp4</span>
+
+
+
+
+
+
+
+
+<div class="markdown-heading">
+<h1 class="heading-element">Readme!</h1>
+
+</div>
+<ol>
+<li>There is a GitHub issue that documents <a href="https://github.com/hrsh7th/nvim-cmp/issues/231" rel="noopener noreferrer">breaking changes</a> for nvim-cmp. Subscribe to the issue to be notified of upcoming breaking changes.</li>
+<li>This is my hobby project. You can support me via GitHub sponsors.</li>
+<li>Bug reports are welcome, but don't expect a fix unless you provide minimal configuration and steps to reproduce your issue.</li>
+<li>The <code>cmp.mapping.preset.*</code> is pre-defined configuration that aims to mimic neovim's native like behavior. It can be changed without announcement. Please manage key-mapping by yourself.</li>
+</ol>
+<div class="markdown-heading">
+<h1 class="heading-element">Concept</h1>
+
+</div>
+<ul>
+<li>Full support for LSP completion related capabilities</li>
+<li>Powerful customizability via Lua functions</li>
+<li>Smart handling of key mappings</li>
+<li>No flicker</li>
+</ul>
+<div class="markdown-heading">
+<h1 class="heading-element">Setup</h1>
+
+</div>
+<div class="markdown-heading">
+<h3 class="heading-element">Recommended Configuration</h3>
+
+</div>
+<p>This example configuration uses <code>vim-plug</code> as the plugin manager and <code>vim-vsnip</code> as a snippet plugin.</p>
+<div class="highlight highlight-source-viml notranslate position-relative overflow-auto js-code-highlight">
+<pre><span class="pl-c1">call</span> <span class="pl-en">plug#begin</span>(<span class="pl-smi"><span class="pl-k">s:</span>plug_dir</span>)
+Plug <span class="pl-s"><span class="pl-pds">'</span>neovim/nvim-lspconfig<span class="pl-pds">'</span></span>
+Plug <span class="pl-s"><span class="pl-pds">'</span>hrsh7th/cmp-nvim-lsp</span></pre>…
+</div>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/hrsh7th/nvim-cmp" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Seguindo o melhor plugin para snippets, esse com certeza vai ser o melhor plugin para autocomplete que você irá ter no mercado até o momento. Seu design flexível permite incluir vários tipos diferentes de <em>sources</em>, desde sugestões de LSP, snippets, etc. A lista é <a href="https://github.com/hrsh7th/nvim-cmp/wiki/List-of-sources" rel="noopener noreferrer">gigantesca</a> e você também conta com uma excelente documentação caso queira criar os seus próprios sources também.</p>
+
+<h2>
+
+
+ #2
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/nvim-lualine" rel="noopener noreferrer">
+ nvim-lualine
+ </a> / <a href="https://github.com/nvim-lualine/lualine.nvim" rel="noopener noreferrer">
+ lualine.nvim
+ </a>
+ </h2>
+ <h3>
+ A blazing fast and easy to configure neovim statusline plugin written in pure lua.
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">lualine.nvim</h1>
+</div>
+<p><a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/6da4d97c7bbc5bccb5827dacee2c9ab833fc2f7d7e800a518b68e5304c0937dd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c616e6775616765732f636f64652d73697a652f6e76696d2d6c75616c696e652f6c75616c696e652e6e76696d3f7374796c653d666c61742d737175617265"><img src="https://camo.githubusercontent.com/6da4d97c7bbc5bccb5827dacee2c9ab833fc2f7d7e800a518b68e5304c0937dd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c616e6775616765732f636f64652d73697a652f6e76696d2d6c75616c696e652f6c75616c696e652e6e76696d3f7374796c653d666c61742d737175617265" alt="code size" /></a>
+<a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/626a72ea58ecf1972a27235993041be5014e1ef417a65b4988a1ef0a13457219/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6e76696d2d6c75616c696e652f6c75616c696e652e6e76696d3f7374796c653d666c61742d737175617265"><img src="https://camo.githubusercontent.com/626a72ea58ecf1972a27235993041be5014e1ef417a65b4988a1ef0a13457219/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6e76696d2d6c75616c696e652f6c75616c696e652e6e76696d3f7374796c653d666c61742d737175617265" alt="license" /></a>
+<a href="https://buymeacoffee.com/shadmansalj" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/ad541f86521726ee2afc61b260dcb24d3a359f0bdd4f8f7ddff53456eba73b64/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4275792532304d6525323061253230436f666665652d4637393431443f7374796c653d706c6173746963266c6f676f3d6275792d6d652d612d636f66666565266c6f676f436f6c6f723d7768697465" alt="Buy Me a Coffee" /></a></p>
+<p>A blazing fast and easy to configure Neovim statusline written in Lua.</p>
+<p><code>lualine.nvim</code> requires Neovim >= 0.7.</p>
+<p>For previous versions of neovim please use compatability tags for example
+compat-nvim-0.5</p>
+<div class="markdown-heading">
+<h2 class="heading-element">Contributing</h2>
+
+</div>
+<p>Feel free to create an issue/PR if you want to see anything else implemented.
+If you have some question or need help with configuration, start a <a href="https://github.com/nvim-lualine/lualine.nvim/discussions" rel="noopener noreferrer">discussion</a>.</p>
+<p>Please read <a href="https://github.com/nvim-lualine/lualine.nvim./CONTRIBUTING.md" rel="noopener noreferrer">CONTRIBUTING.md</a> before opening a PR.
+You can also help with documentation in the <a href="https://github.com/nvim-lualine/lualine.nvim/wiki" rel="noopener noreferrer">wiki</a>.</p>
+
+<div class="markdown-heading">
+<h2 class="heading-element">Screenshots</h2>
+
+</div>
+<p>Here is a preview of what lualine can look like.</p>
+<p>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/41551030/108650373-bb025580-74bf-11eb-8682-2c09321dd18e.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F41551030%2F108650373-bb025580-74bf-11eb-8682-2c09321dd18e.png" /></a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/41551030/108650377-bd64af80-74bf-11eb-9c55-fbfc51b39fe8.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F41551030%2F108650377-bd64af80-74bf-11eb-9c55-fbfc51b39fe8.png" /></a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/41551030/108650378-be95dc80-74bf-11eb-9718-82b242ecdd54.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F41551030%2F108650378-be95dc80-74bf-11eb-9718-82b242ecdd54.png" /></a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/41551030/108650381-bfc70980-74bf-11eb-9245-85c48f0f154a.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F41551030%2F108650381-bfc70980-74bf-11eb-9245-85c48f0f154a.png" /></a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/41551030/103467925-32372b00-4d54-11eb-88d6-6d39c46854d8.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F41551030%2F103467925-32372b00-4d54-11eb-88d6-6d39c46854d8.png" /></a>
+</p>
+
+<p>Screenshots of all available themes are listed in <a href="https://github.com/nvim-lualine/lualine.nvim./THEMES.md" rel="noopener noreferrer">THEMES.md</a></p>
+
+<p>For those who want to break the norms, you can create custom looks for lualine.</p>
+
+<p><strong>Example</strong> :</p>
+
+<ul>
+<li>
+<a href="https://github.com/nvim-lualine/lualine.nvimexamples/evil_lualine.lua" rel="noopener noreferrer">evil_lualine</a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/13149513/113875129-4453ba00-97d8-11eb-8f21-94a9ef565db3.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F13149513%2F113875129-4453ba00-97d8-11eb-8f21-94a9ef565db3.png" /></a>
+</li>
+<li>
+<a href="https://github.com/nvim-lualine/lualine.nvimexamples/slanted-gaps.lua" rel="noopener noreferrer">slanted-gaps</a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/13149513/143395518-f6d6f748-c1ca-491b-9dab-246d0a8cf23f.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F13149513%2F143395518-f6d6f748-c1ca-491b-9dab-246d0a8cf23f.png" /></a>
+</li>
+<li>
+<a href="https://github.com/nvim-lualine/lualine.nvimexamples/bubbles.lua" rel="noopener noreferrer">bubbles</a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/20235646/131350468-fc556196-5f46-4bfe-a72e-960f6a58db2c.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F20235646%2F131350468-fc556196-5f46-4bfe-a72e-960f6a58db2c.png" /></a>
+</li>
+<li>
+<a href="https://github.com/nvim-lualine/lualine.nvimexamples/cosmicink.lua" rel="noopener noreferrer">cosmicink</a>
+<a rel="noopener noreferrer" href="https://private-user-images.githubusercontent.com/125453388/423959036-c8d3e4ba-4997-42e9-a1bb-d5e2a444bbfd.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDQ4MTIxMTAsIm5iZiI6MTc0NDgxMTgxMCwicGF0aCI6Ii8xMjU0NTMzODgvNDIzOTU5MDM2LWM4ZDNlNGJhLTQ5OTctNDJlOS1hMWJiLWQ1ZTJhNDQ0YmJmZC5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwNDE2JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDQxNlQxMzU2NTBaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0wOWU3Nzg3M2NhNzQ4YzY1ZjVhNTVmNDI2MDNkMTM3ODY5MzNiZjQ2NzYyMWE0MmFjYjk3ODkwYmI3ZmEzZWM0JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.nAz4h2CN1evpZdG8RPdAhGxdROfh57jmLX71a34_ZFE"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprivate-user-images.githubusercontent.com%2F125453388%2F423959036-c8d3e4ba-4997-42e9-a1bb-d5e2a444bbfd.png%3Fjwt%3DeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDQ4MTIxMTAsIm5iZiI6MTc0NDgxMTgxMCwicGF0aCI6Ii8xMjU0NTMzODgvNDIzOTU5MDM2LWM4ZDNlNGJhLTQ5OTctNDJlOS1hMWJiLWQ1ZTJhNDQ0YmJmZC5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwNDE2JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDQxNlQxMzU2NTBaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0wOWU3Nzg3M2NhNzQ4YzY1ZjVhNTVmNDI2MDNkMTM3ODY5MzNiZjQ2NzYyMWE0MmFjYjk3ODkwYmI3ZmEzZWM0JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.nAz4h2CN1evpZdG8RPdAhGxdROfh57jmLX71a34_ZFE" /></a>
+</li>
+</ul>
+
+<div class="markdown-heading">
+<h2 class="heading-element">Performance compared to other plugins</h2>
+
+</div>
+
+<p>Unlike other statusline plugins, lualine loads only the components you specify, and nothing else.</p>
+
+<p>Startup time performance measured with an amazing plugin <a href="https://github.com/dstein64/vim-startuptime" rel="noopener noreferrer">dstein64/vim-startuptime</a></p>
+
+<p>Times are measured with a…</p>
+</div>
+
+
+</div>
+<br />
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/nvim-lualine/lualine.nvim" rel="noopener noreferrer">View on GitHub</a></div>
+<br />
+</div>
+<br />
+
+
+<p>Minha escolha para plugin de statusline. Simples, rápido e bem flexível.</p>
+
+<h2>
+
+
+ #1
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/nvim-telescope" rel="noopener noreferrer">
+ nvim-telescope
+ </a> / <a href="https://github.com/nvim-telescope/telescope.nvim" rel="noopener noreferrer">
+ telescope.nvim
+ </a>
+ </h2>
+ <h3>
+ Find, Filter, Preview, Pick. All lua, all the time.
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">telescope.nvim</h1>
+</div>
+<p><a href="https://gitter.im/nvim-telescope/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/4768045434e6ee57bdd36b11fcf554e88d207acd01e0b78a2ef3c04b2f8a7d25/68747470733a2f2f6261646765732e6769747465722e696d2f6e76696d2d74656c6573636f70652f636f6d6d756e6974792e737667" alt="Gitter" /></a>
+<a href="https://luarocks.org/modules/Conni2461/telescope.nvim" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/4b597076c327f7a8f7005e116cdd17c5e936a7d2a8f99655b31f1c81468b6fc4/68747470733a2f2f696d672e736869656c64732e696f2f6c7561726f636b732f762f436f6e6e69323436312f74656c6573636f70652e6e76696d3f6c6f676f3d6c756126636f6c6f723d707572706c65" alt="LuaRocks" /></a></p>
+<p>Gaze deeply into unknown regions using the power of the moon.</p>
+<div class="markdown-heading">
+<h2 class="heading-element">What Is Telescope?</h2>
+</div>
+<p><code>telescope.nvim</code> is a highly extendable fuzzy finder over lists. Built on the
+latest awesome features from <code>neovim</code> core. Telescope is centered around
+modularity, allowing for easy customization.</p>
+<p>Community driven builtin <a href="https://github.com/nvim-telescope/telescope.nvim#pickers" rel="noopener noreferrer">pickers</a>, <a href="https://github.com/nvim-telescope/telescope.nvim#sorters" rel="noopener noreferrer">sorters</a> and
+<a href="https://github.com/nvim-telescope/telescope.nvim#previewers" rel="noopener noreferrer">previewers</a>.</p>
+<p><a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/5eb234defa4dcc0145ba0d8e327a03869f83af8ac0def9007828e4835dfecd32/68747470733a2f2f692e696d6775722e636f6d2f5454546a6136742e676966"><img src="https://camo.githubusercontent.com/5eb234defa4dcc0145ba0d8e327a03869f83af8ac0def9007828e4835dfecd32/68747470733a2f2f692e696d6775722e636f6d2f5454546a6136742e676966" alt="Preview" /></a>
+For more showcases of Telescope, please visit the <a href="https://github.com/nvim-telescope/telescope.nvim/wiki/Showcase" rel="noopener noreferrer">Showcase
+section</a> in the
+Telescope Wiki</p>
+<div class="markdown-heading">
+<h2 class="heading-element">Telescope Table of Contents</h2>
+</div>
+<ul>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#getting-started" rel="noopener noreferrer">Getting Started</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#usage" rel="noopener noreferrer">Usage</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#customization" rel="noopener noreferrer">Customization</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#default-mappings" rel="noopener noreferrer">Default Mappings</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#pickers" rel="noopener noreferrer">Pickers</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#previewers" rel="noopener noreferrer">Previewers</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#sorters" rel="noopener noreferrer">Sorters</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#layout-display" rel="noopener noreferrer">Layout</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#themes" rel="noopener noreferrer">Themes</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#vim-commands" rel="noopener noreferrer">Commands</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#autocmds" rel="noopener noreferrer">Autocmds</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#extensions" rel="noopener noreferrer">Extensions</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#api" rel="noopener noreferrer">API</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#media" rel="noopener noreferrer">Media</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#contributing" rel="noopener noreferrer">Contributing</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim/blob/master/doc/telescope_changelog.txt" rel="noopener noreferrer">Changelog</a></li>
+</ul>
+<div class="markdown-heading">
+<h2 class="heading-element">Getting Started</h2>
+
+</div>
+<p>This section should guide you to run your first builtin pickers.</p>
+<p><a href="https://github.com/neovim/neovim/releases/tag/v0.9.0" rel="noopener noreferrer">Neovim (v0.9.0)</a> or the
+latest neovim nightly commit is required for <code>telescope.nvim</code> to work.
+The neovim version also needs to be compiled with LuaJIT, we currently do not
+support Lua5.1 because of some ongoing issues.</p>
+<div class="markdown-heading">
+<h3 class="heading-element">Required dependencies</h3>
+
+</div>
+<ul>
+<li>
+<a href="https://github.com/nvim-lua/plenary.nvim" rel="noopener noreferrer">nvim-lua/plenary.nvim</a> is required.</li>
+</ul>
+<div class="markdown-heading">
+<h3 class="heading-element">Suggested dependencies</h3>
+
+</div>
+<ul>
+<li>
+<a href="https://github.com/BurntSushi/ripgrep" rel="noopener noreferrer">BurntSushi/ripgrep</a> is required for
+<code>live_grep</code> and <code>grep_string</code> and…</li>
+</ul>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/nvim-telescope/telescope.nvim" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Talvez o plugin mais essencial de todos hoje. Faça buscas por arquivo, texto e muito mais, numa velocidade absurda, com esse plugin fantástico.</p>
+
+<h2>
+
+
+ Conclusão
+</h2>
+
+<p>Com o ecossistema Lua se desenvolvendo cada dia mais, <a href="https://github.com/topics/neovim-plugin?l=lua" rel="noopener noreferrer">muitos plugins estão surgindo todos os dias</a> e com certeza essa lista provavelmente precisará ser atualizada em um futuro próximo.</p>
+
+<p><strong>Faltou algum plugin nessa lista? Me conta ae nos comentários!</strong></p>
+
+ Ellison Leão
+ https://dev.to/ellisonleao
+
+
+ DEV Community: Ellison Leão
+ The latest articles on DEV Community by Ellison Leão (@ellisonleao).
+
+ https://dev.to/feed/ellisonleao
+ 2025-04-17T02:14:10+00:00
+
+
+
+
+ Participe da Python Brasil 2021, a maior conferência de Python da América Latina
+
+ tag:pythonclub.com.br,2021-08-02:/python-brasil-2021.html
+ 2021-08-02T18:00:00+00:00
+ <p><em>Python Brasil 2021, a maior conferência de Python da América Latina, acontecerá entre os dias 11 e 17 de outubro, reunindo pessoas desenvolvedoras, cientistas de dados e entusiastas da tecnologia.</em></p>
+<p><img alt="alt text" src="https://pythonclub.com.br/images/rafpyprog/pybr-banner.jpeg" title="Python Brasil 2021" /></p>
+<p>A <a href="https://2021.pythonbrasil.org.br/">Python Brasil 2021</a>, evento promovido pela comunidade brasileira de Python, traz nesta edição uma agenda imperdível para quem quer mergulhar no universo de uma das linguagens de programação mais utilizadas na atualidade.</p>
+<p>Uma das principais novidades deste ano é a trilha de atividades em espanhol, que vai trazer ao evento palestras de toda a América Latina, criando um ambiente de muita diversidade onde as comunidades Python de língua espanhola vão poder se conectar e trocar experiências.</p>
+<p>Em sua 17 edição, o evento será realizado de forma online, gratuito e aberto para qualquer pessoa de 11 a 17 de outubro. É esperado um público de mais de 10 mil participantes de todas as partes do Brasil e do mundo.
+Nos sete dias de imersão, os participantes poderão contribuir para projetos de software livre, participar de treinamentos e adquirir novos conhecimentos com outras pessoas da comunidade.</p>
+<p>E que tal falar sobre aquele projeto inovador, dar dicas sobre carreira ou mostrar formas de usar a tecnologia para causar impacto social, tudo isso em um ambiente inclusivo, seguro e acolhedor?! Até o dia 12 de agosto você poderá <a href="https://docs.google.com/forms/d/e/1FAIpQLSfXA7KGJbmoE6BHRgWAtK8LlBTEULv8QTS8ffHLIKUgeiLkZA/viewform">submeter sua proposta</a> para apresentação de palestras e tutoriais na maior conferência Python da América Latina.</p>
+<p>Você poderá acompanhar a Python Brasil pelo YouTube em tempo real e também participar de atividades e interagir com toda a comunidade pelo servidor do Discord criado especialmente para o evento.</p>
+<p>Não deixe de seguir o <a href="https://instagram.com/pythonbrasil?utm_medium=copy_link">perfil do evento no Instagram</a> ou <a href="https://twitter.com/pythonbrasil">Twitter</a> para ficar por dentro das últimas novidades!</p>
+
+ Rafael Alves RIbeiro
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Ciclo de Entrevistas sobre as Pesquisas no PPGCC da UFPA – Inteligência Computacional
+
+ https://blog.filipesaraiva.info/?p=2250
+ 2021-06-21T21:51:57+00:00
+ <p><img /></p>
+<p>A <a href="http://computacao.ufpa.br/">Faculdade de Computação</a> e o <a href="https://ppgcc.propesp.ufpa.br/">Programa de Pós-Graduação em Ciência da Computação</a> da <a href="https://ufpa.br/">UFPA</a> estão desenvolvendo um projeto que pretende atingir dois objetivos: o primeiro, fazer uma melhor divulgação para o público externo à universidade do que produzimos em nossas pesquisas; o segundo, uma melhor divulgação INTERNA da mesma coisa – o que desenvolvemos em nossas pesquisas.</p>
+<p>Sim, <strong>INTERNA</strong> – não bastasse de fato a comunicação deficitária e pulverizada do que fazemos para os nossos próprios alunos, a pandemia só veio a piorar esse quadro. Após mais de um ano sem contato próximo com as turmas, com aulas completamente à distância e sem maiores interações extra-classe, os alunos em geral estão perdidos sobre as possibilidades de temas para TCCs e pesquisas que eles podem realizar conosco.</p>
+<p>Dessa forma as duas subunidades estão entrevistando todos os professores para que falem um pouco sobre os temas que trabalham, o histórico de pesquisa, o que vem sendo feito e, mais interessante, o que pode vir a ser feito. As entrevistas ocorrem no canal do YouTube <a href="https://www.youtube.com/c/ComputaçãoUFPA/">Computação UFPA</a> e depois são retrabalhadas para aparecerem no <a href="https://open.spotify.com/show/7oGkFKS4eV4WM2XjbEFeJu">FacompCast</a>.</p>
+<p>Feitas as devidas introduções, nesta terça dia 22/06 às 11h eu e o amigo prof. Jefferson Morais iremos falar um pouco sobre as pesquisas em Inteligência Computacional (ou seria Artificial?) desenvolvidas por nós. Será um bom apanhado sobre os trabalhos em 4 áreas que atuamos – aprendizado de máquina, metaheurísticas, sistemas fuzzy e sistemas multiagentes -, expondo projetos atuais e novos para os interessados.</p>
+<p>Nos vemos portanto logo mais na <a href="https://www.youtube.com/watch?v=VRytpiFWTQI">sala da entrevista</a>.</p>
+<p><strong>UPDATE:</strong></p>
+<p>A gravação já está disponível, segue abaixo:</p>
+<p></p>
+
+ Filipe Saraiva
+ https://blog.filipesaraiva.info
+
+
+ planeta-python-brasil – Filipe Saraiva's blog
+ Tecnologia, sociedade e política.
+
+ https://blog.filipesaraiva.info/?feed=rss2&tag=planeta-python-brasil
+ 2025-04-17T02:14:14+00:00
+
+
+
+
+ Orientação a objetos de outra forma: Property
+
+ tag:pythonclub.com.br,2021-05-17:/oo-de-outra-forma-6.html
+ 2021-05-17T21:00:00+00:00
+ <p>Seguindo com a série, chegou a hora de discutir sobre encapsulamento, ou seja, ocultar detalhes de implementação de uma classe do resto do código. Em algumas linguagens de programação isso é feito utilizando <code>protected</code> ou <code>private</code>, e às vezes o acesso aos atributos é feito através de funções <em>getters</em> e <em>setters</em>. Nesse texto vamos ver como o Python lida com essas questões.</p>
+<h2>Métodos protegidos e privados</h2>
+<p>Diferente de linguagens como Java e PHP que possuem palavras-chave como <code>protected</code> e <code>private</code> para impedir que outras classes acessem determinados métodos ou atributos, Python deixa tudo como público. Porém isso não significa que todas as funções de uma classe podem ser chamadas por outras, ou todos os atributos podem ser lidos e alterados sem cuidados.</p>
+<p>Para que quem estiver escrevendo um código saiba quais as funções ou atributos que não deveriam ser acessados diretamente, segue-se o padrão de começá-los com <code>_</code>, de forma similar aos arquivos ocultos em sistemas UNIX, que começam com <code>.</code>. Esse padrão já foi seguido na classe <code>AutenticavelComRegistro</code> da postagem sobre <a href="https://dev.to/acaverna/orientacao-a-objetos-de-outra-forma-heranca-multiplas-e-mixins-31eb">mixins</a>, onde a função que pega a data do sistema foi nomeada <code>_get_data</code>. Entretanto isso é apenas uma sugestão, nada impede dela ser chamada, como no exemplo a baixo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
+
+
+<span class="k">class</span> <span class="nc">Exemplo</span><span class="p">:</span>
+ <span class="k">def</span> <span class="nf">_get_data</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'</span><span class="si">%d</span><span class="s1">/%m/%Y %T'</span><span class="p">)</span>
+
+
+<span class="n">obj</span> <span class="o">=</span> <span class="n">Exemplo</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">_get_data</span><span class="p">())</span>
+</pre></div>
+
+
+<p>Porém algumas bibliotecas também utilizam o <code>_</code> para indicar outras informações como metadados do objeto, e que podem ser acessados sem muitos problemas. Assim é possível utilizar esse símbolo duas vezes (<code>__</code>) para indicar que realmente essa variável ou função não deveria ser acessada de fora da classe, apresentando erro de que o atributo não foi encontrado ao tentar executar a função, porém ela ainda pode ser acessada:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
+
+
+<span class="k">class</span> <span class="nc">Exemplo</span><span class="p">:</span>
+ <span class="k">def</span> <span class="nf">__get_data</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'</span><span class="si">%d</span><span class="s1">/%m/%Y %T'</span><span class="p">)</span>
+
+
+<span class="n">obj</span> <span class="o">=</span> <span class="n">Exemplo</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">__get_data</span><span class="p">())</span> <span class="c1"># AttributeError</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">_Exemplo__get_data</span><span class="p">())</span> <span class="c1"># Executa a função</span>
+</pre></div>
+
+
+<h2>Property</h2>
+<p>Os <em>getters</em> e <em>setters</em> muitas vezes são usados para impedir que determinadas variáveis sejam alteradas, ou validar o valor antes de atribuir a variável, ou ainda processar um valor a partir de outras variáveis. Porém como o Python incentiva o acesso direto as variáveis, existe a <em>property</em>, que ao tentar acessar uma variável ou alterar um valor, uma função é chamada. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+ <span class="nd">@property</span>
+ <span class="k">def</span> <span class="nf">nome</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_nome</span>
+
+ <span class="nd">@property</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.nome}</span><span class="s1"> </span><span class="si">{self.sobrenome}</span><span class="s1">'</span>
+
+ <span class="nd">@nome_completo</span><span class="o">.</span><span class="n">setter</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="n">valor</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">' '</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_nome</span> <span class="o">=</span> <span class="n">valor</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">valor</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
+
+ <span class="nd">@property</span>
+ <span class="k">def</span> <span class="nf">idade</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_idade</span>
+
+ <span class="nd">@idade</span><span class="o">.</span><span class="n">setter</span>
+ <span class="k">def</span> <span class="nf">idade</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">valor</span> <span class="o"><</span> <span class="mi">0</span><span class="p">:</span>
+ <span class="k">raise</span> <span class="ne">ValueError</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_idade</span> <span class="o">=</span> <span class="n">valor</span>
+
+ <span class="k">def</span> <span class="nf">fazer_aniversario</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">+=</span> <span class="mi">1</span>
+</pre></div>
+
+
+<p>Nesse código algumas variáveis são acessíveis através de <em>properties</em>, de forma geral, as variáveis foram definidas começando com <code>_</code> e com uma <em>property</em> de mesmo nome (sem o <code>_</code>). O primeiro caso é o <code>nome</code>, que possui apenas o <em>getter</em>, sendo possível o seu acesso como <code>obj.nome</code>, porém ao tentar atribuir um valor, será lançado um erro (<code>AttributeError: can't set attribute</code>). Em relação ao <code>sobrenome</code>, como não é necessário nenhum tratamento especial, não foi utilizado um <em>property</em>, porém futuramente pode ser facilmente substituído por um sem precisar alterar os demais códigos. Porém a função <code>nome_completo</code> foi substituída por um <em>property</em>, permitindo tanto o acesso ao nome completo da pessoa, como se fosse uma variável, quanto trocar <code>nome</code> e <code>sobrenome</code> ao atribuir um novo valor para essa <em>property</em>. Quanto a <code>idade</code> utiliza o <em>setter</em> do <em>property</em> para validar o valor recebido, retornando erro para idades inválidas (negativas).</p>
+<p>Vale observar também que todas as funções de <em>getter</em> não recebem nenhum argumento (além do <code>self</code>), enquanto as funções de <em>setter</em> recebem o valor atribuído à variável.</p>
+<p>Utilizando a <a href="https://dev.to/acaverna/orientacao-a-objetos-de-outra-forma-abc-89b">ABC</a>, ainda é possível informar que alguma classe filha deverá implementar alguma <em>property</em>. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span>
+
+
+<span class="k">class</span> <span class="nc">Pessoa</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+ <span class="nd">@property</span>
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="k">class</span> <span class="nc">Brasileiro</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="nd">@property</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.nome}</span><span class="s1"> </span><span class="si">{self.sobrenome}</span><span class="s1">'</span>
+
+
+<span class="k">class</span> <span class="nc">Japones</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="nd">@property</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.sobrenome}</span><span class="s1"> </span><span class="si">{self.nome}</span><span class="s1">'</span>
+</pre></div>
+
+
+<h2>Considerações</h2>
+<p>Diferente de algumas linguagens que ocultam as variáveis dos objetos, permitindo o seu acesso apenas através de funções, Python seguem no sentido contrário, acessando as funções de <em>getter</em> e <em>setter</em> como se fossem variáveis, isso permite começar com uma classe simples e ir adicionando funcionalidades conforme elas forem necessárias, sem precisar mudar o código das demais partes da aplicação, além de deixar transparente para quem desenvolve, não sendo necessário lembrar se precisa usar <em>getteres</em> e <em>setteres</em> ou não.</p>
+<p>De forma geral, programação orientada a objetos consiste em seguir determinados padrões de código, e as linguagens que implementam esse paradigma oferecem facilidades para escrever código seguindo esses padrões, e às vezes até ocultando detalhes complexos de suas implementações. Nesse contexto, eu recomendo a palestra do autor do <a href="https://htop.dev/">htop</a> feita no FISL 16, onde ele comenta como usou <a href="http://hemingway.softwarelivre.org/fisl16/high/41f/sala_41f-high-201507091200.ogv">orientação a objetos em C</a>. E para quem ainda quiser se aprofundar no assunto de orientação a objetos no Python, recomendo os vídeos do <a href="https://www.youtube.com/playlist?list=PLOQgLBuj2-3L_L6ahsBVA_SzuGtKre3OK">Eduardo Mendes</a> (também conhecido como dunossauro).</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+
+ Eduardo Klosowski
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Iniciando com o ORM Pony no Python III - Erros e Exceções
+
+ https://paulohrpinheiro.xyz/texts/python/2021-05-16-iniciando-com-o-orm-pony-no-python-iii-erros-e-excecoes.html
+ 2021-05-16T00:00:00+00:00
+ Seguindo o trilha com o ORM Pony, neste terceiro texto, agora começaremos a gerar situações de erro e ver o que os objetos retornam ou que exceções são geradas.
+
+ Blog do PauloHRPinheiro
+ https://paulohrpinheiro.xyz
+
+
+ Paulo Henrique Rodrigues Pinheiro
+ Blog sobre programação para programadores
+
+ https://paulohrpinheiro.xyz/texts/python/feed.xml
+ 2025-04-17T02:14:01+00:00
+
+
+
+
+ Orientação a objetos de outra forma: ABC
+
+ tag:pythonclub.com.br,2021-05-10:/oo-de-outra-forma-5.html
+ 2021-05-10T15:00:00+00:00
+ <p>Na discussão sobre <a href="https://dev.to/acaverna/orientacao-a-objetos-de-outra-forma-heranca-multiplas-e-mixins-31eb">herança e mixins</a> foram criadas várias classes, como <code>Autenticavel</code> e <code>AutenticavelComRegistro</code> que adicionam funcionalidades a outras classes e implementavam tudo o que precisavam para seu funcionamento. Entretanto podem existir casos em que não seja possível implementar todas as funções na própria classe, deixando com que as classes que a estende implemente essas funções. Uma forma de fazer isso é través das <a href="https://docs.python.org/pt-br/3/library/abc.html">ABC</a> (<em>abstract base classes</em>, ou classes base abstratas).</p>
+<h2>Sem uso de classes base abstratas</h2>
+<p>Um exemplo de classe que não é possível implementar todas as funcionalidades foi dada no texto <a href="https://dev.to/acaverna/encapsulamento-da-logica-do-algoritmo-298e">Encapsulamento da lógica do algoritmo</a>, que discutia a leitura de valores do teclado até que um valor válido fosse lido (ou que repete a leitura caso um valor inválido tivesse sido informado). Nesse caso a classe <code>ValidaInput</code> implementava a lógica base de funcionamento, porém eram suas classes filhas (<code>ValidaNomeInput</code> e <code>ValidaNotaInput</code>) que implementavam as funções para tratar o que foi lido do teclado e verificar se é um valor válido ou não.</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ValidaInput</span><span class="p">:</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Valor inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">raise</span> <span class="ne">NotImplementedError</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">raise</span> <span class="ne">NotImplementedError</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">valor</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNomeInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nome inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">entrada</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">title</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">valor</span> <span class="o">!=</span> <span class="s1">''</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNotaInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nota inválida!'</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">entrada</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">valor</span> <span class="o"><=</span> <span class="mi">10</span>
+</pre></div>
+
+
+<p>Entretanto, esse código permite a criação de objetos da classe <code>ValidaInput</code> mesmo sem ter uma implementação das funções <code>transformar_entrada</code> e <code>validar_valor</code>. E a única mensagem de erro ocorreria ao tentar executar essas funções, o que poderia estar longe do problema real, que é a criação de um objeto a partir de uma classe que não prove todas as implementações das suas funções, o que seria semelhante a uma classe abstrata em outras linguagens.</p>
+<div class="highlight"><pre><span></span><span class="n">obj</span> <span class="o">=</span> <span class="n">ValidaInput</span><span class="p">()</span>
+
+<span class="c1"># Diversas linhas de código</span>
+
+<span class="n">obj</span><span class="p">(</span><span class="s1">'Entrada: '</span><span class="p">)</span> <span class="c1"># Exceção NotImplementedError lançada</span>
+</pre></div>
+
+
+<h2>Com uso de classes base abstratas</h2>
+<p>Seguindo a documentação da <a href="https://docs.python.org/pt-br/3/library/abc.html">ABC</a>, para utilizá-las é necessário informar a metaclasse <code>ABCMeta</code> na criação da classe, ou simplesmente estender a classe <code>ABC</code>, e decorar com <code>abstractmethod</code> as funções que as classes que a estenderem deverão implementar. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaInput</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Valor inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="o">...</span>
+
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="o">...</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">valor</span>
+</pre></div>
+
+
+<p>Desta forma, ocorrerá um erro já ao tentar criar um objeto do tipo <code>ValidaInput</code>, dizendo quais são as funções que precisam ser implementadas. Porém funcionará normalmente ao criar objetos a partir das classes <code>ValidaNomeInput</code> e <code>ValidaNotaInput</code> visto que elas implementam essas funções.</p>
+<div class="highlight"><pre><span></span><span class="n">obj</span> <span class="o">=</span> <span class="n">ValidaInput</span><span class="p">()</span> <span class="c1"># Exceção TypeError lançada</span>
+
+<span class="n">nome_input</span> <span class="o">=</span> <span class="n">ValidaNomeInput</span><span class="p">()</span> <span class="c1"># Objeto criado</span>
+<span class="n">nota_input</span> <span class="o">=</span> <span class="n">ValidaNotaInput</span><span class="p">()</span> <span class="c1"># Objeto criado</span>
+</pre></div>
+
+
+<p>Como essas funções não utilizam a referência ao objeto (<code>self</code>), ainda é possível decorar as funções com <code>staticmethod</code>, como:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaInput</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Valor inválido!'</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="n">entrada</span><span class="p">):</span>
+ <span class="o">...</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="o">...</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">valor</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNomeInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nome inválido!'</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">entrada</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">title</span><span class="p">()</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">valor</span> <span class="o">!=</span> <span class="s1">''</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNotaInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nota inválida!'</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">entrada</span><span class="p">)</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">valor</span> <span class="o"><=</span> <span class="mi">10</span>
+</pre></div>
+
+
+<p>Isso também seria válido para funções decoradas com <code>classmethod</code>, que receberiam a referência a classe (<code>cls</code>).</p>
+<h2>Considerações</h2>
+<p>Não é necessário utilizar ABC para fazer o exemplo discutido, porém ao utilizar essa biblioteca ficou mais explícito quais as funções que precisavam ser implementados nas classes filhas, ainda mais que sem utilizar ABC a classe base poderia nem ter as funções, com:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ValidaInput</span><span class="p">:</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Valor inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">valor</span>
+</pre></div>
+
+
+<p>Como Python possui <a href="https://docs.python.org/pt-br/3/glossary.html#term-duck-typing">duck-typing</a>, não é necessário uma grande preocupação com os tipos, como definir e utilizar interfaces presentes em outras implementações de orientação a objetos, porém devido à herança múltipla, ABC pode ser utilizada como interface que não existe em Python, fazendo com que as classes implementem determinadas funções. Para mais a respeito desse assunto, recomendo as duas lives do dunossauro sobre ABC (<a href="https://www.youtube.com/watch?v=yLHV1__nZZw">1</a> e <a href="https://www.youtube.com/watch?v=erAXvsuihPQ">2</a>), e a apresentação do Luciano Ramalho sobre <a href="https://www.youtube.com/watch?v=AJK2LqrlnTE">type hints</a>.</p>
+<p>Uma classe filha também não é obrigada a implementar todas as funções decoradas com <code>abstractmethod</code>, mas assim como a classe pai, não será possível criar objetos a partir dessa classe, apenas de uma classe filha dela que implemente as demais funções. Como se ao aplicar um <code>abstractmethod</code> tornasse a classe abstrata, e qualquer classe filha só deixasse de ser abstrata quando a última função decorada com <code>abstractmethod</code> for sobrescrita. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
+
+
+<span class="k">class</span> <span class="nc">A</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">func1</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="o">...</span>
+
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">func2</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="k">class</span> <span class="nc">B</span><span class="p">(</span><span class="n">A</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">func1</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'1'</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">C</span><span class="p">(</span><span class="n">B</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">func2</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'2'</span><span class="p">)</span>
+
+
+<span class="n">a</span> <span class="o">=</span> <span class="n">A</span><span class="p">()</span> <span class="c1"># Erro por não implementar func1 e func2</span>
+<span class="n">b</span> <span class="o">=</span> <span class="n">B</span><span class="p">()</span> <span class="c1"># Erro por não implementar func2</span>
+<span class="n">c</span> <span class="o">=</span> <span class="n">C</span><span class="p">()</span> <span class="c1"># Objeto criado</span>
+</pre></div>
+
+
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+
+ Eduardo Klosowski
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Orientação a objetos de outra forma: Herança múltiplas e mixins
+
+ tag:pythonclub.com.br,2021-05-03:/oo-de-outra-forma-4.html
+ 2021-05-03T18:00:00+00:00
+ <p>No <a href="https://dev.to/acaverna/orientacao-a-objetos-de-outra-forma-heranca-3dm7">texto anterior</a> foi apresentando o conceito de herança, que herda toda a estrutura e comportamento de uma classe, podendo estendê-la com outros atributos e comportamentos. Esse texto apresentará a ideia de <a href="https://pt.wikipedia.org/wiki/Heran%C3%A7a_m%C3%BAltipla">herança múltipla</a>, e uma forma para se aproveitar esse recurso, através de mixins.</p>
+<h2>Herança múltiplas</h2>
+<p>Voltando ao sistema para lidar com dados das pessoas, onde algumas dessas pessoas possuem a possibilidade de acessar o sistema através de usuário e senha, também deseja-se permitir que outros sistemas autentiquem e tenham acesso os dados através de uma <a href="https://pt.wikipedia.org/wiki/Interface_de_programa%C3%A7%C3%A3o_de_aplica%C3%A7%C3%B5es">API</a>. Isso pode ser feito criando uma classe para representar os sistemas que terão permissão para acessar os dados. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Sistema</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">=</span> <span class="n">senha</span>
+
+ <span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">==</span> <span class="n">usuario</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">==</span> <span class="n">senha</span>
+</pre></div>
+
+
+<p>Porém, esse código repete a implementação feita para <code>PessoaAutenticavel</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">PessoaAutenticavel</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">=</span> <span class="n">senha</span>
+
+ <span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">==</span> <span class="n">usuario</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">==</span> <span class="n">senha</span>
+</pre></div>
+
+
+<p>Aproveitando que Python, diferente de outras linguagens, possui herança múltipla, é possível extrair essa lógica das classes, centralizando a implementação em uma outra classe e simplesmente herdá-la. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Autenticavel</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">=</span> <span class="n">senha</span>
+
+ <span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">==</span> <span class="n">usuario</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">==</span> <span class="n">senha</span>
+
+
+<span class="k">class</span> <span class="nc">PessoaAutenticavel</span><span class="p">(</span><span class="n">Autenticavel</span><span class="p">,</span> <span class="n">Pessoa</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="k">class</span> <span class="nc">Sistema</span><span class="p">(</span><span class="n">Autenticavel</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="n">p</span> <span class="o">=</span> <span class="n">PessoaAutenticavel</span><span class="p">(</span><span class="n">nome</span><span class="o">=</span><span class="s1">'João'</span><span class="p">,</span> <span class="n">sobrenome</span><span class="o">=</span><span class="s1">'da Silva'</span><span class="p">,</span> <span class="n">idade</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span>
+ <span class="n">usuario</span><span class="o">=</span><span class="s1">'joao'</span><span class="p">,</span> <span class="n">senha</span><span class="o">=</span><span class="s1">'secreta'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>A primeira coisa a ser observada são os argumentos <code>*args</code> e <code>**kwargs</code> no <code>__init__</code> da classe <code>Autenticavel</code>, eles são usados uma vez que não se sabe todos os argumentos que o <code>__init__</code> da classe que estenderá o <code>Autenticavel</code> espera receber, funcionando de forma dinâmica (mais sobre esse recurso pode ser visto na <a href="https://docs.python.org/pt-br/3/tutorial/controlflow.html#more-on-defining-functions">documentação do Python</a>).</p>
+<p>A segunda coisa a ser verificada é que para a classe <code>PessoaAutenticavel</code>, agora cria em seus objetos, a estrutura tanto da classe <code>Pessoa</code>, quanto <code>Autenticavel</code>. Algo similar a versão sem orientação a objetos a baixo:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa_autenticavel.py</span>
+
+<span class="kn">import</span> <span class="nn">autenticavel</span>
+<span class="kn">import</span> <span class="nn">pessoa</span>
+
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="n">autenticavel</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Também vale observar que as classes <code>PessoaAutenticavel</code> e <code>Sistema</code> não precisam definir nenhuma função, uma vez que elas cumprem seus papéis apenas herdando outras classes, porém seria possível implementar funções específicas dessas classes, assim como sobrescrever as funções definidas por outras classes.</p>
+<h2>Ordem de resolução de métodos</h2>
+<p>Embora herança múltiplas sejam interessantes, existe um problema, se ambas as classes pai possuírem uma função com um mesmo nome, a classe filha deveria chamar qual das funções? A do primeiro pai? A do último? Para lidar com esse problema o Python usa o MRO (<em>method resolution order</em>, ordem de resolução do método), que consiste em uma tupla com a ordem de qual classe o Python usará para encontrar o método a ser chamado. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="n">PessoaAutenticavel</span><span class="o">.</span><span class="vm">__mro__</span><span class="p">)</span>
+<span class="c1"># (<class '__main__.PessoaAutenticavel'>, <class '__main__.Autenticavel'>, <class '__main__.Pessoa'>, <class 'object'>)</span>
+</pre></div>
+
+
+<p>Por esse motivo que também foi possível chamar o <code>super().__init__</code> dentro de <code>Autenticavel</code>, que devido ao MRO, o Python chama o <code>__init__</code> da outra classe pai da classe que estendeu <code>Autenticavel</code>, em vez de precisar fazer um método <code>__init__</code> em <code>PessoaAutenticavel</code> chamando o <code>__init__</code> de todas as suas classes pais, como foi feito na versão sem orientação a objetos. E por isso a ordem <code>Autenticavel</code> e <code>Pessoa</code> na herança de <code>PessoaAutenticavel</code>, para fazer o MRO procurar os métodos primeiro em <code>Autenticavel</code> e depois em <code>Pessoa</code>.</p>
+<p>Para tentar fugir da complexidade que pode ser herança múltipla, é possível escrever classes que tem por objetivo unicamente incluir alguma funcionalidade em outra, como o caso da classe <code>Autenticavel</code>, que pode ser herdada por qualquer outra classe do sistema para permitir o acesso ao sistema. Essas classes recebem o nome de mixins, e adiciona uma funcionalidade bem definida.</p>
+<h2>Estendendo mixins</h2>
+<p>Imagine se além de permitir o acesso ao sistema, também gostaríamos de registrar algumas tentativas de acesso, informando quando houve a tentativa e se o acesso foi concedido ou não. Como <code>Autenticavel</code> é uma classe, é possível extendê-la para implementar essa funcionalidade na função <code>autenticar</code>. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
+
+
+<span class="k">class</span> <span class="nc">AutenticavelComRegistro</span><span class="p">(</span><span class="n">Autenticavel</span><span class="p">):</span>
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">_get_data</span><span class="p">():</span>
+ <span class="k">return</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'</span><span class="si">%d</span><span class="s1">/%m/%Y %T'</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'{self._get_data()} Tentativa de acesso de </span><span class="si">{usuario}</span><span class="s1">'</span><span class="p">)</span>
+ <span class="n">acesso</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">acesso</span><span class="p">:</span>
+ <span class="n">acesso_str</span> <span class="o">=</span> <span class="s1">'permitido'</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="n">acesso_str</span> <span class="o">=</span> <span class="s1">'negado'</span>
+ <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'{self._get_data()} Acesso de </span><span class="si">{usuario}</span><span class="s1"> </span><span class="si">{acesso_str}</span><span class="s1">'</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">acesso</span>
+
+
+<span class="k">class</span> <span class="nc">PessoaAutenticavelComRegistro</span><span class="p">(</span><span class="n">AutenticavelComRegistro</span><span class="p">,</span> <span class="n">Pessoa</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="k">class</span> <span class="nc">SistemaAutenticavelComRegistro</span><span class="p">(</span><span class="n">AutenticavelComRegistro</span><span class="p">,</span> <span class="n">Sistema</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="n">p</span> <span class="o">=</span> <span class="n">PessoaAutenticavelComRegistro</span><span class="p">(</span>
+ <span class="n">nome</span><span class="o">=</span><span class="s1">'João'</span><span class="p">,</span> <span class="n">sobrenome</span><span class="o">=</span><span class="s1">'da Silva'</span><span class="p">,</span> <span class="n">idade</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span>
+ <span class="n">usuario</span><span class="o">=</span><span class="s1">'joao'</span><span class="p">,</span> <span class="n">senha</span><span class="o">=</span><span class="s1">'secreta'</span><span class="p">,</span>
+<span class="p">)</span>
+<span class="n">p</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">)</span>
+<span class="c1"># Saída na tela:</span>
+<span class="c1"># 23/04/2021 16:56:58 Tentativa de acesso de joao</span>
+<span class="c1"># 23/04/2021 16:56:58 Acesso de joao permitido</span>
+</pre></div>
+
+
+<p>Essa implementação utiliza-se do <code>super()</code> para acessar a função <code>autenticar</code> da classe <code>Autenticavel</code> para não precisar reimplementar a autenticação. Porém, antes de chamá-la, manipula seus argumentos para registrar quem tentou acessar o sistema, assim como também manipula o seu retorno para registrar se o acesso foi permitido ou não.</p>
+<p>Essa classe também permite analisar melhor a ordem em que as classes são consultadas quando uma função é chamada:</p>
+<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="n">PessoaAutenticavelComRegistro</span><span class="o">.</span><span class="vm">__mro__</span><span class="p">)</span>
+<span class="c1"># (<class '__main__.PessoaAutenticavelComRegistro'>, <class '__main__.AutenticavelComRegistro'>, <class '__main__.Autenticavel'>, <class '__main__.Pessoa'>, <class 'object'>)</span>
+</pre></div>
+
+
+<p>Que também pode ser visto na forma de um digrama de classes:</p>
+<p><img alt="Diagrama de classes" src="https://pythonclub.com.br/feeds/images/eduardoklosowski/oo-de-outra-forma-4/mro.png" /></p>
+<p>Onde é feito uma <a href="https://pt.wikipedia.org/wiki/Busca_em_profundidade">busca em profundidade</a>, como se a função fosse chamada no primeiro pai, e só se ela não for encontrada, busca-se no segundo pai e assim por diante. Também é possível observar a classe <code>object</code>, que sempre será a última classe, e é a classe pai de todas as outras classes do Python quando elas não possuirem um pai declarado explicitamente.</p>
+<h2>Considerações</h2>
+<p>Herança múltipla pode dificultar bastante o entendimento do código, principalmente para encontrar onde determinada função está definida, porém pode facilitar bastante o código. Um exemplo que usa bastante herança e mixins são as <em>views</em> baseadas em classe do django (<a href="https://docs.djangoproject.com/pt-br/3.2/topics/class-based-views/"><em>class-based views</em></a>), porém para facilitar a visualização existe o site <a href="https://ccbv.co.uk/">Classy Class-Based Views</a> que lista todas as classes, e os mixins utilizados em cada uma, como pode ser visto em "Ancestors" como na <a href="https://ccbv.co.uk/projects/Django/3.1/django.views.generic.edit/UpdateView/">UpdateView</a>, que é usado para criar uma página com formulário para editar um registro já existente no banco, assim ela usa mixins para pegar um objeto do banco (<code>SingleObjectMixin</code>), processar formulário baseado em uma tabela do banco (<code>ModelFormMixin</code>) e algumas outras funcionalidades necessárias para implementar essa página.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+
+ Eduardo Klosowski
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Orientação a objetos de outra forma: Herança
+
+ tag:pythonclub.com.br,2021-04-26:/oo-de-outra-forma-3.html
+ 2021-04-26T20:00:00+00:00
+ <p>Algo que ajuda no desenvolvimento é a reutilização de código. Em orientação a objetos, essa reutilização pode ocorrer através de herança, onde um objeto pode se comportar como um objeto da sua própria classe, como também da classe que herdou.</p>
+<h2>Adicionando funcionalidades</h2>
+<p>Uma das utilidades da herança é estender uma classe para adicionar funcionalidades. Pensando no contexto das postagens anteriores, poderíamos querer criar um usuário e senha para algumas pessoas poderem acessar o sistema. Isso poderia ser feito adicionando atributos usuário e senha para as pessoas, além de uma função para validar se os dados estão corretos, e assim permitir o acesso ao sistema. Porém isso não pode ser feito para todas as pessoas, e sim apenas para aqueles que possuem permissão de acesso.</p>
+<h3>Sem orientação a objetos</h3>
+<p>Voltando a solução com dicionários (sem utilizar orientação a objetos), isso consistiria em criar um dicionário com a estrutura de uma pessoa, e em seguida estender essa estrutura com os novos campos de usuário e senha nesse mesmo dicionário, algo como:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa.py</span>
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'nome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'sobrenome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'idade'</span><span class="p">]</span> <span class="o">=</span> <span class="n">idade</span>
+
+
+<span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="n">pessoa</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{pessoa['nome']}</span><span class="s2"> </span><span class="si">{pessoa['sobrenome']}</span><span class="s2">"</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa_autenticavel.py</span>
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'usuario'</span><span class="p">]</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'senha'</span><span class="p">]</span> <span class="o">=</span> <span class="n">senha</span>
+
+
+<span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">pessoa</span><span class="p">[</span><span class="s1">'usuario'</span><span class="p">]</span> <span class="o">==</span> <span class="n">usuario</span> <span class="ow">and</span> <span class="n">pessoa</span><span class="p">[</span><span class="s1">'senha'</span><span class="p">]</span> <span class="o">==</span> <span class="n">senha</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+<span class="kn">import</span> <span class="nn">pessoa_autenticavel</span>
+
+<span class="n">p</span> <span class="o">=</span> <span class="p">{}</span>
+<span class="n">pessoa</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">pessoa_autenticavel</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">pessoa</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">(</span><span class="n">p</span><span class="p">))</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">pessoa_autenticavel</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">))</span>
+</pre></div>
+
+
+<p>Porém nessa solução é possível que o programador esqueça de chamar as duas funções <code>init</code> diferentes, e como queremos que todo dicionário com a estrutura de <code>pessoa_autenticavel</code> contenha também a estrutura de <code>pessoa</code>, podemos chamar o <code>init</code> de pessoa dentro do <code>init</code> de <code>pessoa_autenticavel</code>:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa_autenticavel.py</span>
+
+<span class="kn">import</span> <span class="nn">pessoa</span>
+
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="n">p</span><span class="p">[</span><span class="s1">'usuario'</span><span class="p">]</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="n">p</span><span class="p">[</span><span class="s1">'senha'</span><span class="p">]</span> <span class="o">=</span> <span class="n">senha</span>
+
+
+<span class="o">...</span> <span class="c1"># Demais funções</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+<span class="kn">import</span> <span class="nn">pessoa_autenticavel</span>
+
+<span class="n">p</span> <span class="o">=</span> <span class="p">{}</span>
+<span class="n">pessoa_autenticavel</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">pessoa</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">(</span><span class="n">p</span><span class="p">))</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">pessoa_autenticavel</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">))</span>
+</pre></div>
+
+
+<p>Nesse caso foi necessário alterar o nome do argumento <code>pessoa</code> da função <code>pessoa_autenticavel.init</code> para não conflitar com o outro módulo importado com esse mesmo nome. Porém ao chamar um <code>init</code> dentro de outro, temos a garantia de que o dicionário será compatível tanto com a estrutura pedida para ser criada pelo programador, quanto pelas estruturas pais dela.</p>
+<h3>Com orientação a objetos</h3>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.nome}</span><span class="s1"> </span><span class="si">{self.sobrenome}</span><span class="s1">'</span>
+
+
+<span class="k">class</span> <span class="nc">PessoaAutenticavel</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="n">Pessoa</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">=</span> <span class="n">senha</span>
+
+ <span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">==</span> <span class="n">usuario</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">==</span> <span class="n">senha</span>
+
+
+<span class="n">p</span> <span class="o">=</span> <span class="n">PessoaAutenticavel</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">Pessoa</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">(</span><span class="n">p</span><span class="p">))</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">PessoaAutenticavel</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">))</span>
+</pre></div>
+
+
+<p>A principal novidade desse exemplo é que ao declarar a classe <code>PessoaAutenticavel</code> (filha), foi declarado a classe <code>Pessoa</code> (pai) entre parênteses, isso faz o interpretador Python criar uma cópia dessa classe estendendo-a com as novas funções que estamos criando. Porém pode ser um pouco redundante chamar <code>Pessoa.__init__</code> dentro da função <code>__init__</code> sendo que já foi declarado que ela estende <code>Pessoa</code>, podendo ser trocado por <code>super()</code>, que aponta para a classe que foi estendida. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">PessoaAutenticavel</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">=</span> <span class="n">senha</span>
+
+ <span class="o">...</span> <span class="c1"># Demais funções</span>
+</pre></div>
+
+
+<p>Assim se evita repetir o nome da classe, e já passa automaticamente a referência para <code>self</code>, assim como quando usamos o açúcar sintático apresentado na primeira postagem dessa série. E esse açúcar sintática também pode ser usado para chamar tanto as funções declaradas em <code>Pessoa</code> quanto em <code>PessoaAutenticavel</code>. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">p</span> <span class="o">=</span> <span class="n">PessoaAutenticavel</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">())</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">))</span>
+</pre></div>
+
+
+<p>Esse método também facilita a utilização das funções, uma vez que não é necessário lembrar em qual classe que cada função foi declarada. Na verdade, como <code>PessoaAutenticavel</code> estende <code>Pessoa</code>, seria possível executar também <code>PessoaAutenticavel.nome_completo</code>, porém eles apontam para a mesma função.</p>
+<h2>Sobrescrevendo uma função</h2>
+<p>A classe <code>Pessoa</code> possui a função <code>nome_completo</code> que retorna uma <code>str</code> contento nome e sobrenome. Porém no Japão, assim como em outros países asiáticos, o sobrenome vem primeiro, e até <a href="https://noticias.uol.com.br/ultimas-noticias/efe/2019/06/24/japao-quer-voltar-a-ordem-tradicional-dos-nomes-abe-shinzo-nao-shinzo-abe.htm">estão pedindo para seguir a tradição deles ao falarem os nomes de japoneses</a>, como o caso do primeiro-ministro, mudando de Shinzo Abe para Abe Shinzo.</p>
+<h3>Com orientação a objetos</h3>
+<p>Isso também pode ser feito no sistema usando herança, porém em vez de criar uma nova função com outro nome, é possível criar uma função com o mesmo nome, sobrescrevendo a anterior, porém apenas para os objetos da classe filha. Algo semelhante ao que já foi feito com a função <code>__init__</code>. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Japones</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.sobrenome}</span><span class="s1"> </span><span class="si">{self.nome}</span><span class="s1">'</span>
+
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">p2</span> <span class="o">=</span> <span class="n">Japones</span><span class="p">(</span><span class="s1">'Shinzo'</span><span class="p">,</span> <span class="s1">'Abe'</span><span class="p">,</span> <span class="mi">66</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">())</span> <span class="c1"># João da Silva</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p2</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">())</span> <span class="c1"># Abe Shinzo</span>
+</pre></div>
+
+
+<p>Essa relação de herança traz algo interessante, todo objeto da classe <code>Japones</code> se comporta como um objeto da classe <code>Pessoa</code>, porém a relação inversa não é verdade. Assim como podemos dizer que todo japonês é uma pessoa, mas nem todas as pessoas são japonesas. Ser japonês é um caso mais específico de pessoa, assim como as demais nacionalidades.</p>
+<h3>Sem orientação a objetos</h3>
+<p>Esse comportamento de sobrescrever a função <code>nome_completo</code> não é tão simples de replicar em uma estrutura de dicionário, porém é possível fazer. Porém como uma pessoa pode ser tanto japonês quanto não ser, não é possível saber de antemão para escrever no código <code>pessoa.nome_completo</code> ou <code>japones.nome_completo</code>, que diferente do exemplo da autenticação, agora são duas funções diferentes, isso precisa ser descoberto dinamicamente quando se precisar chamar a função.</p>
+<p>Uma forma de fazer isso é guardar uma referência para a função que deve ser chamada dentro da própria estrutura. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa.py</span>
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'nome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'sobrenome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'idade'</span><span class="p">]</span> <span class="o">=</span> <span class="n">idade</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'nome_completo'</span><span class="p">]</span> <span class="o">=</span> <span class="n">nome_completo</span>
+
+
+<span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="n">pessoa</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{pessoa['nome']}</span><span class="s2"> </span><span class="si">{pessoa['sobrenome']}</span><span class="s2">"</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: japones.py</span>
+
+<span class="kn">import</span> <span class="nn">pessoa</span>
+
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">japones</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">(</span><span class="n">japones</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="n">japones</span><span class="p">[</span><span class="s1">'nome_completo'</span><span class="p">]</span> <span class="o">=</span> <span class="n">nome_completo</span>
+
+
+<span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="n">japones</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{pessoa['sobrenome']}</span><span class="s2"> </span><span class="si">{pessoa['nome']}</span><span class="s2">"</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+<span class="kn">import</span> <span class="nn">japones</span>
+
+<span class="n">p1</span> <span class="o">=</span> <span class="p">{}</span>
+<span class="n">pessoa</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">p2</span> <span class="o">=</span> <span class="p">{}</span>
+<span class="n">japones</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p2</span><span class="p">,</span> <span class="s1">'Shinzo'</span><span class="p">,</span> <span class="s1">'Abe'</span><span class="p">,</span> <span class="mi">66</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="p">[</span><span class="s1">'nome_completo'</span><span class="p">](</span><span class="n">p1</span><span class="p">))</span> <span class="c1"># João da Silva</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p2</span><span class="p">[</span><span class="s1">'nome_completo'</span><span class="p">](</span><span class="n">p2</span><span class="p">))</span> <span class="c1"># Abe Shinzo</span>
+</pre></div>
+
+
+<p>Perceba que a forma de chamar a função foi alterada. O que acontece na prática é que toda função que pode ser sobrescrita não é chamada diretamente, e sim a partir de uma referência, e isso gera um custo computacional adicional. Como esse custo não é tão alto (muitas vezes sendo quase irrelevante), esse é o comportamento adotado em várias linguagens, porém em C++, por exemplo, existe a palavra-chave <code>virtual</code> para descrever quando uma função pode ser sobrescrita ou não.</p>
+<h2>Considerações</h2>
+<p>Herança é um mecanismo interessante para ser explorado com o objetivo de reaproveitar código e evitar repeti-lo. Porém isso pode vir com alguns custos, seja computacional durante sua execução, seja durante a leitura do código, sendo necessário verificar diversas classes para saber o que de fato está sendo executado, porém isso também pode ser usado para ocultar e abstrair lógicas mais complicadas, como eu já comentei em outra <a href="https://dev.to/acaverna/encapsulamento-da-logica-do-algoritmo-298e">postagem</a>.</p>
+<p>Herança também permite trabalhar com generalização e especialização, podendo descrever o comportamento mais geral, ou mais específico. Ou simplesmente só adicionar mais funcionalidades a uma classe já existente.</p>
+<p>Assim como foi utilizado o <code>super()</code> para chamar a função <code>__init__</code> da classe pai, é possível utilizá-lo para chamar qualquer outra função. Isso permite, por exemplo, tratar os argumentos da função, aplicando modificações antes de chamar a função original, ou seu retorno, executando algum processamento em cima do retorno dela, não precisando rescrever toda a função.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+
+ Eduardo Klosowski
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Orientação a objetos de outra forma: Métodos estáticos e de classes
+
+ tag:pythonclub.com.br,2021-04-19:/oo-de-outra-forma-2.html
+ 2021-04-19T20:00:00+00:00
+ <p>Na <a href="https://dev.to/acaverna/orientacao-a-objetos-de-outra-forma-classes-e-objetos-3mfd">postagem anterior</a> foi apresentado o <code>self</code>, nessa postagem será discutido mais a respeito desse argumento, considerando opções para ele e suas aplicações.</p>
+<h2>Métodos estáticos</h2>
+<p>Nem todas as funções de uma classe precisam receber uma referência de um objeto para lê-lo ou alterá-lo, muitas vezes uma função pode fazer o seu papel apenas com os dados passados como argumento, por exemplo, receber um nome e validar se ele possui pelo menos três caracteres sem espaço. Dessa forma, essa função poderia ser colocada fora do escopo da classe, porém para facilitar sua chamada, e possíveis alterações (que será discutido em outra postagem), é possível colocar essa função dentro da classe e informar que ela não receberá o argumento <code>self</code> com o decorador <code>@staticmethod</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="o">...</span> <span class="c1"># Demais funções</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">valida_nome</span><span class="p">(</span><span class="n">nome</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">nome</span><span class="p">)</span> <span class="o">>=</span> <span class="mi">3</span> <span class="ow">and</span> <span class="s1">' '</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">nome</span>
+</pre></div>
+
+
+<p>Dessa forma, essa função pode ser chamada diretamente de um objeto pessoa, ou até mesmo diretamente da classe, sem precisar criar um objeto primeiro:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Chamando diretamente da classe</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">Pessoa</span><span class="o">.</span><span class="n">valida_nome</span><span class="p">(</span><span class="s1">'João'</span><span class="p">))</span>
+
+<span class="c1"># Chamando através de um objeto do tipo Pessoa</span>
+<span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">valida_nome</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">nome</span><span class="p">))</span>
+</pre></div>
+
+
+<p>E essa função também pode ser utilizada dendro de outras funções, como validar o nome na criação de uma pessoa, de forma que caso o nome informado seja válido, será criado um objeto do tipo Pessoa, e caso o nome seja inválido, será lançado uma exceção:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">valida_nome</span><span class="p">(</span><span class="n">nome</span><span class="p">):</span>
+ <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s1">'Nome inválido'</span><span class="p">)</span>
+
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+ <span class="o">...</span> <span class="c1"># Demais funções</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">valida_nome</span><span class="p">(</span><span class="n">nome</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">nome</span><span class="p">)</span> <span class="o">>=</span> <span class="mi">3</span> <span class="ow">and</span> <span class="s1">' '</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">nome</span>
+
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span> <span class="c1"># Cria objeto</span>
+<span class="n">p2</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'a'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span> <span class="c1"># Lança ValueError: Nome inválido</span>
+</pre></div>
+
+
+<h2>Métodos da classe</h2>
+<p>Entretanto algumas funções podem precisar de um meio termo, necessitar acessar o contexto da classe, porém sem necessitar de um objeto. Isso é feito através do decorador <code>@classmethod</code>, onde a função decorada com ele, em vez de receber um objeto como primeiro argumento, recebe a própria classe.</p>
+<p>Para demonstrar essa funcionalidade será implementado um <em>id</em> auto incremental para os objetos da classe <code>Pessoa</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="n">total_de_pessoas</span> <span class="o">=</span> <span class="mi">0</span>
+
+ <span class="nd">@classmethod</span>
+ <span class="k">def</span> <span class="nf">novo_id</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span>
+ <span class="bp">cls</span><span class="o">.</span><span class="n">total_de_pessoas</span> <span class="o">+=</span> <span class="mi">1</span>
+ <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">total_de_pessoas</span>
+
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">id</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">novo_id</span><span class="p">()</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">id</span><span class="p">)</span> <span class="c1"># Imprime 1</span>
+<span class="n">p2</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'Maria'</span><span class="p">,</span> <span class="s1">'dos Santos'</span><span class="p">,</span> <span class="mi">18</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p2</span><span class="o">.</span><span class="n">id</span><span class="p">)</span> <span class="c1"># Imprime 2</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">Pessoa</span><span class="o">.</span><span class="n">total_de_pessoas</span><span class="p">)</span> <span class="c1"># Imprime 2</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">total_de_pessoas</span><span class="p">)</span> <span class="c1"># Imprime 2</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p2</span><span class="o">.</span><span class="n">total_de_pessoas</span><span class="p">)</span> <span class="c1"># Imprime 2</span>
+</pre></div>
+
+
+<p>Nesse código é criado uma variável <code>total_de_pessoas</code> dentro do escopo da classe <code>Pessoas</code>, e que é compartilhado tanto pela classe, como pelos objetos dessa classe, diferente de declará-la com <code>self.</code> dentro do <code>__init__</code>, onde esse valor pertenceria apenas ao objeto, e não é compartilhado com os demais objetos. Declarar variáveis dentro do contexto da classe é similar ao se declarar variáveis com <code>static</code> em outras linguagens, assim como o <code>@classmethod</code> é semelhante a declaração de funções com <code>static</code>.</p>
+<p>As funções declaradas com <code>@classmethod</code> também podem ser chamadas sem a necessidade de se criar um objeto, como <code>Pessoa.novo_id()</code>, embora que para essa função específica isso não faça muito sentido, ou receber outros argumentos, tudo depende do que essa função fará.</p>
+<h2>Considerações</h2>
+<p>Embora possa parecer confuso identificar a diferença de uma função de um objeto (função sem decorador), função de uma classe (com decorador <code>@classmethod</code>) e função sem acesso a nenhum outro contexto (com decorador <code>@staticmethod</code>), essa diferença fica mais clara ao se analisar o primeiro argumento recebido por cada tipo de função. Podendo ser a referência a um objeto (<code>self</code>) e assim necessitando que um objeto seja criado anteriormente, ser uma classe (<code>cls</code>) e não necessitando receber um objeto, ou simplesmente não recebendo nenhum argumento especial, apenas os demais argumentos necessários para a função. Sendo diferenciados pelo uso dos decoradores.</p>
+<p>Na orientação a objetos implementada pelo Python, algumas coisas podem ficar confusas quando se mistura com nomenclaturas de outras linguagens que possuem implementações diferentes. A linguagem Java, por exemplo, utiliza a palavra-chave <code>static</code> para definir os atributos e métodos de classe, enquanto no Python um método estático é aquele que não acessa nem um objeto, nem uma classe, devendo ser utilizado o escopo da classe e o decorador <code>@classmethod</code> para se criar atributos e métodos da classe.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+
+ Eduardo Klosowski
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Orientação a objetos de outra forma: Classes e objetos
+
+ tag:pythonclub.com.br,2021-04-12:/oo-de-outra-forma-1.html
+ 2021-04-12T18:00:00+00:00
+ <p>Nas poucas e raríssimas lives que eu fiz na <a href="https://www.twitch.tv/eduardoklosowski">Twitch</a>, surgiu a ideia de escrever sobre programação orientada a objetos em <a href="https://www.python.org/">Python</a>, principalmente por algumas diferenças de como ela foi implementada nessa linguagem. Aproveitando o tema, vou fazer uma série de postagens dando uma visão diferente sobre orientação a objetos. E nessa primeira postagem falarei sobre classes e objetos.</p>
+<h2>Usando um dicionário</h2>
+<p>Entretanto, antes de começar com orientação a objetos, gostaria de apresentar e discutir alguns exemplos sem utilizar esse paradigma de programação.</p>
+<p>Pensando em um sistema que precise manipular dados de pessoas, é possível utilizar os <a href="https://docs.python.org/pt-br/3/library/stdtypes.html#mapping-types-dict">dicionários</a> do Python para agrupar os dados de uma pessoa em uma única variável, como no exemplo a baixo:</p>
+<div class="highlight"><pre><span></span><span class="n">pessoa</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'nome'</span><span class="p">:</span> <span class="s1">'João'</span><span class="p">,</span>
+ <span class="s1">'sobrenome'</span><span class="p">:</span> <span class="s1">'da Silva'</span><span class="p">,</span>
+ <span class="s1">'idade'</span><span class="p">:</span> <span class="mi">20</span><span class="p">,</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>Onde os dados poderiam ser acessados através da variável e do nome do dado desejado, como:</p>
+<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="n">pessoa</span><span class="p">[</span><span class="s1">'nome'</span><span class="p">])</span> <span class="c1"># Imprimindo João</span>
+</pre></div>
+
+
+<p>Assim, todos os dados de uma pessoa ficam agrupados em uma variável, o que facilita bastante a programação, visto que não é necessário criar uma variável para cada dado, e quando se manipula os dados de diferentes pessoas fica muito mais fácil identificar de qual pessoa aquele dado se refere, bastando utilizar variáveis diferentes.</p>
+<h3>Função para criar o dicionário</h3>
+<p>Apesar de prático, é necessário replicar essa estrutura de dicionário toda vez que se desejar utilizar os dados de uma nova pessoa. Para evitar a repetição de código, a criação desse dicionário pode ser feita dentro de uma função que pode ser colocada em um módulo <code>pessoa</code> (arquivo, nesse caso com o nome de <code>pessoa.py</code>):</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa.py</span>
+
+<span class="k">def</span> <span class="nf">nova</span><span class="p">(</span><span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="k">return</span> <span class="p">{</span>
+ <span class="s1">'nome'</span><span class="p">:</span> <span class="n">nome</span><span class="p">,</span>
+ <span class="s1">'sobrenome'</span><span class="p">:</span> <span class="n">sobrenome</span><span class="p">,</span>
+ <span class="s1">'idade'</span><span class="p">:</span> <span class="n">idade</span><span class="p">,</span>
+ <span class="p">}</span>
+</pre></div>
+
+
+<p>E para criar o dicionário que representa uma pessoa, basta importar esse módulo (arquivo) e chamar a função <code>nova</code>:</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">pessoa</span><span class="o">.</span><span class="n">nova</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">p2</span> <span class="o">=</span> <span class="n">pessoa</span><span class="o">.</span><span class="n">nova</span><span class="p">(</span><span class="s1">'Maria'</span><span class="p">,</span> <span class="s1">'dos Santos'</span><span class="p">,</span> <span class="mi">18</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Desta forma, garante-se que todos os dicionários representando pessoas terão os campos desejados e devidamente preenchidos.</p>
+<h3>Função com o dicionário</h3>
+<p>Também é possível criar algumas funções para executar operações com os dados desses dicionários, como pegar o nome completo da pessoa, trocar o seu sobrenome, ou fazer aniversário (o que aumentaria a idade da pessoa em um ano):</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa.py</span>
+
+<span class="k">def</span> <span class="nf">nova</span><span class="p">(</span><span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="o">...</span> <span class="c1"># Código abreviado</span>
+
+
+<span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="n">pessoa</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{pessoa['nome']}</span><span class="s2"> </span><span class="si">{pessoa['sobrenome']}</span><span class="s2">"</span>
+
+
+<span class="k">def</span> <span class="nf">trocar_sobrenome</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'sobrenome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">sobrenome</span>
+
+
+<span class="k">def</span> <span class="nf">fazer_aniversario</span><span class="p">(</span><span class="n">pessoa</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'idade'</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
+</pre></div>
+
+
+<p>E sendo usado como:</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">pessoa</span><span class="o">.</span><span class="n">nova</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">pessoa</span><span class="o">.</span><span class="n">trocar_sobrenome</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="s1">'dos Santos'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">pessoa</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">(</span><span class="n">p1</span><span class="p">))</span>
+<span class="n">pessoa</span><span class="o">.</span><span class="n">fazer_aniversario</span><span class="p">(</span><span class="n">p1</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="p">[</span><span class="s1">'idade'</span><span class="p">])</span>
+</pre></div>
+
+
+<p>Nesse caso, pode-se observar que todas as funções aqui implementadas seguem o padrão de receber o dicionário que representa a pessoa como primeiro argumento, podendo ter outros argumentos ou não conforme a necessidade, acessando e alterando os valores desse dicionário.</p>
+<h2>Versão com orientação a objetos</h2>
+<p>Antes de entrar na versão orientada a objetos propriamente dita dos exemplos anteriores, vou fazer uma pequena alteração para facilitar o entendimento posterior. A função <code>nova</code> será separada em duas partes, a primeira que criará um dicionário, e chamará uma segunda função (<code>init</code>), que receberá esse dicionário como primeiro argumento (seguindo o padrão das demais funções) e criará sua estrutura com os devidos valores.</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa.py</span>
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'nome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'sobrenome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'idade'</span><span class="p">]</span> <span class="o">=</span> <span class="n">idade</span>
+
+
+<span class="k">def</span> <span class="nf">nova</span><span class="p">(</span><span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="n">pessoa</span> <span class="o">=</span> <span class="p">{}</span>
+ <span class="n">init</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">pessoa</span>
+
+
+<span class="o">...</span> <span class="c1"># Demais funções do arquivo</span>
+</pre></div>
+
+
+<p>Porém isso não muda a forma de uso:</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">pessoa</span><span class="o">.</span><span class="n">nova</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+</pre></div>
+
+
+<h3>Função para criar uma pessoa</h3>
+<p>A maioria das linguagens de programação que possuem o paradigma de programação orientado a objetos faz o uso de classes para definir a estrutura dos objetos. O Python também utiliza classes, que podem ser definidas com a palavra-chave <code>class</code> seguidas de um nome para ela. E dentro dessa estrutura, podem ser definidas funções para manipular os objetos daquela classe, que em algumas linguagens também são chamadas de métodos (funções declaradas dentro do escopo uma classe).</p>
+<p>Para converter o dicionário para uma classe, o primeiro passo é implementar uma função para criar a estrutura desejada. Essa função deve possui o nome <code>__init__</code>, e é bastante similar a função <code>init</code> do código anterior:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+</pre></div>
+
+
+<p>As diferenças são que agora o primeiro parâmetro se chama <code>self</code>, que é um padrão utilizado no Python, e em vez de usar colchetes e aspas para acessar os dados, aqui basta utilizar o ponto e o nome do dado desejado (que aqui também pode ser chamado de atributo, visto que é uma variável do objeto). A função <code>nova</code> implementada anteriormente não é necessária, a própria linguagem cria um objeto e passa ele como primeiro argumento para o <code>__init__</code>. E assim para se criar um objeto da classe <code>Pessoa</code> basta chamar a classe como se fosse uma função, ignorando o argumento <code>self</code> e informando os demais, como se estivesse chamando a função <code>__init__</code> diretamente:</p>
+<div class="highlight"><pre><span></span><span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Nesse caso, como a própria classe cria um contexto diferente para as funções (escopo ou <em>namespace</em>), não está mais sendo utilizado arquivos diferentes, porém ainda é possível fazê-lo, sendo necessário apenas fazer o <code>import</code> adequado. Mas para simplificação, tanto a declaração da classe, como a criação do objeto da classe <code>Pessoa</code> podem ser feitas no mesmo arquivo, assim como os demais exemplos dessa postagem.</p>
+<h3>Outras funções</h3>
+<p>As demais funções feitas anteriormente para o dicionário também podem ser feitas na classe <code>Pessoa</code>, seguindo as mesmas diferenças já apontadas anteriormente:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.nome}</span><span class="s1"> </span><span class="si">{self.sobrenome}</span><span class="s1">'</span>
+
+ <span class="k">def</span> <span class="nf">trocar_sobrenome</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+
+ <span class="k">def</span> <span class="nf">fazer_aniversario</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">+=</span> <span class="mi">1</span>
+</pre></div>
+
+
+<p>Para se chamar essas funções, basta acessá-las através do contexto da classe, passando o objeto criado anteriormente como primeiro argumento:</p>
+<div class="highlight"><pre><span></span><span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'dos Santos'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">Pessoa</span><span class="o">.</span><span class="n">trocar_sobrenome</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="s1">'dos Santos'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">Pessoa</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">(</span><span class="n">p1</span><span class="p">))</span>
+<span class="n">Pessoa</span><span class="o">.</span><span class="n">fazer_aniversario</span><span class="p">(</span><span class="n">p1</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">idade</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Essa sintaxe é bastante semelhante a versão sem orientação a objetos implementada anteriormente. Porém quando se está utilizando objetos, é possível chamar essas funções com uma outra sintaxe, informando primeiro o objeto, seguido de ponto e o nome da função desejada, com a diferença de que não é mais necessário informar o objeto como primeiro argumento. Como a função foi chamada através de um objeto, o próprio Python se encarrega de passá-lo para o argumento <code>self</code>, sendo necessário informar apenas os demais argumentos:</p>
+<div class="highlight"><pre><span></span><span class="n">p1</span><span class="o">.</span><span class="n">trocar_sobrenome</span><span class="p">(</span><span class="s1">'dos Santos'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">())</span>
+<span class="n">p1</span><span class="o">.</span><span class="n">fazer_aniversario</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">idade</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Existem algumas diferenças entre as duas sintaxes, porém isso será tratado posteriormente. Por enquanto a segunda sintaxe pode ser vista como um <a href="https://pt.wikipedia.org/wiki/A%C3%A7%C3%BAcar_sint%C3%A1tico">açúcar sintático</a> da primeira, ou seja, uma forma mais rápida e fácil de fazer a mesma coisa que a primeira, e por isso sendo a recomendada.</p>
+<h2>Considerações</h2>
+<p>Como visto nos exemplos, programação orientada a objetos é uma técnica para juntar variáveis em uma mesma estrutura e facilitar a escrita de funções que seguem um determinado padrão, recebendo a estrutura como argumento, porém a sintaxe mais utilizada no Python para chamar as funções de um objeto (métodos) posiciona a variável que guarda a estrutura antes do nome da função, em vez do primeiro argumento.</p>
+<p>No Python, o argumento da estrutura ou objeto (<code>self</code>) aparece explicitamente como primeiro argumento da função, enquanto em outras linguagens essa variável pode receber outro nome (como <code>this</code>) e não aparece explicitamente nos argumentos da função, embora essa variável tenha que ser criada dentro do contexto da função para permitir manipular o objeto.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+
+ Eduardo Klosowski
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Funções in place ou cópia de valor
+
+ tag:pythonclub.com.br,2021-03-29:/funcao-inplace-ou-copia-de-valor.html
+ 2021-03-29T15:00:00+00:00
+ <p>Eventualmente observo dificuldades de algumas pessoas em usar corretamente alguma função, seja porque a função deveria ser executada isoladamente, e utilizado a própria variável que foi passada como argumento posteriormente, seja porque deveria se atribuir o retorno da função a alguma variável, e utilizar essa nova variável. No Python, essa diferença pode ser observada nos métodos das listas <code>sort</code> e <code>reverse</code> para as funções <code>sorted</code> e <code>reversed</code>, que são implementadas com padrões diferentes, <em>in place</em> e cópia de valor respectivamente. Assim pretendo discutir esses dois padrões de funções, comentando qual a diferença e o melhor caso de aplicação de cada padrão.</p>
+<h2>Função de exemplo</h2>
+<p>Para demonstrar como esses padrões funcionam, será implementado uma função que recebe uma lista e calcula o dobro dos valores dessa lista. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">entrada</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+
+<span class="c1"># Execução da função</span>
+
+<span class="n">resultado</span> <span class="o">=</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">8</span><span class="p">]</span>
+</pre></div>
+
+
+<h3>Função com in place</h3>
+<p>A ideia do padrão <em>in place</em> é alterar a própria variável recebida pela função (ou o próprio objeto, caso esteja lidando com orientação a objetos). Neste caso, bastaria calcular o dobro do valor de cada posição da lista, e sobrescrever a posição com seu resultado. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span>
+
+
+<span class="k">def</span> <span class="nf">dobro_inplace</span><span class="p">(</span><span class="n">lista</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
+ <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">lista</span><span class="p">)):</span>
+ <span class="n">lista</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">lista</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
+
+
+<span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">retorno</span> <span class="o">=</span> <span class="n">dobro_inplace</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: valores | Tipo: {type(valores)} | Valor: </span><span class="si">{valores}</span><span class="s1">'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: retorno | Tipo: {type(retorno)} | Valor: </span><span class="si">{retorno}</span><span class="s1">'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Resultado da execução:</p>
+<div class="highlight"><pre><span></span><span class="err">Variável: valores | Tipo: <class 'list'> | Valor: [10, 4, 16, 12, 8]</span>
+<span class="err">Variável: retorno | Tipo: <class 'NoneType'> | Valor: None</span>
+</pre></div>
+
+
+<p>Com essa execução é possível observar que os valores da lista foram alterados, e que o retorno da função é nulo (<code>None</code>), ou seja, a função alterou a própria lista passada como argumento. Outro ponto importante a ser observado é a assinatura da função (tipo dos argumentos e do retorno da função), que recebe uma lista de inteiros e não tem retorno ou é nulo (<code>None</code>). Dessa forma embora seja possível chamar essa função diretamente quando está se informando os argumentos de outra função, como <code>print(dobro_inplace(valores))</code>, a função <code>print</code> receberia <code>None</code> e não a lista como argumento.</p>
+<h3>Função com cópia de valor</h3>
+<p>A ideia do padrão cópia de valor é criar uma cópia do valor passado como argumento e retornar essa cópia, sem alterar a variável recebida (ou criando um novo objeto, no caso de orientação a objetos). Neste caso, é necessário criar uma nova lista e adicionar nela os valores calculados. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span>
+
+
+<span class="k">def</span> <span class="nf">dobro_copia</span><span class="p">(</span><span class="n">lista</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
+ <span class="n">nova_lista</span> <span class="o">=</span> <span class="p">[]</span>
+
+ <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">lista</span><span class="p">)):</span>
+ <span class="n">nova_lista</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">lista</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
+
+ <span class="k">return</span> <span class="n">nova_lista</span>
+
+
+<span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">retorno</span> <span class="o">=</span> <span class="n">dobro_copia</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: valores | Tipo: {type(valores)} | Valor: </span><span class="si">{valores}</span><span class="s1">'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: retorno | Tipo: {type(retorno)} | Valor: </span><span class="si">{retorno}</span><span class="s1">'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Resultado da execução:</p>
+<div class="highlight"><pre><span></span><span class="err">Variável: valores | Tipo: <class 'list'> | Valor: [5, 2, 8, 6, 4]</span>
+<span class="err">Variável: retorno | Tipo: <class 'list'> | Valor: [10, 4, 16, 12, 8]</span>
+</pre></div>
+
+
+<p>Com essa execução é possível observar que a variável <code>valores</code> continua com os valores que tinha antes da execução da função, e a variável retorno apresenta uma lista com os dobros, ou seja, a função não altera a lista passada como argumento e retorna uma nova lista com os valores calculados. Observado a assinatura da função, ela recebe uma lista de inteiros e retorna uma lista de inteiros. Isso permite chamar essa função diretamente nos argumentos para outra função, como <code>print(dobro_copia(valores))</code>, nesse caso a função <code>print</code> receberia a lista de dobros como argumento. Porém caso o retorno da função não seja armazenado, parecerá que a função não fez nada, ou não funcionou. Então em alguns casos, quando o valor anterior não é mais necessário, pode-se reatribuir o retorno da função a própria variável passada como argumento:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="n">dobro_copia</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+</pre></div>
+
+
+<h3>Função híbrida</h3>
+<p>Ainda é possível mesclar os dois padrões de função, alterando o valor passado e retornando-o. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span>
+
+
+<span class="k">def</span> <span class="nf">dobro_hibrido</span><span class="p">(</span><span class="n">lista</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
+ <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">lista</span><span class="p">)):</span>
+ <span class="n">lista</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">lista</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
+
+ <span class="k">return</span> <span class="n">lista</span>
+
+
+<span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">retorno</span> <span class="o">=</span> <span class="n">dobro_hibrido</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: valores | Tipo: {type(valores)} | Valor: </span><span class="si">{valores}</span><span class="s1">'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: retorno | Tipo: {type(retorno)} | Valor: </span><span class="si">{retorno}</span><span class="s1">'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Resultado da execução:</p>
+<div class="highlight"><pre><span></span><span class="err">Variável: valores | Tipo: <class 'list'> | Valor: [10, 4, 16, 12, 8]</span>
+<span class="err">Variável: retorno | Tipo: <class 'list'> | Valor: [10, 4, 16, 12, 8]</span>
+</pre></div>
+
+
+<p>Nesse caso, pode-se apenas chamar a função, como também utilizá-la nos argumentos de outras funções. Porém para se ter os valores originais, deve-se fazer uma cópia manualmente antes de executar a função.</p>
+<h2>Exemplo na biblioteca padrão</h2>
+<p>Na biblioteca padrão do Python, existem os métodos <code>sort</code> e <code>reverse</code> que seguem o padrão <em>in place</em>, e as funções <code>sorted</code> e <code>reversed</code> que seguem o padrão cópia de valor, podendo ser utilizados para ordenar e inverter os valores de uma lista, por exemplo. Quando não é mais necessário uma cópia da lista com a ordem original, é preferível utilizar funções <em>in place</em>, que alteram a própria lista, e como não criam uma cópia da lista, utilizam menos memória. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">valores</span><span class="o">.</span><span class="n">sort</span><span class="p">()</span>
+<span class="n">valores</span><span class="o">.</span><span class="n">reverse</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Se for necessário manter uma cópia da lista inalterada, deve-se optar pelas funções de cópia de valor. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">novos_valores</span> <span class="o">=</span> <span class="nb">reversed</span><span class="p">(</span><span class="nb">sorted</span><span class="p">(</span><span class="n">valores</span><span class="p">))</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">novos_valores</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Porém esse exemplo cria duas cópias da lista, uma em cada função. Para criar apenas uma cópia, pode-se misturar funções <em>in place</em> com cópia de valor. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">novos_valores</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+<span class="n">novos_valores</span><span class="o">.</span><span class="n">reverse</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">novos_valores</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Também vale observar que algumas utilizações dessas funções podem dar a impressão de que elas não funcionaram, como:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+
+<span class="nb">sorted</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span> <span class="c1"># Imprime a lista original, e não a ordenada</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">valores</span><span class="o">.</span><span class="n">sort</span><span class="p">())</span> <span class="c1"># Imprime None e não a lista</span>
+</pre></div>
+
+
+<h2>Considerações</h2>
+<p>Nem sempre é possível utilizar o padrão desejado, <em>strings</em> no Python (<code>str</code>) são imutáveis, logo todas as funções que manipulam elas seguiram o padrão cópia de valor, e para outros tipos, pode ocorrer de só existir funções <em>in place</em>, sendo necessário fazer uma cópia manualmente antes de chamar a função, caso necessário. Para saber qual padrão a função implementa, é necessário consultar sua documentação, ou verificando sua assinatura, embora ainda possa existir uma dúvida entre cópia de valor e híbrida, visto que a assinatura dos dois padrões são iguais.</p>
+<p>Os exemplos aqui dados são didáticos. Caso deseja-se ordenar de forma reversa, tanto o método <code>sort</code>, quanto a função <code>sorted</code> podem receber como argumento <code>reverse=True</code>, e assim já fazer a ordenação reversa. Assim como é possível criar uma nova lista já com os valores, sem precisar adicionar manualmente item por item, como os exemplos:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">partes_dos_valores</span> <span class="o">=</span> <span class="n">valores</span><span class="p">[</span><span class="mi">2</span><span class="p">:]</span>
+<span class="n">novos_valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span> <span class="o">*</span> <span class="n">valor</span> <span class="k">for</span> <span class="n">valor</span> <span class="ow">in</span> <span class="n">valores</span><span class="p">]</span>
+</pre></div>
+
+
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+
+ Eduardo Klosowski
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Encapsulamento da lógica do algoritmo
+
+ tag:pythonclub.com.br,2021-03-02:/encapsulamento-da-logica-do-algoritmo.html
+ 2021-03-02T18:00:00+00:00
+ <p>Muitas listas de exercícios de lógica de programação pedem em algum momento que um valor seja lido do teclado, e caso esse valor seja inválido, deve-se avisar, e repetir a leitura até que um valor válido seja informado. Utilizando a ideia de <a href="https://dev.to/acaverna/otimizando-o-algoritmo-passo-a-passo-4co0">otimização do algoritmo passo a passo</a>, começando com uma solução simples, pretendo estudar como reduzir a duplicação de código alterando o algoritmo, encapsulando a lógica em funções, e encapsulando em classes.</p>
+<h2>Exercício</h2>
+<p>Um exemplo de exercício que pede esse tipo de validação é a leitura de notas, que devem estar entre 0 e 10. A solução mais simples, consiste em ler um valor, e enquanto esse valor for inválido, dar o aviso e ler outro valor. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">))</span>
+<span class="k">while</span> <span class="n">nota</span> <span class="o"><</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">nota</span> <span class="o">></span> <span class="mi">10</span><span class="p">:</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'Nota inválida'</span><span class="p">)</span>
+ <span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">))</span>
+</pre></div>
+
+
+<p>Esse algoritmo funciona, porém existe uma duplicação no código que faz a leitura da nota (uma antes do <em>loop</em> e outra dentro). Caso seja necessário uma alteração, como a mudança da nota para um valor inteiro entre 0 e 100, deve-se alterar os dois lugares, e se feito em apenas um lugar, o algoritmo poderia processar valores inválidos.</p>
+<h3>Alterando o algoritmo</h3>
+<p>Visando remover a repetição de código, é possível unificar a leitura do valor dentro do <em>loop</em>, uma vez que é necessário repetir essa instrução até que o valor válido seja obtido. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">))</span>
+ <span class="k">if</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">nota</span> <span class="o"><=</span> <span class="mi">10</span><span class="p">:</span>
+ <span class="k">break</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'Nota inválida!'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Dessa forma, não existe mais a repetição de código. A condição de parada, que antes verificava se o valor era inválido (o que pode ter uma leitura não tão intuitiva), agora verifica se é um valor válido (que é geralmente é mais fácil de ler e escrever a condição). E a ordem dos comandos dentro do <em>loop</em>, que agora estão em uma ordem que facilita a leitura, visto que no algoritmo anterior era necessário tem em mente o que era executado antes do <em>loop</em>.</p>
+<p>Porém esses algoritmos validam apenas o valor lido, apresentando erro caso seja informado um valor com formato inválido, como letras em vez de números. Isso pode ser resolvido tratando as exceções lançadas. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">))</span>
+ <span class="k">if</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">nota</span> <span class="o"><=</span> <span class="mi">10</span><span class="p">:</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'Nota inválida!'</span><span class="p">)</span>
+</pre></div>
+
+
+<h3>Encapsulamento da lógica em função</h3>
+<p>Caso fosse necessário ler várias notas, com os algoritmos apresentados até então, seria necessário repetir todo esse trecho de código, ou utilizá-lo dentro de uma estrutura de repetição. Para facilitar sua reutilização, evitando a duplicação de código, é possível encapsular esse algoritmo dentro de uma função. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">nota_input</span><span class="p">(</span><span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">nota</span> <span class="o"><=</span> <span class="mi">10</span><span class="p">:</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'Nota inválida!'</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">nota</span>
+
+
+<span class="n">nota1</span> <span class="o">=</span> <span class="n">nota_input</span><span class="p">(</span><span class="s1">'Digite a primeira nota: '</span><span class="p">)</span>
+<span class="n">nota2</span> <span class="o">=</span> <span class="n">nota_input</span><span class="p">(</span><span class="s1">'Digite a segunda nota: '</span><span class="p">)</span>
+</pre></div>
+
+
+<h3>Encapsulamento da lógica em classes</h3>
+<p>Em vez de encapsular essa lógica em uma função, é possível encapsulá-la em uma classe, o que permitiria separar cada etapa do algoritmo em métodos, assim como ter um método responsável por controlar qual etapa deveria ser chamada em qual momento. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ValidaNotaInput</span><span class="p">:</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nota inválida!'</span>
+
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">entrada</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">validar_nota</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nota</span><span class="p">):</span>
+ <span class="k">return</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">nota</span> <span class="o"><=</span> <span class="mi">10</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">nota</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_nota</span><span class="p">(</span><span class="n">nota</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">nota</span>
+
+
+<span class="n">nota_input</span> <span class="o">=</span> <span class="n">ValidaNotaInput</span><span class="p">()</span>
+
+
+<span class="n">nota</span> <span class="o">=</span> <span class="n">nota_input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Vale observar que o método <code>__call__</code> permite que o objeto criado a partir dessa classe seja chamado como se fosse uma função. Nesse caso ele é o responsável por chamar cada etapa do algoritmo, como: <code>ler_entrada</code> que é responsável por ler o que foi digitado no teclado, <code>transformar_entrada</code> que é responsável por converter o texto lido para o tipo desejado (converter de <code>str</code> para <code>float</code>), e <code>validar_nota</code> que é responsável por dizer se o valor é válido ou não. Vale observar que ao dividir o algoritmo em métodos diferentes, seu código principal virou uma espécie de código comentado, descrevendo o que está sendo feito e onde está sendo feito.</p>
+<p>Outra vantagem de encapsular a lógica em classe, em vez de uma função, é a possibilidade de generalizá-la. Se fosse necessário validar outro tipo de entrada, encapsulando em uma função, seria necessário criar outra função repetindo todo o algoritmo, alterando apenas a parte referente a transformação do valor lido, e validação, o que gera uma espécie de repetição de código. Ao encapsular em classes, é possível se aproveitar dos mecanismos de herança para evitar essa repetição. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ValidaInput</span><span class="p">:</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Valor inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">raise</span> <span class="ne">NotImplementedError</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">raise</span> <span class="ne">NotImplementedError</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">valor</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNomeInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nome inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">entrada</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">title</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">valor</span> <span class="o">!=</span> <span class="s1">''</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNotaInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nota inválida!'</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">entrada</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">valor</span> <span class="o"><=</span> <span class="mi">10</span>
+
+
+<span class="n">nome_input</span> <span class="o">=</span> <span class="n">ValidaNomeInput</span><span class="p">()</span>
+<span class="n">nota_input</span> <span class="o">=</span> <span class="n">ValidaNotaInput</span><span class="p">()</span>
+
+
+<span class="n">nome</span> <span class="o">=</span> <span class="n">nome_input</span><span class="p">(</span><span class="s1">'Digite o nome: '</span><span class="p">)</span>
+<span class="n">nota</span> <span class="o">=</span> <span class="n">nota_input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Dessa forma, é possível reutilizar o código já existente para criar outras validações, sendo necessário implementar apenas como converter a <code>str</code> lida do teclado para o tipo desejado, e como esse valor deve ser validado. Não é necessário entender e repetir a lógica de ler o valor, validá-lo, imprimir a mensagem de erro, e repetir até que seja informado um valor válido.</p>
+<h2>Considerações</h2>
+<p>É possível encapsular a lógica de um algoritmo em funções ou em classes. Embora para fazê-lo em uma classe exija conhecimentos de programação orientada a objetos, o seu reaproveitamento é facilitado, abstraindo toda a complexidade do algoritmo, que pode ser disponibilizado através de uma biblioteca, exigindo apenas a implementações de métodos simples por quem for a utilizar.</p>
+<p>Ainda poderia ser discutido outras formas de fazer essa implementação, como passar funções como parâmetro e a utilização de <a href="https://docs.python.org/pt-br/3/library/asyncio-task.html">corrotinas</a> no encapsulamento do algoritmo em função, assim como a utilização de <a href="https://docs.python.org/pt-br/3/library/functions.html#classmethod">classmethod</a>, <a href="https://docs.python.org/pt-br/3/library/functions.html#staticmethod">staticmethod</a> e <a href="https://docs.python.org/pt-br/3/library/abc.html">ABC</a> no encapsulamento do algoritmo em classes.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+
+ Eduardo Klosowski
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Fazendo backup do banco de dados no Django
+
+ tag:pythonclub.com.br,2020-10-30:/fazendo-backup-do-banco-de-dados-no-django.html
+ 2020-10-30T13:40:00+00:00
+ <h2>Apresentação</h2>
+<p>Em algum momento, durante o seu processo de desenvolvimento com Django, pode ser que surja a necessidade de criar e restaurar o banco de dados da aplicação. Pensando nisso, resolvi fazer um pequeno tutorial, básico, de como realizar essa operação.</p>
+<p>Nesse tutorial, usaremos o <a href="https://github.com/django-dbbackup/django-dbbackup">django-dbbackup</a>, um pacote desenvolvido especificamente para isso.</p>
+<h2>Configurando nosso ambiente</h2>
+<p>Primeiro, partindo do início, vamos criar uma pasta para o nosso projeto e, nela, isolar o nosso ambiente de desenvolvimento usando uma <a href="https://virtualenv.pypa.io/en/latest/index.html">virtualenv</a>:</p>
+<div class="highlight"><pre><span></span>mkdir projeto_db <span class="o">&&</span> <span class="nb">cd</span> projeto_db <span class="c1">#criando a pasta do nosso projeto</span>
+
+virtualenv -p python3.8 env <span class="o">&&</span> <span class="nb">source</span> env/bin/activate <span class="c1">#criando e ativando a nossa virtualenv</span>
+</pre></div>
+
+
+<p>Depois disso e com o nosso ambiente já ativo, vamos realizar os seguintes procedimentos:</p>
+<div class="highlight"><pre><span></span>pip install -U pip <span class="c1">#com isso, atualizamos a verão do pip instalado</span>
+</pre></div>
+
+
+<h2>Instalando as dependências</h2>
+<p>Agora, vamos instalar o <a href="https://www.djangoproject.com/">Django</a> e o pacote que usaremos para fazer nossos backups.</p>
+<div class="highlight"><pre><span></span>pip install <span class="nv">Django</span><span class="o">==</span><span class="m">3</span>.1.2 <span class="c1">#instalando o Django</span>
+
+pip install django-dbbackup <span class="c1">#instalando o django-dbbackup</span>
+</pre></div>
+
+
+<h2>Criando e configurando projeto</h2>
+<p>Depois de instaladas nossas dependências, vamos criar o nosso projeto e configurar o nosso pacote nas configurações do Django.</p>
+<div class="highlight"><pre><span></span>django-admin startproject django_db . <span class="c1">#dentro da nossa pasta projeto_db, criamos um projeto Django com o nome de django_db.</span>
+</pre></div>
+
+
+<p>Depois de criado nosso projeto, vamos criar e popular o nosso banco de dados.</p>
+<div class="highlight"><pre><span></span>python manage.py migrate <span class="c1">#com isso, sincronizamos o estado do banco de dados com o conjunto atual de modelos e migrações.</span>
+</pre></div>
+
+
+<p>Criado nosso banco de dados, vamos criar um superusuário para podemos o painel admin do nosso projeto.</p>
+<div class="highlight"><pre><span></span>python manage.py createsuperuser
+</pre></div>
+
+
+<p>Perfeito. Já temos tudo que precisamos para executar nosso projeto. Para execução dele, é só fazermos:</p>
+<div class="highlight"><pre><span></span>python manage.py runserver
+</pre></div>
+
+
+<p>Você terá uma imagem assim do seu projeto:</p>
+<p><img alt="" src="https://jacksonosvaldo.github.io/img/django_db.png" /></p>
+<h2>Configurando o django-dbbackup</h2>
+<p>Dentro do seu projeto, vamos acessar o arquivo settings.py, como expresso abaixo:</p>
+<div class="highlight"><pre><span></span>django_db/
+├── settings.py
+</pre></div>
+
+
+<p>Dentro desse arquivos iremos, primeiro, adiconar o django-dbbackup às apps do projeto:</p>
+<div class="highlight"><pre><span></span><span class="n">INSTALLED_APPS</span> <span class="o">=</span> <span class="p">(</span>
+ <span class="o">...</span>
+ <span class="s1">'dbbackup'</span><span class="p">,</span> <span class="c1"># adicionando django-dbbackup</span>
+<span class="p">)</span>
+</pre></div>
+
+
+<p>Depois de adicionado às apps, vamos dizer para o Django o que vamos salvar no backup e, depois, indicar a pasta para onde será encaminhado esse arquivo. Essa inserção deve ou pode ser feita no final do arquivo <em>settings.py</em>:</p>
+<div class="highlight"><pre><span></span><span class="n">DBBACKUP_STORAGE</span> <span class="o">=</span> <span class="s1">'django.core.files.storage.FileSystemStorage'</span> <span class="c1">#o que salvar</span>
+<span class="n">DBBACKUP_STORAGE_OPTIONS</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'location'</span><span class="p">:</span> <span class="s1">'backups/'</span><span class="p">}</span> <span class="c1"># onde salvar</span>
+</pre></div>
+
+
+<p>Percebam que dissemos para o Django salvar o backup na pasta <em>backups</em>, mas essa pasta ainda não existe no nosso projeto. Por isso, precisamos criá-la [fora da pasta do projeto]:</p>
+<div class="highlight"><pre><span></span>mkdir backups
+</pre></div>
+
+
+<h2>Criando e restaurando nosso backup</h2>
+<p>Já temos tudo pronto. Agora, vamos criar o nosso primeiro backup:</p>
+<div class="highlight"><pre><span></span>python manage.py dbbackup
+</pre></div>
+
+
+<p>Depois de exetudado, será criado um arquivo -- no nosso exemplo, esse arquivo terá uma extensão .dump --, salvo na pasta <em>backups</em>. Esse arquivo contem todo backup do nosso banco de dados.</p>
+<p>Para recuperarmos nosso banco, vamos supor que migramos nosso sistema de um servidor antigo para um novo e, por algum motivo, nossa base de dados foi corrompida, inviabilizando seu uso. Ou seja, estamos com o sistema/projeto sem banco de dados -- ou seja, exlua ou mova a a sua base dados .sqlite3 para que esse exemplo seja útil --, mas temos os backups. Com isso, vamos restaurar o banco:</p>
+<div class="highlight"><pre><span></span>python manage.py dbrestore
+</pre></div>
+
+
+<p>Prontinho, restauramos nosso banco de dados. O interessante do django-dbbackup, dentre outras coisas, é que ele gera os backups com datas e horários específicos, facilitando o processo de recuperação das informações mais recentes.</p>
+<p>Por hoje é isso, pessoal. Até a próxima. ;) </p>
+
+ Jackson Osvaldo
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Seqtembro de eventos virtuais e gratuitos sobre Qt e KDE
+
+ https://blog.filipesaraiva.info/?p=2185
+ 2020-08-29T18:48:00+00:00
+ <p>(Ok a piada com <em>seqtembro</em> funciona melhor na versão em inglês, <em>seqtember</em>, mas simbora)</p>
+<p>Por uma grande coincidência, obra do destino, ou nada disso, teremos um Setembro de 2020 repleto de eventos virtuais e gratuitos de alta qualidade sobre <a href="https://www.qt.io/" target="_blank" rel="noopener noreferrer">Qt</a> e <a href="https://kde.org/" target="_blank" rel="noopener noreferrer">KDE</a>.</p>
+<p>Começando de 4 à 11 do referido mês teremos o <a href="https://akademy.kde.org/2020/" target="_blank" rel="noopener noreferrer">Akademy 2020</a>, o grande encontro mundial da comunidade KDE que esse ano, por motivos que todos sabemos, acontecerá de forma virtual. A <a href="https://akademy.kde.org/2020/program" target="_blank" rel="noopener noreferrer">programação</a> do Akademy traz palestras, treinamentos, <em>hacking sessions</em>, discussões com foco em aplicações KDE específicas, e mais, reunindo hackers, designers, gerentes de projetos, tradutores, e colabores dos mais diversos segmentos para discutir e planejar o KDE e seus futuros passos.</p>
+<p><a href="https://blog.filipesaraiva.info/?attachment_id=2190" rel="attachment wp-att-2190"><img /></a></p>
+<p>E como falamos em KDE, por extensão, também falamos em Qt – afinal, grande parte das aplicações é escrita nesse framework. Portanto, mesmo que você trabalhe com Qt mas não use nada do KDE, vale a pena participar do evento – e também se perguntar “porque diabos não estou usando e desenvolvendo aplicações do KDE?”.</p>
+<p>Um incentivo extra é que durante o Akademy, entre 7 e 11, acontecerá o <a href="https://www.qtdesktopdays.com/" target="_blank" rel="noopener noreferrer">Qt Desktop Days</a>, evento da <a href="https://www.kdab.com/" target="_blank" rel="noopener noreferrer">KDAB</a> voltado para Qt no desktop (surpresa?). A <a href="https://www.qtdesktopdays.com/program/" target="_blank" rel="noopener noreferrer">programação preliminar</a> já está disponível e será muito interessante ver os avanços da tecnologia em um campo que pode parecer menos <em>sexy</em> hoje em dia, por conta da muita atenção dada a projetos mobile ou embarcados, mas que pelo contrário, continua vibrante e recebendo muito investimento.</p>
+<p><img /></p>
+<p>Após uma rápida pausa para um respiro, temos a <a href="http://bit.do/maratonaqt" target="_blank" rel="noopener noreferrer">Maratona Qt</a>. Nosso amigo <a href="https://sandroandrade.org/" target="_blank" rel="noopener noreferrer">Sandro Andrade</a>, professor do IFBA e colaborador de longa data do KDE, resolveu dedicar uma semana inteira, de 14 à 18 de setembro, para apresentar 5 tópicos sobre o Qt tratando de seus fundamentos e passos iniciais de cada um. O programa cobre QML, C++ e Qt, Qt no Android, no iOS, na web (sim!), computação gráfica e mesmo jogos! Extremamente recomendada pra todo mundo que conhece ou quer conhecer o framework.</p>
+<p>A Maratona Qt vai servir como um esquenta para a <a href="https://br.qtcon.org/" target="_blank" rel="noopener noreferrer">QtCon Brasil 2020</a>, esse ano também virtual. Em 26 e 27 de setembro o pessoal da <a href="http://qmob.solutions/" target="_blank" rel="noopener noreferrer">qmob.solutions</a> reunirá desenvolvedores Qt de vários países para apresentarem, entre outras coisas, trabalhos com Wayland, visão computacional e IA, análise de dados, Python, containers, prototipagem, embarcados, e outros, tudo envolvendo Qt! E também haverá uma apresentação sobre a próxima versão <em>major</em> da ferramenta, Qt 6.</p>
+<p>Portanto pessoal, reservem este mês para uma grande imersão nos vários aspectos e possibilidades disponibilizadas pelo Qt.</p>
+
+ Filipe Saraiva
+ https://blog.filipesaraiva.info
+
+
+ planeta-python-brasil – Filipe Saraiva's blog
+ Tecnologia, sociedade e política.
+
+ https://blog.filipesaraiva.info/?feed=rss2&tag=planeta-python-brasil
+ 2025-04-17T02:14:14+00:00
+
+
+
+
+ MinHashing all the things: a quick analysis of MAG search results
+
+ tag:blog.luizirber.org,2020-07-24:/2020/07/24/mag-results/
+ 2020-07-24T15:00:00+00:00
+ <p><a href="https://blog.luizirber.org/2020/07/22/mag-search/">Last time</a> I described a way to search MAGs in metagenomes,
+and teased about interesting results.
+Let's dig in some of them!</p>
+<p>I prepared a <a href="https://github.com/luizirber/2020-07-22-mag-search/">repo</a> with the data and a notebook with the analysis I did in this
+post.
+You can also follow along in <a href="https://mybinder.org">Binder</a>,
+as well as do your own analysis! <a href="https://mybinder.org/v2/gh/luizirber/2020-07-22-mag-search/master?filepath=index.ipynb"><img alt="Binder" src="https://mybinder.org/badge_logo.svg" /></a></p>
+<h2>Preparing some metadata</h2>
+<p>The supplemental materials for <a href="https://www.nature.com/articles/sdata2017203">Tully et al</a> include more details about each MAG,
+so let's download them.
+I prepared a small snakemake workflow to do that,
+as well as downloading information about the SRA datasets from Tara Oceans
+(the dataset used to generate the MAGs),
+as well as from <a href="https://www.nature.com/articles/s41564-017-0012-7">Parks et al</a>,
+which also generated MAGs from Tara Oceans.
+Feel free to include them in your analysis,
+but I was curious to find matches in other metagenomes.</p>
+<h2>Loading the data</h2>
+<p>The results from the MAG search are in a CSV file,
+with a column for the MAG name,
+another for the SRA dataset ID for the metagenome and a third column for the
+containment of the MAG in the metagenome.
+I also fixed the names to make it easier to query,
+and finally removed the Tara and Parks metagenomes
+(because we already knew they contained these MAGs).</p>
+<p>This left us with 23,644 SRA metagenomes with matches,
+covering 2,291 of the 2,631 MAGs.
+These are results for a fairly low containment (10%),
+so if we limit to MAGs with more than 50% containment we still have 1,407 MAGs and 2,938 metagenomes left.</p>
+<h2>TOBG_NP-110, I choose you!</h2>
+<p>That's still a lot,
+so I decided to pick a candidate to check before doing any large scale analysis.
+I chose TOBG_NP-110 because there were many matches above 50% containment,
+and even some at 99%.
+Turns out it is also an Archaeal MAG that failed to be classified further than Phylum level (Euryarchaeota),
+with a 70.3% complete score in the original analysis.
+Oh, let me dissect the name a bit:
+TOBG is "Tara Ocean Binned Genome" and "NP" is North Pacific.</p>
+<p>And so I went checking where the other metagenome matches came from.
+5 of the 12 matches above 50% containment come from one study,
+<a href="https://trace.ncbi.nlm.nih.gov/Traces/sra/?study=SRP044185">SRP044185</a>,
+with samples collected from a column of water in a station in Manzanillo, Mexico.
+Other 3 matches come from
+<a href="https://trace.ncbi.nlm.nih.gov/Traces/sra/?study=SRP003331">SRP003331</a>,
+in the South Pacific ocean (in northern Chile).
+Another match,
+<a href="https://trace.ncbi.nlm.nih.gov/Traces/sra/?run=ERR3256923">ERR3256923</a>,
+also comes from the South Pacific.</p>
+<h2>What else can I do?</h2>
+<p>I'm curious to follow <a href="http://merenlab.org/data/refining-mags/">the refining MAGs</a> tutorial from the Meren Lab and see where this goes,
+and especially in using <a href="https://genomebiology.biomedcentral.com/articles/10.1186/s13059-020-02066-4"><code>spacegraphcats</code></a>
+to extract neighborhoods from the MAG and better evaluate what is missing or if there are other interesting bits that
+the MAG generation methods ended up discarding.</p>
+<p>So, for now that's it.
+But more important,
+I didn't want to sit on these results until there is a publication in press,
+especially when there are people that can do so much more with these,
+so I decided to make it all public.
+It is way more exciting to see this being used to know more about these
+organisms than me being the only one with access to this info.</p>
+<p>And yesterday I saw <a href="https://twitter.com/DrJonathanRosa/status/1286381346605027328">this tweet</a> by
+<a href="https://twitter.com/DrJonathanRosa/status/1286381346605027328">@DrJonathanRosa</a>,
+saying:</p>
+<blockquote>
+<p>I don’t know who told students that the goal of research is to find some
+previously undiscovered research topic, claim individual ownership over it,
+& fiercely protect it from theft, but that almost sounds like, well,
+colonialism, capitalism, & policing </p>
+</blockquote>
+<p>Amen.</p>
+<h2>I want to run this with my data!</h2>
+<p>Next time. But we will have a discussion about scientific infrastructure and
+sustainability first =]</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://twitter.com/luizirber/status/1286700888111738880">Thread on Twitter</a></li>
+</ul>
+
+ luizirber
+ https://blog.luizirber.org/
+
+
+ Gabbleblotchits
+ Vogon Poetry, Computers and (some) biology
+
+ https://blog.luizirber.org/
+ 2025-04-17T02:14:15+00:00
+
+
+
+
+ MinHashing all the things: searching for MAGs in the SRA
+
+ tag:blog.luizirber.org,2020-07-22:/2020/07/22/mag-search/
+ 2020-07-22T15:00:00+00:00
+ <p>(or: Top-down and bottom-up approaches for working around sourmash limitations)</p>
+<p>In the last month I updated <a href="https://wort.oxli.org">wort</a>,
+the system I developed for computing sourmash signature for public genomic databases,
+and started calculating signatures
+for the <a href="https://www.ncbi.nlm.nih.gov/sra/?term=%22METAGENOMIC%22%5Bsource%5D+NOT+amplicon%5BAll+Fields%5D)">metagenomes</a> in the <a href="https://www.ncbi.nlm.nih.gov/sra/">Sequence Read Archive</a>.
+This is a more challenging subset than the <a href="https://blog.luizirber.org/2016/12/28/soursigs-arch-1/">microbial datasets</a> I was doing previously,
+since there are around 534k datasets from metagenomic sources in the SRA,
+totalling 447 TB of data.
+Another problem is the size of the datasets,
+ranging from a couple of MB to 170 GB.
+Turns out that the workers I have in <code>wort</code> are very good for small-ish datasets,
+but I still need to figure out how to pull large datasets faster from the SRA,
+because the large ones take forever to process...</p>
+<p>The good news is that I managed to calculate signatures for almost 402k of them
+<sup id="sf-mag-search-1-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-mag-search-1" class="simple-footnote" title="pulling about a 100 TB in 3 days, which was pretty fun to see because I ended up DDoS myself because I couldn't download the generated sigs fast enough from the S3 bucket where they are temporarily stored =P">1</a></sup>,
+which already let us work on some pretty exciting problems =]</p>
+<h2>Looking for MAGs in the SRA</h2>
+<p>Metagenome-assembled genomes are essential for studying organisms that are hard to isolate and culture in lab,
+especially for environmental metagenomes.
+<a href="https://www.nature.com/articles/sdata2017203">Tully et al</a> published 2,631 draft MAGs from 234 samples collected during the Tara Oceans expedition,
+and I wanted to check if they can also be found in other metagenomes besides the Tara Oceans ones.
+The idea is to extract the reads from these other matches and evaluate how the MAG can be improved,
+or at least evaluate what is missing in them.
+I choose to use environmental samples under the assumption they are easier to deposit on the SRA and have public access,
+but there are many human gut microbiomes in the SRA and this MAG search would work just fine with those too.</p>
+<p>Moreover,
+I want to search for containment,
+and not similarity.
+The distinction is subtle,
+but similarity takes into account both datasets sizes
+(well, the size of the union of all elements in both datasets),
+while containment only considers the size of the query.
+This is relevant because the similarity of a MAG and a metagenome is going to be very small (and is symmetrical),
+but the containment of the MAG in the metagenome might be large
+(and is asymmetrical, since the containment of the metagenome in the MAG is likely very small because the metagenome is so much larger than the MAG).</p>
+<h2>The computational challenge: indexing and searching</h2>
+<p>sourmash signatures are a small fraction of the original size of the datasets,
+but when you have hundreds of thousands of them the collection ends up being pretty large too.
+More precisely, 825 GB large.
+That is way bigger than any index I ever built for sourmash,
+and it would also have pretty distinct characteristics than what we usually do:
+we tend to index genomes and run <code>search</code> (to find similar genomes) or <code>gather</code>
+(to decompose metagenomes into their constituent genomes),
+but for this MAG search I want to find which metagenomes have my MAG query above a certain containment threshold.
+Sort of a <code>sourmash search --containment</code>,
+but over thousands of metagenome signatures.
+The main benefit of an SBT index in this context is to avoid checking all signatures because we can prune the search early,
+but currently SBT indices need to be totally loaded in memory during <code>sourmash index</code>.
+I will have to do this in the medium term,
+but I want a solution NOW! =]</p>
+<p><a href="https://github.com/dib-lab/sourmash/releases/tag/v3.4.0">sourmash 3.4.0</a> introduced <code>--from-file</code> in many commands,
+and since I can't build an index I decided to use it to load signatures for the metagenomes.
+But... <code>sourmash search</code> tries to load all signatures in memory,
+and while I might be able to find a cluster machine with hundreds of GBs of RAM available,
+that's not very practical.</p>
+<p>So, what to do?</p>
+<h2>The top-down solution: a snakemake workflow</h2>
+<p>I don't want to modify sourmash now,
+so why not make a workflow and use snakemake to run one <code>sourmash search --containment</code> for each metagenome?
+That means 402k tasks,
+but at least I can use <a href="https://snakemake.readthedocs.io/en/stable/executing/cli.html#dealing-with-very-large-workflows">batches</a> and <a href="https://slurm.schedmd.com/job_array.html">SLURM job arrays</a> to submit reasonably-sized jobs to our HPC queue.
+After running all batches I summarized results for each task,
+and it worked well for a proof of concept.</p>
+<p>But... it was still pretty resource intensive:
+each task was running one query MAG against one metagenome,
+and so each task needed to do all the overhead of starting the Python interpreter and parsing the query signature,
+which is exactly the same for all tasks.
+Extending it to support multiple queries to the same metagenome would involve duplicating tasks,
+and 402k metagenomes times 2,631 MAGs is...
+a very large number of jobs.</p>
+<p>I also wanted to avoid clogging the job queues,
+which is not very nice to the other researchers using the cluster.
+This limited how many batches I could run in parallel...</p>
+<h2>The bottom-up solution: Rust to the rescue!</h2>
+<p>Thinking a bit more about the problem,
+here is another solution:
+what if we load all the MAGs in memory
+(as they will be queried frequently and are not that large),
+and then for each metagenome signature load it,
+perform all MAG queries,
+and then unload the metagenome signature from memory?
+This way we can control memory consumption
+(it's going to be proportional to all the MAG sizes plus the size of the largest metagenome)
+and can also efficiently parallelize the code because each task/metagenome is independent
+and the MAG signatures can be shared freely (since they are read-only).</p>
+<p>This could be done with the sourmash Python API plus <code>multiprocessing</code> or some
+other parallelization approach (maybe dask?),
+but turns out that everything we need comes from the Rust API.
+Why not enjoy a bit of the <a href="https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html">fearless concurrency</a> that is one of the major Rust goals?</p>
+<p><a href="https://github.com/luizirber/phd/blob/aa1ed9eb33ba71fdf9b3f2c92931701be6df00cd/experiments/wort/sra_search/src/main.rs">The whole code</a> ended up being 176 lines long,
+including command line parsing using <a href="https://docs.rs/structopt/latest/structopt/">strucopt</a> and parallelizing the search using <a href="https://docs.rs/rayon/latest/rayon/">rayon</a>
+and a <a href="https://doc.rust-lang.org/std/sync/mpsc/fn.channel.html">multiple-producer, single-consumer channel</a> to write results to an output
+(either the terminal or a file).
+This version took 11 hours to run,
+using less than 5GB of RAM and 32 processors,
+to search 2k MAGs against 402k metagenomes.
+And, bonus! It can also be parallelized again if you have multiple machines,
+so it potentially takes a bit more than an hour to run if you can allocate 10 batch jobs,
+with each batch 1/10 of the metagenome signatures.</p>
+<h2>So, is bottom-up always the better choice?</h2>
+<p>I would like to answer "Yes!",
+but bioinformatics software tends to be organized as command line interfaces,
+not as libraries.
+Libraries also tend to have even less documentation than CLIs,
+and this particular case is not a fair comparison because...
+Well, I wrote most of the library,
+and the Rust API is not that well documented for general use.</p>
+<p>But I'm pretty happy with how the sourmash CLI is viable both for the top-down approach
+(and whatever workflow software you want to use) as well as how the Rust core worked for the bottom-up approach.
+I think the most important is having the option to choose which way to go,
+especially because now I can use the bottom-up approach to make the sourmash CLI
+and Python API better.
+The top-down approach is also way more accessible in general,
+because you can pick your favorite workflow software and use all the tricks you're comfortable with.</p>
+<h2>But, what about the results?!?!?!</h2>
+<p>Next time. But I did find MAGs with over 90% containment in very different locations,
+which is pretty exciting!</p>
+<p>I also need to find a better way of distributing all these signature,
+because storing 4 TB of data in S3 is somewhat cheap,
+but transferring data is very expensive.
+All signatures are also available on IPFS,
+but I need more people to host them and share.
+Get in contact if you're interested in helping =]</p>
+<p>And while I'm asking for help,
+any tips on pulling data faster from the SRA are greatly appreciated!</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://twitter.com/luizirber/status/1285782732790849537">Thread on Twitter</a></li>
+</ul><hr /><h2>Footnotes</h2><ol><li id="sf-mag-search-1"><p>pulling about a 100 TB in 3 days, which was pretty fun to see because I
+ended up DDoS myself because I couldn't download the generated sigs fast enough
+from the S3 bucket where they are temporarily stored =P <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-mag-search-1-back" class="simple-footnote-back">↩</a></p></li></ol>
+
+ luizirber
+ https://blog.luizirber.org/
+
+
+ Gabbleblotchits
+ Vogon Poetry, Computers and (some) biology
+
+ https://blog.luizirber.org/
+ 2025-04-17T02:14:15+00:00
+
+
+
+
+ Putting it all together
+
+ tag:blog.luizirber.org,2020-05-11:/2020/05/11/sbt-zip/
+ 2020-05-11T15:00:00+00:00
+ <p>sourmash <a href="https://twitter.com/ctitusbrown/status/1257418140729868291">3.3</a> was released last week,
+and it is the first version supporting <a href="http://ivory.idyll.org/blog/2020-sourmash-databases-as-zip-files.html">zipped databases</a>.
+Here is my personal account of how that came to be =]</p>
+<h2>What is a sourmash database?</h2>
+<p>A sourmash database contains signatures (typically Scaled MinHash sketches built from genomic datasets) and
+an index for allowing efficient similarity and containment queries over these signatures.
+The two types of index are SBT,
+a hierarchical index that uses less memory by keeping data on disk,
+and LCA,
+an inverted index that uses more memory but is potentially faster.
+Indices are described as JSON files,
+with LCA storing all the data in one JSON file and SBT opting for saving a description of the index structure in JSON,
+and all the data into a hidden directory with many files.</p>
+<p>We distribute some <a href="https://sourmash.readthedocs.io/en/v3.3.0/databases.html">prepared databases</a> (with SBT indices) for Genbank and RefSeq as compressed TAR files.
+The compressed file is ~8GB,
+but after decompressing it turns into almost 200k files in a hidden directory,
+using about 40 GB of disk space.</p>
+<h2>Can we avoid generating so many hidden files?</h2>
+<p>The initial issue in this saga is <a href="https://github.com/dib-lab/sourmash/issues/490">dib-lab/sourmash#490</a>,
+and the idea was to take the existing support for multiple data storages
+(hidden dir,
+TAR files,
+IPFS and Redis) and save the index description in the storage,
+allowing loading everything from the storage.
+Since we already had the databases as TAR files,
+the first test tried to use them but it didn't take long to see it was a doomed approach:
+TAR files are terrible from random access
+(or at least the <code>tarfile</code> module in Python is).</p>
+<p>Zip files showed up as a better alternative,
+and it helps that Python has the <code>zipfile</code> module already available in the
+standard library.
+Initial tests were promising,
+and led to <a href="https://github.com/dib-lab/sourmash/pull/648">dib-lab/sourmash#648</a>.
+The main issue was performance:
+compressing and decompressing was slow,
+but there was also another limitation...</p>
+<h2>Loading Nodegraphs from a memory buffer</h2>
+<p>Another challenge was efficiently loading the data from a storage.
+The two core methods in a storage are <code>save(location, content)</code>,
+where <code>content</code> is a bytes buffer,
+and <code>load(location)</code>,
+which returns a bytes buffer that was previously saved.
+This didn't interact well with the <code>khmer</code> <code>Nodegraph</code>s (the Bloom Filter we use for SBTs),
+since <code>khmer</code> only loads data from files,
+not from memory buffers.
+We ended up doing a temporary file dance,
+which made things slower for the default storage (hidden dir),
+where it could have been optimized to work directly with files,
+and involved interacting with the filesystem for the other storages
+(IPFS and Redis could be pulling data directly from the network,
+for example).</p>
+<p>This one could be fixed in <code>khmer</code> by exposing C++ stream methods,
+and I did a <a href="https://github.com/luizirber/2018-cython-streams">small PoC</a> to test the idea.
+While doable,
+this is something that was happening while the sourmash conversion to Rust was underway,
+and depending on <code>khmer</code> was a problem for my Webassembly aspirations...
+so,
+having the Nodegraph <a href="https://github.com/luizirber/sourmash-rust/pull/15">implemented in Rust</a> seemed like a better direction,
+That has actually been quietly living in the sourmash codebase for quite some time,
+but it was never exposed to the Python (and it was also lacking more extensive
+tests).</p>
+<p>After the release of sourmash 3 and the replacement of the C++ for the Rust implementation,
+all the pieces for exposing the Nodegraph where in place,
+so <a href="https://github.com/dib-lab/sourmash/pull/799">dib-lab/sourmash#799</a> was the next step.
+It wasn't a priority at first because other optimizations
+(that were released in 3.1 and 3.2)
+were more important,
+but then it was time to check how this would perform.
+And...</p>
+<h2>Your Rust code is not so fast, huh?</h2>
+<p>Turns out that my Nodegraph loading code was way slower than <code>khmer</code>.
+The Nodegraph binary format <a href="https://khmer.readthedocs.io/en/latest/dev/binary-file-formats.html#nodegrap://khmer.readthedocs.io/en/latest/dev/binary-file-formats.html#nodegraph">is well documented</a>,
+and doing an initial implementation wasn't so hard by using the <code>byteorder</code> crate
+to read binary data with the right endianess,
+and then setting the appropriate bits in the internal <code>fixedbitset</code> in memory.
+But the khmer code doesn't parse bit by bit:
+it <a href="https://github.com/dib-lab/khmer/blob/fe0ce116456b296c522ba24294a0cabce3b2648b/src/oxli/storage.cc#L233">reads</a> a long <code>char</code> buffer directly,
+and that is many orders of magnitude faster than setting bit by bit.</p>
+<p>And there was no way to replicate this behavior directly with <code>fixedbitset</code>.
+At this point I could either bit-indexing into a large buffer
+and lose all the useful methods that <code>fixedbitset</code> provides,
+or try to find a way to support loading the data directly into <code>fixedbitset</code> and
+open a PR.</p>
+<p><a href="https://github.com/petgraph/fixedbitset/pull/42">I chose the PR</a> (and even got #42! =]).</p>
+<p>It was more straightforward than I expected,
+but it did expose the internal representation of <code>fixedbitset</code>,
+so I was a bit nervous it wasn't going to be merged.
+But <a href="https://github.com/bluss">bluss</a> was super nice,
+and his suggestions made the PR way better!
+This <a href="https://github.com/dib-lab/sourmash/blob/9a695fb03b99c060bb8d1384ab78bb3797c5eb65/src/core/src/sketch/nodegraph.rs#L235L261">simplified</a> the final <code>Nodegraph</code> code,
+and actually was more correct
+(because I was messing a few corner cases when doing the bit-by-bit parsing before).
+Win-win!</p>
+<h2>Nodegraphs are kind of large, can we compress them?</h2>
+<p>Being able to save and load <code>Nodegraph</code>s in Rust allowed using memory buffers,
+but also opened the way to support other operations not supported in khmer <code>Nodegraph</code>s.
+One example is loading/saving compressed files,
+which is supported for <code>Countgraph</code>
+(another khmer data structure,
+based on Count-Min Sketch)
+but not in <code>Nodegraph</code>.</p>
+<p>If only there was an easy way to support working with compressed files...</p>
+<p>Oh wait, there is! <a href="https://github.com/luizirber/niffler">niffler</a> is a crate that I made with <a href="https://twitter.com/pierre_marijon">Pierre Marijon</a> based
+on some functionality I saw in one of his projects,
+and we iterated a bit on the API and documented everything to make it more
+useful for a larger audience.
+<code>niffler</code> tries to be as transparent as possible,
+with very little boilerplate when using it but with useful features nonetheless
+(like auto detection of the compression format).
+If you want more about the motivation and how it happened,
+check this <a href="https://twitter.com/luizirber/status/1253445504622424064">Twitter thread</a>.</p>
+<p>The cool thing is that adding compressed files support in <code>sourmash</code> was mostly
+<a href="https://github.com/dib-lab/sourmash/pull/799/files#diff-313a7ff0fdb14f408a64b3f010f46f65R220">one-line changes</a> for loading
+(and <a href="https://github.com/dib-lab/sourmash/pull/648/files#diff-d80ae1dd777d07300d7b6066b3318397L249-R273">a bit more</a> for saving,
+but mostly because converting compression levels could use some refactoring).</p>
+<h2>Putting it all together: zipped SBT indices</h2>
+<p>With all these other pieces in places,
+it's time to go back to <a href="https://github.com/dib-lab/sourmash/pull/648">dib-lab/sourmash#648</a>.
+Compressing and decompressing with the Python <code>zipfile</code> module is slow,
+but Zip files can also be used just for storage,
+handing back the data without extracting it.
+And since we have compression/decompression implemented in Rust with <code>niffler</code>,
+that's what the zipped sourmash databases are:
+data is loaded and saved into the Zip file without using the Python module
+compression/decompression,
+and all the work is done before (or after) in the Rust side.</p>
+<p>This allows keeping the Zip file with similar sizes to the original TAR files we started with,
+but with very low overhead for decompression.
+For compression we opted for using Gzip level 1,
+which doesn't compress perfectly but also doesn't take much longer to run:</p>
+<table>
+<thead>
+<tr>
+<th>Level</th>
+<th>Size</th>
+<th>Time</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>0</td>
+<td>407 MB</td>
+<td>16s</td>
+</tr>
+<tr>
+<td>1</td>
+<td>252 MB</td>
+<td>21s</td>
+</tr>
+<tr>
+<td>5</td>
+<td>250 MB</td>
+<td>39s</td>
+</tr>
+<tr>
+<td>9</td>
+<td>246 MB</td>
+<td>1m48s</td>
+</tr>
+</tbody>
+</table>
+<p>In this table, <code>0</code> is without compression,
+while <code>9</code> is the best compression.
+The size difference from <code>1</code> to <code>9</code> is only 6 MB (~2% difference)
+but runs 5x faster,
+and it's only 30% slower than saving the uncompressed data.</p>
+<p>The last challenge was updating an existing Zip file.
+It's easy to support appending new data,
+but if any of the already existing data in the file changes
+(which happens when internal nodes change in the SBT,
+after a new dataset is inserted) then there is no easy way to replace the data in the Zip file.
+Worse,
+the Python <code>zipfile</code> will add the new data while keeping the old one around,
+leading to ginormous files over time<sup id="sf-sbt-zip-1-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sbt-zip-1" class="simple-footnote" title="The zipfile module does throw a UserWarning pointing that duplicated files were inserted, which is useful during development but generally doesn't show during regular usage...">1</a></sup>
+So, what to do?</p>
+<p>I ended up opting for dealing with the complexity and <a href="https://github.com/dib-lab/sourmash/pull/648/files#diff-a99b088adcc872e1b408fbdcca20ebebR110-R248">complicating the ZipStorage</a> implementation a bit,
+by keeping a buffer for new data.
+If it's a new file or it already exists but there are no insertions
+the buffer is ignored and all works as before.</p>
+<p>If the file exists and new data is inserted,
+then it is first stored in the buffer
+(where it might also replace a previous entry with the same name).
+In this case we also need to check the buffer when trying to load some data
+(because it might exist only in the buffer,
+and not in the original file).</p>
+<p>Finally,
+when the <code>ZipStorage</code> is closed it needs to verify if there are new items in the buffer.
+If not,
+it is safe just to close the original file.
+If there are new items but they were not present in the original file,
+then we can append the new data to the original file.
+The final case is if there are new items that were also in the original file,
+and in this case a new Zip file is created and all the content from buffer and
+original file are copied to it,
+prioritizing items from the buffer.
+The original file is replaced by the new Zip file.</p>
+<p>Turns out this worked quite well! And so the PR was merged =]</p>
+<h2>The future</h2>
+<p>Zipped databases open the possibility of distributing extra data that might be
+useful for some kinds of analysis.
+One thing we are already considering is adding <a href="https://github.com/dib-lab/sourmash/issues/969">taxonomy information</a>,
+let's see what else shows up.</p>
+<p>Having <code>Nodegraph</code> in Rust is also pretty exciting,
+because now we can change the internal representation for something that uses
+less memory (maybe using <a href="https://alexbowe.com/rrr/">RRR encoding</a>?),
+but more importantly:
+now they can also be used with Webassembly,
+which opens many possibilities for running not only <a href="https://blog.luizirber.org/2018/08/27/sourmash-wasm/">signature computation</a> but
+also <code>search</code> and <code>gather</code> in the browser,
+since now we have all the pieces to build it.</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://twitter.com/luizirber/status/1260031886744621059">Thread on Twitter</a></li>
+</ul><hr /><h2>Footnotes</h2><ol><li id="sf-sbt-zip-1"><p>The <code>zipfile</code> module does throw a <code>UserWarning</code> pointing that duplicated files were inserted,
+which is useful during development but generally doesn't show during regular usage... <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sbt-zip-1-back" class="simple-footnote-back">↩</a></p></li></ol>
+
+ luizirber
+ https://blog.luizirber.org/
+
+
+ Gabbleblotchits
+ Vogon Poetry, Computers and (some) biology
+
+ https://blog.luizirber.org/
+ 2025-04-17T02:14:15+00:00
+
+
+
+
+ Criando um CI de uma aplicação Django usando Github Actions
+
+ tag:pythonclub.com.br,2020-01-24:/django-ci-github-actions.html
+ 2020-01-24T15:10:00+00:00
+ <p>Fala pessoal, tudo bom?</p>
+<p>Nos vídeo abaixo vou mostrar como podemos configurar um CI de uma aplicação Django usando Github Actions.</p>
+<p><a class="reference external" href="https://www.youtube.com/watch?v=KpSlY8leYFY">https://www.youtube.com/watch?v=KpSlY8leYFY</a>.</p>
+<div class="youtube youtube-16x9"></div>
+
+ Lucas Magnum
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Oxidizing sourmash: PR walkthrough
+
+ tag:blog.luizirber.org,2020-01-10:/2020/01/10/sourmash-pr/
+ 2020-01-10T15:00:00+00:00
+ <p>sourmash 3 was released last week,
+finally landing the Rust backend.
+But, what changes when developing new features in sourmash?
+I was thinking about how to best document this process,
+and since <a href="https://github.com/dib-lab/sourmash/pull/826/files">PR #826</a> is a short example touching all the layers I decided to do a
+small walkthrough.</p>
+<p>Shall we?</p>
+<h2>The problem</h2>
+<p>The first step is describing the problem,
+and trying to convince reviewers (and yourself) that the changes bring enough benefits to justify a merge.
+This is the description I put in the PR:</p>
+<blockquote>
+<p>Calling <code>.add_hash()</code> on a MinHash sketch is fine,
+but if you're calling it all the time it's better to pass a list of hashes and call <code>.add_many()</code> instead.
+Before this PR <code>add_many</code> just called <code>add_hash</code> for each hash it was passed,
+but now it will pass the full list to Rust (and that's way faster).</p>
+<p>No changes for public APIs,
+and I changed the <code>_signatures</code> method in LCA to accumulate hashes for each sig first,
+and then set them all at once.
+This is way faster,
+but might use more intermediate memory (I'll evaluate this now).</p>
+</blockquote>
+<p>There are many details that sound like jargon for someone not familiar with the codebase,
+but if I write something too long I'll probably be wasting the reviewers time too.
+The benefit of a very detailed description is extending the knowledge for other people
+(not necessarily the maintainers),
+but that also takes effort that might be better allocated to solve other problems.
+Or, more realistically, putting out other fires =P</p>
+<p>Nonetheless,
+some points I like to add in PR descriptions:
+- why is there a problem with the current approach?
+- is this the minimal viable change, or is it trying to change too many things
+ at once? The former is way better, in general.
+- what are the trade-offs? This PR is using more memory to lower the runtime,
+ but I hadn't measure it yet when I opened it.
+- Not changing public APIs is always good to convince reviewers.
+ If the project follows a <a href="https://semver.org/">semantic versioning</a> scheme,
+ changes to the public APIs are major version bumps,
+ and that can brings other consequences for users.</p>
+<h2>Setting up for changing code</h2>
+<p>If this was a bug fix PR,
+the first thing I would do is write a new test triggering the bug,
+and then proceed to fix it in the code
+(Hmm, maybe that would be another good walkthrough?).
+But this PR is making performance claims ("it's going to be faster"),
+and that's a bit hard to codify in tests.
+<sup id="sf-sourmash-pr-1-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-1" class="simple-footnote" title="We do have https://asv.readthedocs.io/ set up for micro-benchmarks, and now that I think about it... I could have started by writing a benchmark for add_many, and then showing that it is faster. I will add this approach to the sourmash PR checklist =]">1</a></sup>
+Since it's also proposing to change a method (<code>_signatures</code> in LCA indices) that is better to benchmark with a real index (and not a toy example),
+I used the same data and command I run in <a href="https://github.com/luizirber/sourmash_resources">sourmash_resources</a> to check how memory consumption and runtime changed.
+For reference, this is the command: </p>
+<div class="highlight"><pre><span></span>sourmash search -o out.csv --scaled <span class="m">2000</span> -k <span class="m">51</span> HSMA33OT.fastq.gz.sig genbank-k51.lca.json.gz
+</pre></div>
+
+
+<p>I'm using the <code>benchmark</code> feature from <a href="https://snakemake.readthedocs.io/">snakemake</a> in <a href="https://github.com/luizirber/sourmash_resources/blob/83ea237397d242e48c9d95eb0d9f50ceb4ad95c7/Snakefile#L99L114">sourmash_resources</a> to
+track how much memory, runtime and I/O is used for each command (and version) of sourmash,
+and generate the plots in the README in that repo.
+That is fine for a high-level view ("what's the maximum memory used?"),
+but not so useful for digging into details ("what method is consuming most memory?").</p>
+<p>Another additional problem is the dual<sup id="sf-sourmash-pr-2-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-2" class="simple-footnote" title="or triple, if you count C">2</a></sup> language nature of sourmash,
+where we have Python calling into Rust code (via CFFI).
+There are great tools for measuring and profiling Python code,
+but they tend to not work with extension code...</p>
+<p>So, let's bring two of my favorite tools to help!</p>
+<h3>Memory profiling: heaptrack</h3>
+<p><a href="https://github.com/KDE/heaptrack">heaptrack</a> is a heap profiler, and I first heard about it from <a href="https://www.vincentprouillet.com/">Vincent Prouillet</a>.
+Its main advantage over other solutions (like valgrind's massif) is the low
+overhead and... how easy it is to use:
+just stick <code>heaptrack</code> in front of your command,
+and you're good to go!</p>
+<p>Example output:</p>
+<div class="highlight"><pre><span></span>$ heaptrack sourmash search -o out.csv --scaled <span class="m">2000</span> -k <span class="m">51</span> HSMA33OT.fastq.gz.sig genbank-k51.lca.json.gz
+
+heaptrack stats:
+ allocations: <span class="m">1379353</span>
+ leaked allocations: <span class="m">1660</span>
+ temporary allocations: <span class="m">168984</span>
+Heaptrack finished! Now run the following to investigate the data:
+
+ heaptrack --analyze heaptrack.sourmash.66565.gz
+</pre></div>
+
+
+<p><code>heaptrack --analyze</code> is a very nice graphical interface for analyzing the results,
+but for this PR I'm mostly focusing on the Summary page (and overall memory consumption).
+Tracking allocations in Python doesn't give many details,
+because it shows the CPython functions being called,
+but the ability to track into the extension code (Rust) allocations is amazing
+for finding bottlenecks (and memory leaks =P).
+<sup id="sf-sourmash-pr-3-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-3" class="simple-footnote" title="It would be super cool to have the unwinding code from py-spy in heaptrack, and be able to see exactly what Python methods/lines of code were calling the Rust parts...">3</a></sup></p>
+<h3>CPU profiling: py-spy</h3>
+<p>Just as other solutions exist for profiling memory,
+there are many for profiling CPU usage in Python,
+including <code>profile</code> and <code>cProfile</code> in the standard library.
+Again, the issue is being able to analyze extension code,
+and bringing the cannon (the <code>perf</code> command in Linux, for example) looses the
+benefit of tracking Python code properly (because we get back the CPython
+functions, not what you defined in your Python code).</p>
+<p>Enters <a href="https://github.com/benfred/py-spy">py-spy</a> by <a href="https://www.benfrederickson.com">Ben Frederickson</a>,
+based on the <a href="https://github.com/rbspy/rbspy">rbspy</a> project by <a href="https://jvns.ca">Julia Evans</a>.
+Both use a great idea:
+read the process maps for the interpreters and resolve the full stack trace information,
+with low overhead (because it uses sampling).
+<a href="https://github.com/benfred/py-spy">py-spy</a> also goes further and resolves <a href="https://www.benfrederickson.com/profiling-native-python-extensions-with-py-spy/">native Python extensions</a> stack traces,
+meaning we can get the complete picture all the way from the Python CLI to the
+Rust core library!<sup id="sf-sourmash-pr-4-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-4" class="simple-footnote" title="Even if py-spy doesn't talk explicitly about Rust, it works very very well, woohoo!">4</a></sup></p>
+<p><code>py-spy</code> is also easy to use:
+stick <code>py-spy record --output search.svg -n --</code> in front of the command,
+and it will generate a flamegraph in <code>search.svg</code>.
+The full command for this PR is</p>
+<div class="highlight"><pre><span></span>py-spy record --output search.svg -n -- sourmash search -o out.csv --scaled <span class="m">2000</span> -k <span class="m">51</span> HSMA.fastq.sig genbank-k51.lca.json.gz
+</pre></div>
+
+
+<h2>Show me the code!</h2>
+<p>OK, OK, sheesh. But it's worth repeating: the code is important, but there are
+many other aspects that are just as important =]</p>
+<h3>Replacing <code>add_hash</code> calls with one <code>add_many</code></h3>
+<p>Let's start at the <a href="https://github.com/dib-lab/sourmash/pull/826/files#diff-adf06d14c535d5b22da9fb3862e4a487"><code>_signatures()</code></a> method on LCA indices.
+This is the original method:</p>
+<div class="highlight"><pre><span></span><span class="nd">@cached_property</span>
+<span class="k">def</span> <span class="nf">_signatures</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="s2">"Create a _signatures member dictionary that contains {idx: minhash}."</span>
+ <span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">MinHash</span>
+
+ <span class="n">minhash</span> <span class="o">=</span> <span class="n">MinHash</span><span class="p">(</span><span class="n">n</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">ksize</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">ksize</span><span class="p">,</span> <span class="n">scaled</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">scaled</span><span class="p">)</span>
+
+ <span class="n">debug</span><span class="p">(</span><span class="s1">'creating signatures for LCA DB...'</span><span class="p">)</span>
+ <span class="n">sigd</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="n">minhash</span><span class="o">.</span><span class="n">copy_and_clear</span><span class="p">)</span>
+
+ <span class="k">for</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">hashval_to_idx</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="k">for</span> <span class="n">vv</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span>
+ <span class="n">sigd</span><span class="p">[</span><span class="n">vv</span><span class="p">]</span><span class="o">.</span><span class="n">add_hash</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
+
+ <span class="n">debug</span><span class="p">(</span><span class="s1">'=> </span><span class="si">{}</span><span class="s1"> signatures!'</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">sigd</span><span class="p">))</span>
+ <span class="k">return</span> <span class="n">sigd</span>
+</pre></div>
+
+
+<p><code>sigd[vv].add_hash(k)</code> is the culprit.
+Each call to <code>.add_hash</code> has to go thru CFFI to reach the extension code,
+and the overhead is significant.
+It is a similar situation to accessing array elements in NumPy:
+it works,
+but it is way slower than using operations that avoid crossing from Python to
+the extension code.
+What we want to do instead is call <code>.add_many(hashes)</code>,
+which takes a list of hashes and process it entirely in Rust
+(ideally. We will get there).</p>
+<p>But, to have a list of hashes, there is another issue with this code.</p>
+<div class="highlight"><pre><span></span><span class="k">for</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">hashval_to_idx</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="k">for</span> <span class="n">vv</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span>
+ <span class="n">sigd</span><span class="p">[</span><span class="n">vv</span><span class="p">]</span><span class="o">.</span><span class="n">add_hash</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
+</pre></div>
+
+
+<p>There are two nested for loops,
+and <code>add_hash</code> is being called with values from the inner loop.
+So... we don't have the list of hashes beforehand.</p>
+<p>But we can change the code a bit to save the hashes for each signature
+in a temporary list,
+and then call <code>add_many</code> on the temporary list.
+Like this:</p>
+<div class="highlight"><pre><span></span><span class="n">temp_vals</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span>
+
+<span class="k">for</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">hashval_to_idx</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="k">for</span> <span class="n">vv</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span>
+ <span class="n">temp_vals</span><span class="p">[</span><span class="n">vv</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
+
+<span class="k">for</span> <span class="n">sig</span><span class="p">,</span> <span class="n">vals</span> <span class="ow">in</span> <span class="n">temp_vals</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="n">sigd</span><span class="p">[</span><span class="n">sig</span><span class="p">]</span><span class="o">.</span><span class="n">add_many</span><span class="p">(</span><span class="n">vals</span><span class="p">)</span>
+</pre></div>
+
+
+<p>There is a trade-off here:
+if we save the hashes in temporary lists,
+will the memory consumption be so high that the runtime gains of calling
+<code>add_many</code> in these temporary lists be cancelled?</p>
+<p>Time to measure it =]</p>
+<table>
+<thead>
+<tr>
+<th align="left">version</th>
+<th align="left">mem</th>
+<th align="left">time</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td align="left">original</td>
+<td align="left">1.5 GB</td>
+<td align="left">160s</td>
+</tr>
+<tr>
+<td align="left"><code>list</code></td>
+<td align="left">1.7GB</td>
+<td align="left">173s</td>
+</tr>
+</tbody>
+</table>
+<p>Wait, it got worse?!?! Building temporary lists only takes time and memory,
+and bring no benefits!</p>
+<p>This mystery goes away when you look at the <a href="https://github.com/dib-lab/sourmash/pull/826/files#diff-2f53b2a5be4083c39a0275847c87f88fR190">add_many method</a>:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">add_many</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">hashes</span><span class="p">):</span>
+ <span class="s2">"Add many hashes in at once."</span>
+ <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">hashes</span><span class="p">,</span> <span class="n">MinHash</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_methodcall</span><span class="p">(</span><span class="n">lib</span><span class="o">.</span><span class="n">kmerminhash_add_from</span><span class="p">,</span> <span class="n">hashes</span><span class="o">.</span><span class="n">_objptr</span><span class="p">)</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="k">for</span> <span class="nb">hash</span> <span class="ow">in</span> <span class="n">hashes</span><span class="p">:</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_methodcall</span><span class="p">(</span><span class="n">lib</span><span class="o">.</span><span class="n">kmerminhash_add_hash</span><span class="p">,</span> <span class="nb">hash</span><span class="p">)</span>
+</pre></div>
+
+
+<p>The first check in the <code>if</code> statement is a shortcut for adding hashes from
+another <code>MinHash</code>, so let's focus on <code>else</code> part...
+And turns out that <code>add_many</code> is lying!
+It doesn't process the <code>hashes</code> in the Rust extension,
+but just loops and call <code>add_hash</code> for each <code>hash</code> in the list.
+That's not going to be any faster than what we were doing in <code>_signatures</code>.</p>
+<p>Time to fix <code>add_many</code>!</p>
+<h3>Oxidizing <code>add_many</code></h3>
+<p>The idea is to change this loop in <code>add_many</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">for</span> <span class="nb">hash</span> <span class="ow">in</span> <span class="n">hashes</span><span class="p">:</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_methodcall</span><span class="p">(</span><span class="n">lib</span><span class="o">.</span><span class="n">kmerminhash_add_hash</span><span class="p">,</span> <span class="nb">hash</span><span class="p">)</span>
+</pre></div>
+
+
+<p>with a call to a Rust extension function:</p>
+<div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">_methodcall</span><span class="p">(</span><span class="n">lib</span><span class="o">.</span><span class="n">kmerminhash_add_many</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">hashes</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">hashes</span><span class="p">))</span>
+</pre></div>
+
+
+<p><code>self._methodcall</code> is a convenience method defined in <a href="https://github.com/dib-lab/sourmash/blob/c6cbdf0398ef836797492e13371a38373c544ae1/sourmash/utils.py#L24">RustObject</a>
+which translates a method-like call into a function call,
+since our C layer only has functions.
+This is the C prototype for this function:</p>
+<div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">kmerminhash_add_many</span><span class="p">(</span><span class="w"></span>
+<span class="w"> </span><span class="n">KmerMinHash</span><span class="w"> </span><span class="o">*</span><span class="n">ptr</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">uint64_t</span><span class="w"> </span><span class="o">*</span><span class="n">hashes_ptr</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="kt">uintptr_t</span><span class="w"> </span><span class="n">insize</span><span class="w"></span>
+<span class="w"> </span><span class="p">);</span><span class="w"></span>
+</pre></div>
+
+
+<p>You can almost read it as a Python method declaration,
+where <code>KmerMinHash *ptr</code> means the same as the <code>self</code> in Python methods.
+The other two arguments are a common idiom when passing pointers to data in C,
+with <code>insize</code> being how many elements we have in the list.
+<sup id="sf-sourmash-pr-5-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-5" class="simple-footnote" title="Let's not talk about lack of array bounds checks in C...">5</a></sup>.
+<code>CFFI</code> is very good at converting Python lists into pointers of a specific type,
+as long as the type is of a primitive type
+(<code>uint64_t</code> in our case, since each hash is a 64-bit unsigned integer number).</p>
+<p>And the Rust code with the implementation of the function:</p>
+<div class="highlight"><pre><span></span><span class="n">ffi_fn</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">kmerminhash_add_many</span><span class="p">(</span><span class="w"></span>
+<span class="w"> </span><span class="n">ptr</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">KmerMinHash</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">hashes_ptr</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="kt">u64</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">insize</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="p">)</span><span class="w"> </span>-> <span class="nb">Result</span><span class="o"><</span><span class="p">()</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">mh</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="o">!</span><span class="n">ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">());</span><span class="w"></span>
+<span class="w"> </span><span class="o">&</span><span class="k">mut</span><span class="w"> </span><span class="o">*</span><span class="n">ptr</span><span class="w"></span>
+<span class="w"> </span><span class="p">};</span><span class="w"></span>
+
+<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">hashes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="o">!</span><span class="n">hashes_ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">());</span><span class="w"></span>
+<span class="w"> </span><span class="n">slice</span>::<span class="n">from_raw_parts</span><span class="p">(</span><span class="n">hashes_ptr</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u64</span><span class="p">,</span><span class="w"> </span><span class="n">insize</span><span class="p">)</span><span class="w"></span>
+<span class="w"> </span><span class="p">};</span><span class="w"></span>
+
+<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">hash</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">hashes</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="n">mh</span><span class="p">.</span><span class="n">add_hash</span><span class="p">(</span><span class="o">*</span><span class="n">hash</span><span class="p">);</span><span class="w"></span>
+<span class="w"> </span><span class="p">}</span><span class="w"></span>
+
+<span class="w"> </span><span class="nb">Ok</span><span class="p">(())</span><span class="w"></span>
+<span class="p">}</span><span class="w"></span>
+<span class="p">}</span><span class="w"></span>
+</pre></div>
+
+
+<p>Let's break what's happening here into smaller pieces.
+Starting with the function signature:</p>
+<div class="highlight"><pre><span></span><span class="n">ffi_fn</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">kmerminhash_add_many</span><span class="p">(</span><span class="w"></span>
+<span class="w"> </span><span class="n">ptr</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">KmerMinHash</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">hashes_ptr</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="kt">u64</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">insize</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="p">)</span><span class="w"> </span>-> <span class="nb">Result</span><span class="o"><</span><span class="p">()</span><span class="o">></span><span class="w"></span>
+</pre></div>
+
+
+<p>The weird <code>ffi_fn! {}</code> syntax around the function is a macro in Rust:
+it changes the final generated code to convert the return value (<code>Result<()></code>) into something that is valid C code (in this case, <code>void</code>).
+What happens if there is an error, then?
+The Rust extension has code for passing back an error code and message to Python,
+as well as capturing panics (when things go horrible bad and the program can't recover)
+in a way that Python can then deal with (raising exceptions and cleaning up).
+It also sets the <code>#[no_mangle]</code> attribute in the function,
+meaning that the final name of the function will follow C semantics (instead of Rust semantics),
+and can be called more easily from C and other languages.
+This <code>ffi_fn!</code> macro comes from <a href="https://github.com/getsentry/symbolic">symbolic</a>,
+a big influence on the design of the Python/Rust bridge in sourmash.</p>
+<p><code>unsafe</code> is the keyword in Rust to disable some checks in the code to allow
+potentially dangerous things (like dereferencing a pointer),
+and it is required to interact with C code.
+<code>unsafe</code> doesn't mean that the code is always unsafe to use:
+it's up to whoever is calling this to verify that valid data is being passed and invariants are being preserved.</p>
+<p>If we remove the <code>ffi_fn!</code> macro and the <code>unsafe</code> keyword,
+we have</p>
+<div class="highlight"><pre><span></span><span class="k">fn</span> <span class="nf">kmerminhash_add_many</span><span class="p">(</span><span class="w"></span>
+<span class="w"> </span><span class="n">ptr</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">KmerMinHash</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">hashes_ptr</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="kt">u64</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">insize</span>: <span class="kt">usize</span>
+ <span class="p">);</span><span class="w"></span>
+</pre></div>
+
+
+<p>At this point we can pretty much map between Rust and the C function prototype:</p>
+<div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">kmerminhash_add_many</span><span class="p">(</span><span class="w"></span>
+<span class="w"> </span><span class="n">KmerMinHash</span><span class="w"> </span><span class="o">*</span><span class="n">ptr</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">uint64_t</span><span class="w"> </span><span class="o">*</span><span class="n">hashes_ptr</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="kt">uintptr_t</span><span class="w"> </span><span class="n">insize</span><span class="w"></span>
+<span class="w"> </span><span class="p">);</span><span class="w"></span>
+</pre></div>
+
+
+<p>Some interesting points:</p>
+<ul>
+<li>We use <code>fn</code> to declare a function in Rust.</li>
+<li>The type of an argument comes after the name of the argument in Rust,
+ while it's the other way around in C.
+ Same for the return type (it is omitted in the Rust function, which means it
+ is <code>-> ()</code>, equivalent to a <code>void</code> return type in C).</li>
+<li>In Rust everything is <strong>immutable</strong> by default, so we need to say that we want
+ a mutable pointer to a <code>KmerMinHash</code> item: <code>*mut KmerMinHash</code>).
+ In C everything is mutable by default.</li>
+<li><code>u64</code> in Rust -> <code>uint64_t</code> in C</li>
+<li><code>usize</code> in Rust -> <code>uintptr_t</code> in C</li>
+</ul>
+<p>Let's check the implementation of the function now.
+We start by converting the <code>ptr</code> argument (a raw pointer to a <code>KmerMinHash</code> struct)
+into a regular Rust struct:</p>
+<div class="highlight"><pre><span></span><span class="kd">let</span><span class="w"> </span><span class="n">mh</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="o">!</span><span class="n">ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">());</span><span class="w"></span>
+<span class="w"> </span><span class="o">&</span><span class="k">mut</span><span class="w"> </span><span class="o">*</span><span class="n">ptr</span><span class="w"></span>
+<span class="p">};</span><span class="w"></span>
+</pre></div>
+
+
+<p>This block is asserting that <code>ptr</code> is not a null pointer,
+and if so it dereferences it and store in a mutable reference.
+If it was a null pointer the <code>assert!</code> would panic (which might sound extreme,
+but is way better than continue running because dereferencing a null pointer is
+BAD).
+Note that functions always need all the types in arguments and return values,
+but for variables in the body of the function
+Rust can figure out types most of the time,
+so no need to specify them.</p>
+<p>The next block prepares our list of hashes for use:</p>
+<div class="highlight"><pre><span></span><span class="kd">let</span><span class="w"> </span><span class="n">hashes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="o">!</span><span class="n">hashes_ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">());</span><span class="w"></span>
+<span class="w"> </span><span class="n">slice</span>::<span class="n">from_raw_parts</span><span class="p">(</span><span class="n">hashes_ptr</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u64</span><span class="p">,</span><span class="w"> </span><span class="n">insize</span><span class="p">)</span><span class="w"></span>
+<span class="p">};</span><span class="w"></span>
+</pre></div>
+
+
+<p>We are again asserting that the <code>hashes_ptr</code> is not a null pointer,
+but instead of dereferencing the pointer like before we use it to create a <code>slice</code>,
+a dynamically-sized view into a contiguous sequence.
+The list we got from Python is a contiguous sequence of size <code>insize</code>,
+and the <code>slice::from_raw_parts</code> function creates a slice from a pointer to data and a size.</p>
+<p>Oh, and can you spot the bug?
+I created the slice using <code>*mut u64</code>,
+but the data is declared as <code>*const u64</code>.
+Because we are in an <code>unsafe</code> block Rust let me change the mutability,
+but I shouldn't be doing that,
+since we don't need to mutate the slice.
+Oops.</p>
+<p>Finally, let's add hashes to our MinHash!
+We need a <code>for</code> loop, and call <code>add_hash</code> for each <code>hash</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">for</span><span class="w"> </span><span class="n">hash</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">hashes</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="n">mh</span><span class="p">.</span><span class="n">add_hash</span><span class="p">(</span><span class="o">*</span><span class="n">hash</span><span class="p">);</span><span class="w"></span>
+<span class="p">}</span><span class="w"></span>
+
+<span class="nb">Ok</span><span class="p">(())</span><span class="w"></span>
+</pre></div>
+
+
+<p>We finish the function with <code>Ok(())</code> to indicate no errors occurred.</p>
+<p>Why is calling <code>add_hash</code> here faster than what we were doing before in Python?
+Rust can optimize these calls and generate very efficient native code,
+while Python is an interpreted language and most of the time don't have the same
+guarantees that Rust can leverage to generate the code.
+And, again,
+calling <code>add_hash</code> here doesn't need to cross FFI boundaries or,
+in fact,
+do any dynamic evaluation during runtime,
+because it is all statically analyzed during compilation.</p>
+<h2>Putting it all together</h2>
+<p>And... that's the PR code.
+There are some other unrelated changes that should have been in new PRs,
+but since they were so small it would be more work than necessary.
+OK, that's a lame excuse:
+it's confusing for reviewers to see these changes here,
+so avoid doing that if possible!</p>
+<p>But, did it work?</p>
+<table>
+<thead>
+<tr>
+<th align="left">version</th>
+<th align="left">mem</th>
+<th align="left">time</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td align="left">original</td>
+<td align="left">1.5 GB</td>
+<td align="left">160s</td>
+</tr>
+<tr>
+<td align="left"><code>list</code></td>
+<td align="left">1.7GB</td>
+<td align="left">73s</td>
+</tr>
+</tbody>
+</table>
+<p>We are using 200 MB of extra memory,
+but taking less than half the time it was taking before.
+I think this is a good trade-off,
+and so did the <a href="https://github.com/dib-lab/sourmash/pull/826#pullrequestreview-339020803">reviewer</a> and the PR was approved.</p>
+<p>Hopefully this was useful, 'til next time!</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/103461534713587975">Thread on Mastodon</a></li>
+<li><a href="https://twitter.com/luizirber/status/1215772245928235008">Thread on Twitter</a></li>
+<li><a href="https://lobste.rs/s/xnaugq/oxidizing_sourmash_pr_walkthrough">Lobste.rs submission</a></li>
+</ul>
+<h2>Bonus: <code>list</code> or <code>set</code>?</h2>
+<p>The first version of the PR used a <code>set</code> instead of a <code>list</code> to accumulate hashes.
+Since a <code>set</code> doesn't have repeated elements,
+this could potentially use less memory.
+The code:</p>
+<div class="highlight"><pre><span></span><span class="n">temp_vals</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">set</span><span class="p">)</span>
+
+<span class="k">for</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">hashval_to_idx</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="k">for</span> <span class="n">vv</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span>
+ <span class="n">temp_vals</span><span class="p">[</span><span class="n">vv</span><span class="p">]</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
+
+<span class="k">for</span> <span class="n">sig</span><span class="p">,</span> <span class="n">vals</span> <span class="ow">in</span> <span class="n">temp_vals</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="n">sigd</span><span class="p">[</span><span class="n">sig</span><span class="p">]</span><span class="o">.</span><span class="n">add_many</span><span class="p">(</span><span class="n">vals</span><span class="p">)</span>
+</pre></div>
+
+
+<p>The runtime was again half of the original,
+but...</p>
+<table>
+<thead>
+<tr>
+<th align="left">version</th>
+<th align="left">mem</th>
+<th align="left">time</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td align="left">original</td>
+<td align="left">1.5 GB</td>
+<td align="left">160s</td>
+</tr>
+<tr>
+<td align="left"><code>set</code></td>
+<td align="left">3.8GB</td>
+<td align="left">80s</td>
+</tr>
+<tr>
+<td align="left"><code>list</code></td>
+<td align="left">1.7GB</td>
+<td align="left">73s</td>
+</tr>
+</tbody>
+</table>
+<p>... memory consumption was almost 2.5 times the original! WAT</p>
+<p>The culprit this time? The new <code>kmerminhash_add_many</code> call in the <code>add_many</code>
+method.
+This one:</p>
+<div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">_methodcall</span><span class="p">(</span><span class="n">lib</span><span class="o">.</span><span class="n">kmerminhash_add_many</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">hashes</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">hashes</span><span class="p">))</span>
+</pre></div>
+
+
+<p><code>CFFI</code> doesn't know how to convert a <code>set</code> into something that C understands,
+so we need to call <code>list(hashes)</code> to convert it into a list.
+Since Python (and <code>CFFI</code>) can't know if the data is going to be used later
+<sup id="sf-sourmash-pr-6-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-6" class="simple-footnote" title="something that the memory ownership model in Rust does, BTW">6</a></sup>
+it needs to keep it around
+(and be eventually deallocated by the garbage collector).
+And that's how we get at least double the memory being allocated...</p>
+<p>There is another lesson here.
+If we look at the <code>for</code> loop again:</p>
+<div class="highlight"><pre><span></span><span class="k">for</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">hashval_to_idx</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="k">for</span> <span class="n">vv</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span>
+ <span class="n">temp_vals</span><span class="p">[</span><span class="n">vv</span><span class="p">]</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
+</pre></div>
+
+
+<p>each <code>k</code> is already unique because they are keys in the <code>hashval_to_idx</code> dictionary,
+so the initial assumption
+(that a <code>set</code> might save memory because it doesn't have repeated elements)
+is... irrelevant for the problem =]</p><hr /><h2>Footnotes</h2><ol><li id="sf-sourmash-pr-1"><p>We do have https://asv.readthedocs.io/ set up for micro-benchmarks,
+and now that I think about it...
+I could have started by writing a benchmark for <code>add_many</code>,
+and then showing that it is faster.
+I will add this approach to the sourmash PR checklist =] <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-1-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-pr-2"><p>or triple, if you count C <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-2-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-pr-3"><p>It would be super cool to have the unwinding code from py-spy in heaptrack,
+and be able to see exactly what Python methods/lines of code were calling the
+Rust parts... <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-3-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-pr-4"><p>Even if py-spy doesn't talk explicitly about Rust,
+it works very very well, woohoo! <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-4-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-pr-5"><p>Let's not talk about lack of array bounds checks in C... <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-5-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-pr-6"><p>something that the memory ownership model in Rust does, BTW <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-6-back" class="simple-footnote-back">↩</a></p></li></ol>
+
+ luizirber
+ https://blog.luizirber.org/
+
+
+ Gabbleblotchits
+ Vogon Poetry, Computers and (some) biology
+
+ https://blog.luizirber.org/
+ 2025-04-17T02:14:15+00:00
+
+
+
+
+ Interoperability #rust2020
+
+ tag:blog.luizirber.org,2019-12-01:/2019/12/01/rust-2020/
+ 2019-12-01T15:00:00+00:00
+ <p>In January I wrote a <a href="https://blog.luizirber.org/2019/01/05/rust-2019/">post</a> for the Rust 2019 call for blogs.
+The <a href="https://blog.rust-lang.org/2019/10/29/A-call-for-blogs-2020.html">2020 call</a> is aiming for an RFC and roadmap earlier this time,
+so here is my 2020 post =]</p>
+<h3>Last call review: what happened?</h3>
+<h4>An attribute proc-macro like <code>#[wasm_bindgen]</code> but for FFI</h4>
+<p>This sort of happened... because WebAssembly is growing =]</p>
+<p>I was very excited when <a href="https://hacks.mozilla.org/2019/08/webassembly-interface-types/">Interface Types</a> showed up in August,
+and while it is still very experimental it is moving fast and bringing saner
+paths for interoperability than raw C FFIs.
+David Beazley even point this at the end of his <a href="https://www.youtube.com/watch?v=r-A78RgMhZU">PyCon India keynote</a>,
+talking about how easy is to get information out of a WebAssembly module
+compared to what had to be done for SWIG.</p>
+<p>This doesn't solve the problem where strict C compatibility is required,
+or for platforms where a WebAssembly runtime is not available,
+but I think it is a great solution for scientific software
+(or, at least, for my use cases =]).</p>
+<h4>"More -sys and Rust-like crates for interoperability with the larger ecosystems" and "More (bioinformatics) tools using Rust!"</h4>
+<p>I did some of those this year (<a href="https://crates.io/crates/bbhash-sys">bbhash-sys</a> and <a href="https://crates.io/crates/mqf">mqf</a>),
+and also found some great crates to use in my projects.
+Rust is picking up steam in bioinformatics,
+being used as the primary choice for high quality software
+(like <a href="https://varlociraptor.github.io/">varlociraptor</a>,
+or the many coming from <a href="https://github.com/10XGenomics/">10X Genomics</a>)
+but it is still somewhat hard to find more details
+(I mostly find it on Twitter,
+and sometime Google Scholar alerts).
+It would be great to start bringing this info together,
+which leads to...</p>
+<h4>"A place to find other scientists?"</h4>
+<p>Hey, this one happened! <a href="https://twitter.com/algo_luca/status/1081966759048028162">Luca Palmieri</a> started a conversation on <a href="https://www.reddit.com/r/rust/comments/ae77gt/scientific_computingmachine_learning_do_we_want_a/">reddit</a> and
+the <a href="https://discord.gg/EXTSq4v">#science-and-ai</a> Discord channel on the Rust community server was born!
+I think it works pretty well,
+and Luca also has being doing a great job running <a href="https://github.com/LukeMathWalker/ndarray-koans">workshops</a>
+and guiding the conversation around <a href="https://github.com/rust-ml/discussion">rust-ml</a>.</p>
+<h2>Rust 2021: Interoperability</h2>
+<p>Rust is amazing because it is very good at bringing many concepts and ideas that
+seem contradictory at first,
+but can really shine when <a href="https://rust-lang.github.io/rustconf-2018-keynote/#127">synthesized</a>.
+But can we share this combined wisdom and also improve the situation in other
+places too?
+Despite the "Rewrite it in Rust" meme,
+increased interoperability is something that is already driving a lot of the
+best aspects of Rust:</p>
+<ul>
+<li>
+<p>Interoperability with other languages: as I said before,
+ with WebAssembly (and Rust being having the best toolchain for it)
+ there is a clear route to achieve this,
+ but it will not replace all the software that already exist and can benefit
+ from FFI and C compatibility.
+ Bringing together developers from the many language specific binding
+ generators (<a href="https://github.com/tildeio/helix">helix</a>, <a href="https://github.com/neon-bindings/neon">neon</a>, <a href="https://github.com/rusterlium/rustler">rustler</a>, <a href="https://github.com/PyO3/pyo3">PyO3</a>...) and figuring out what's missing from
+ them (or what is the common parts that can be shared) also seems productive.</p>
+</li>
+<li>
+<p>Interoperability with new and unexplored domains.
+ I think Rust benefits enormously from not focusing only in one domain,
+ and choosing to prioritize CLI, WebAssembly, Networking and Embedded is a good
+ subset to start tackling problems,
+ but how to guide other domains to also use Rust and come up with new
+ contributors and expose missing pieces of the larger picture?</p>
+</li>
+</ul>
+<p>Another point extremely close to interoperability is training.
+A great way to interoperate with other languages and domains is having good
+documentation and material from transitioning into Rust without having to figure
+everything at once.
+Rust documentation is already amazing,
+especially considering the many books published by each working group.
+But... there is a gap on the transitions,
+both from understanding the basics of the language and using it,
+to the progression from beginner to intermediate and expert.</p>
+<p>I see good resources for <a href="https://github.com/yoshuawuyts/rust-for-js-people">JavaScript</a> and <a href="https://github.com/rochacbruno/py2rs">Python</a> developers,
+but we are still covering a pretty small niche:
+programmers curious enough to go learn another language,
+or looking for solutions for problems in their current language.</p>
+<p>Can we bring more people into Rust?
+<a href="https://rustbridge.com/">RustBridge</a> is obviously the reference here,
+but there is space for much,
+much more.
+Using Rust in <a href="https://carpentries.org/">The Carpentries</a> lessons?
+Creating <code>RustOpenSci</code>,
+mirroring the communities of practice of <a href="https://ropensci.org/about/">rOpenSci</a> and <a href="https://www.pyopensci.org/">pyOpenSci</a>?</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/103236549475802733">Thread on Mastodon</a></li>
+<li><a href="https://twitter.com/luizirber/status/1201373423592562690">Thread on Twitter</a></li>
+</ul>
+
+ luizirber
+ https://blog.luizirber.org/
+
+
+ Gabbleblotchits
+ Vogon Poetry, Computers and (some) biology
+
+ https://blog.luizirber.org/
+ 2025-04-17T02:14:15+00:00
+
+
+
+
+ Criando dicts a partir de outros dicts
+
+ tag:pythonclub.com.br,2019-10-01:/crie_dict-a-partir-de-outros-dicts.html
+ 2019-10-01T23:20:29+00:00
+ <p>Neste tutorial, será abordado o processo de criação de um <em>dict</em> ou dicionário, a partir de um ou mais <em>dicts</em> em Python. </p>
+<p>Como já é de costume da linguagem, isso pode ser feito de várias maneiras diferentes.</p>
+<h2>Abordagem inicial</h2>
+<p>Pra começar, vamos supor que temos os seguintes dicionários:</p>
+<div class="highlight"><pre><span></span><span class="n">dict_1</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'a'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
+ <span class="s1">'b'</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
+<span class="p">}</span>
+
+<span class="n">dict_2</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'b'</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
+ <span class="s1">'c'</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>Como exemplo, vamos criar um novo dicionário chamado <strong>new_dict</strong> com os valores de <strong>dict_1</strong> e <strong>dict_2</strong> logo acima. Uma abordagem bem conhecida é utilizar o método <em>update</em>.</p>
+<div class="highlight"><pre><span></span><span class="n">new_dict</span> <span class="o">=</span> <span class="p">{}</span>
+
+<span class="n">new_dcit</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">dict_1</span><span class="p">)</span>
+<span class="n">new_dcit</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">dict_2</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Assim, temos que <strong>new_dict</strong> será:</p>
+<div class="highlight"><pre><span></span><span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">)</span>
+<span class="p">{</span>
+ <span class="s1">'a'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
+ <span class="s1">'b'</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
+ <span class="s1">'c'</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>Este método funciona bem, porém temos de chamar o método <em>update</em> para cada <em>dict</em> que desejamos mesclar em <strong>new_dict</strong>. Não seria interessante se fosse possível passar todos os <em>dicts</em> necessários já na inicialização de <strong>new_dict</strong>?</p>
+<h3>Novidades do Python 3</h3>
+<p>O Python 3 introduziu uma maneira bem interessante de se fazer isso, utilizando os operadores <code>**</code>.</p>
+<div class="highlight"><pre><span></span><span class="n">new_dict</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="o">**</span><span class="n">dict_1</span><span class="p">,</span>
+ <span class="o">**</span><span class="n">dict_2</span><span class="p">,</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>Assim, de maneira semelhante ao exemplo anterior, temos que <strong>new_dict</strong> será :</p>
+<div class="highlight"><pre><span></span><span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'a'</span><span class="p">])</span>
+<span class="mi">1</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'b'</span><span class="p">])</span>
+<span class="mi">3</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'c'</span><span class="p">])</span>
+<span class="mi">4</span>
+</pre></div>
+
+
+<h2>Cópia real de <em>dicts</em></h2>
+<p>Ao utilizamos o procedimento de inicialização acima, devemos tomar conseiderar alguns fatores. Apenas os valores do primeiro nível serão realmente duplicados no novo dicionário. Como exemplo, vamos alterar uma chave presente em ambos os <em>dicts</em> e verificar se as mesmas possuem o mesmo valor:</p>
+<div class="highlight"><pre><span></span><span class="o">>></span> <span class="n">dict_1</span><span class="p">[</span><span class="s1">'a'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">10</span>
+<span class="o">>></span> <span class="n">new_dict</span><span class="p">[</span><span class="s1">'a'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">11</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">dict_1</span><span class="p">[</span><span class="s1">'a'</span><span class="p">])</span>
+<span class="mi">10</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'a'</span><span class="p">])</span>
+<span class="mi">11</span>
+</pre></div>
+
+
+<p>Porém isso muda quando um dos valores de <strong>dict_1</strong> for uma <em>list</em>, outro <em>dict</em> ou algum objeto complexo. Por exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">dict_3</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'a'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
+ <span class="s1">'b'</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
+ <span class="s1">'c'</span><span class="p">:</span> <span class="p">{</span>
+ <span class="s1">'d'</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
+ <span class="p">}</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>e agora, vamos criar um novo <em>dict</em> a partir desse:</p>
+<div class="highlight"><pre><span></span><span class="n">new_dict</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="o">**</span><span class="n">dict_3</span><span class="p">,</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>Como no exemplo anterior, podemos imaginar que foi realizado uma cópia de todos os elementos de <strong>dict_3</strong>, porém isso não é totalmente verdade. O que realmente aconteceu é que foi feita uma cópia <em>superficial</em> dos valores de <strong>dict_3</strong>, ou seja, apenas os valores de <em>primeiro nível</em> foram duplicados. Observe o que acontece quando alteramos o valor do <em>dict</em> presente na chave <strong>c</strong>.</p>
+<div class="highlight"><pre><span></span><span class="o">>></span> <span class="n">new_dict</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">11</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">])</span>
+<span class="mi">11</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">dict_3</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">])</span>
+<span class="mi">11</span>
+<span class="c1"># valor anterior era 5</span>
+</pre></div>
+
+
+<p>No caso da chave <strong>c</strong>, ela contem uma referência para outra estrutura de dados (um <em>dict</em>, no caso). Quando alteramos algum valor de <strong>dict_3['c']</strong>, isso reflete em todos os <em>dict</em> que foram inicializados com <strong>dict_3</strong>. Em outras palavras, deve-se ter cuidado ao inicializar um <em>dict</em> a partir de outros <strong>dicts</strong> quando os mesmos possuírem valores complexos, como <em>list</em>, <em>dict</em> ou outros objetos (os atributos deste objeto não serão duplicados).</p>
+<p>De modo a contornar este inconveniente, podemos utilizar o método <em>deepcopy</em> da <em>lib</em> nativa <a href="https://docs.python.org/2/library/copy.html">copy</a>. Agora, ao inicializarmos <strong>new_dict</strong>:</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">copy</span>
+
+<span class="n">dict_3</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'a'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
+ <span class="s1">'b'</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
+ <span class="s1">'c'</span><span class="p">:</span> <span class="p">{</span>
+ <span class="s1">'d'</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
+ <span class="p">}</span>
+<span class="p">}</span>
+
+<span class="n">new_dict</span> <span class="o">=</span> <span class="n">copy</span><span class="o">.</span><span class="n">deepcopy</span><span class="p">(</span><span class="n">dict_3</span><span class="p">)</span>
+</pre></div>
+
+
+<p>O método <em>deepcopy</em> realiza uma cópia recursiva de cada elemento de <strong>dict_3</strong>, resolvendo nosso problema. Veja mais um exemplo:</p>
+<div class="highlight"><pre><span></span><span class="o">>></span> <span class="n">new_dict</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">11</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">])</span>
+<span class="mi">11</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">dict_3</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">])</span>
+<span class="mi">5</span>
+<span class="c1"># valor não foi alterado</span>
+</pre></div>
+
+
+<h2>Conclusão</h2>
+<p>Este artigo tenta demonstrar de maneira simples a criação de <em>dicts</em>, utilizando os diversos recursos que a linguagem oferece bem como os prós e contras de cada abordagem. </p>
+<h2>Referências</h2>
+<p>Para mais detalhes e outros exemplos, deem uma olhada neste <em>post</em> do forum da Python Brasil <a href="https://groups.google.com/forum/#!topic/python-brasil/OhUqYQ32M7E">aqui</a>.</p>
+<p>É isso pessoal. Obrigado por ler!</p>
+
+ Michell Stuttgart
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Desbravando o pygame 5 - Movimento e Colisão
+
+ https://humberto.io/pt-br/blog/desbravando-o-pygame-5-movimento-e-colisao/
+ 2019-09-10T00:00:00+00:00
+ O movimento é uma característica que está presente na maioria dos jogos. Ao saltar entre plataformas, atirar contra a horda de inimigos, pilotar uma nave espacial e correr pelas estradas estamos exercendo movimento, interagindo com o ambiente do jogo, aplicando ações e causando reações.
+Neste capítulo iremos conhecer os conceitos básicos de movimentação de objetos na tela e sua interação com outros elementos através da detecção de colisão.
+Movimento Se você vem acompanhando esta série de postagens, teve um breve exemplo de movimentação na postagem sobre game loop, onde uma bola que se movimentava quicando pela tela foi implementada.
+
+ Humberto Rocha
+ https://humberto.io/pt-br/tags/python/
+
+
+ Python on Humberto Rocha
+ Recent content in Python on Humberto Rocha
+
+ https://humberto.io/pt-br/tags/python/index.xml
+ 2025-04-17T02:14:43+00:00
+
+
+
+
+ Publicando meu primeiro Jogo
+
+ https://humberto.io/pt-br/blog/publicando-meu-primeiro-jogo/
+ 2019-08-28T00:00:00+00:00
+ Jogos sempre me conectam com tecnologia desde o início.
+Eu e meu pai montamos nosso primeiro computador (um Pentium 286) e a primeira coisa que eu me lembro de fazer, foi jogar os jogos de DOS como Prince of Persia e Lunar Lander. Eu aprendi vários comandos de CLI só para poder jogar os meus jogos favoritos.
+A paixão por jogar e fazer jogos sempre me acompanhou como um hobby. I tenho uma série de posts sobre pygame neste blog onde eu passo pelos conceitos básicos de desenvolvimento de jogos tentando explicar para pessoas que estejam iniciando seu aprendizado na área.
+
+ Humberto Rocha
+ https://humberto.io/pt-br/tags/python/
+
+
+ Python on Humberto Rocha
+ Recent content in Python on Humberto Rocha
+
+ https://humberto.io/pt-br/tags/python/index.xml
+ 2025-04-17T02:14:43+00:00
+
+
+
+
+ Tutorial Django 2.2
+
+ tag:pythonclub.com.br,2019-06-24:/tutorial-django-2.html
+ 2019-06-25T01:00:00+00:00
+ <p>Este tutorial é baseado no <strong>Intro to Django</strong> que fica na parte de baixo da página <a href="https://www.djangoproject.com/start/">start</a> do Django project.</p>
+<p>Até a data deste post o Django está na versão 2.2.2, e requer Python 3.</p>
+<h2>O que você precisa?</h2>
+<p>Python 3.6 ou superior, pip e virtualenv.</p>
+<p>Considere que você tenha instalado Python 3.6 ou superior, <a href="https://pip.readthedocs.io/en/latest/">pip</a> e <a href="https://virtualenv.pypa.io/en/latest/">virtualenv</a>.</p>
+<h2>Criando o ambiente</h2>
+<p>Crie uma pasta com o nome <code>django2-pythonclub</code></p>
+<div class="highlight"><pre><span></span>$ mkdir django2-pythonclub
+$ <span class="nb">cd</span> django2-pythonclub
+</pre></div>
+
+
+<p>A partir de agora vamos considerar esta como a nossa pasta principal.</p>
+<p>Considerando que você está usando <strong>Python 3</strong>, digite</p>
+<div class="highlight"><pre><span></span><span class="err">python3 -m venv .venv</span>
+</pre></div>
+
+
+<p>Lembre-se de colocar esta pasta no seu <code>.gitignore</code>, caso esteja usando.</p>
+<div class="highlight"><pre><span></span><span class="err">echo .venv >> .gitignore</span>
+</pre></div>
+
+
+<p>Depois ative o ambiente digitando</p>
+<div class="highlight"><pre><span></span><span class="err">source .venv/bin/activate</span>
+</pre></div>
+
+
+<blockquote>
+<p>Lembre-se, sempre quando você for mexer no projeto, tenha certeza de ter ativado o <code>virtualenv</code>, executando o comando <code>source .venv/bin/activate</code>. Você deve repetir esse comando toda a vez que você abrir um novo terminal.</p>
+</blockquote>
+<h2>Instalando Django 2.2.2</h2>
+<p>Basta digitar</p>
+<div class="highlight"><pre><span></span><span class="err">pip install django==2.2.2</span>
+</pre></div>
+
+
+<p>Dica: se você digitar <code>pip freeze</code> você verá a versão dos programas instalados.</p>
+<p>É recomendável que você atualize a versão do <code>pip</code></p>
+<div class="highlight"><pre><span></span><span class="err">pip install -U pip</span>
+</pre></div>
+
+
+<p>Se der erro então faça:</p>
+<div class="highlight"><pre><span></span><span class="err">python -m pip install --upgrade pip</span>
+</pre></div>
+
+
+<h2>Instalando mais dependências</h2>
+<p>Eu gosto de usar o <a href="https://django-extensions.readthedocs.io/en/latest/">django-extensions</a> e o <a href="https://github.com/jazzband/django-widget-tweaks">django-widget-tweaks</a>, então digite</p>
+<div class="highlight"><pre><span></span><span class="err">pip install django-extensions django-widget-tweaks python-decouple</span>
+</pre></div>
+
+
+<p><strong>Importante:</strong> você precisa criar um arquivo <code>requirements.txt</code> para instalações futuras do projeto em outro lugar.</p>
+<div class="highlight"><pre><span></span><span class="err">pip freeze > requirements.txt</span>
+</pre></div>
+
+
+<p>Este é o resultado do meu até o dia deste post:</p>
+<div class="highlight"><pre><span></span><span class="p">(.</span><span class="n">venv</span><span class="p">):</span><span class="err">$</span> <span class="n">cat</span> <span class="n">requirements</span><span class="p">.</span><span class="n">txt</span>
+
+<span class="n">django</span><span class="o">-</span><span class="n">extensions</span><span class="o">==</span><span class="mi">2</span><span class="p">.</span><span class="mi">1</span><span class="p">.</span><span class="mi">6</span>
+<span class="n">django</span><span class="o">-</span><span class="n">widget</span><span class="o">-</span><span class="n">tweaks</span><span class="o">==</span><span class="mi">1</span><span class="p">.</span><span class="mi">4</span><span class="p">.</span><span class="mi">3</span>
+<span class="n">python</span><span class="o">-</span><span class="n">decouple</span><span class="o">==</span><span class="mi">3</span><span class="p">.</span><span class="mi">1</span>
+<span class="n">pytz</span><span class="o">==</span><span class="mi">2018</span><span class="p">.</span><span class="mi">9</span>
+<span class="n">six</span><span class="o">==</span><span class="mi">1</span><span class="p">.</span><span class="mi">12</span><span class="p">.</span><span class="mi">0</span>
+</pre></div>
+
+
+<h2>Escondendo a SECRET_KEY e trabalhando com variáveis de ambiente</h2>
+<p>É muito importante que você não deixe sua SECRET_KEY exposta. Então remova-o imediatamente do seu settings.py ANTES mesmo do primeiro commit. Espero que você esteja usando Git.</p>
+<p>Vamos usar o <a href="https://github.com/henriquebastos/python-decouple">python-decouple</a> escrito por <a href="https://henriquebastos.net/">Henrique Bastos</a> para gerenciar nossas variáveis de ambiente. Repare que já instalamos ele logo acima.</p>
+<p>Em seguida você vai precisar criar um arquivo <code>.env</code>, para isso rode o comando a seguir, ele vai criar uma pasta contrib e dentro dele colocar um arquivo <code>env_gen.py</code></p>
+<div class="highlight"><pre><span></span><span class="err">if [ ! -d contrib ]; then mkdir contrib; fi; git clone https://gist.github.com/22626de522f5c045bc63acdb8fe67b24.git contrib/</span>
+<span class="err">rm -rf contrib/.git/ # remova a pasta .git que está dentro de contrib.</span>
+</pre></div>
+
+
+<p>Em seguida rode</p>
+<div class="highlight"><pre><span></span><span class="err">python contrib/env_gen.py</span>
+</pre></div>
+
+
+<p>que ele vai criar o arquivo <code>.env</code>.</p>
+<p>Supondo que você está versionando seu código com Git, é importante que você escreva isso dentro do seu arquivo <code>.gitignore</code>, faça direto pelo terminal</p>
+<div class="highlight"><pre><span></span><span class="err">echo .env >> .gitignore</span>
+<span class="err">echo .venv >> .gitignore</span>
+<span class="err">echo '*.sqlite3' >> .gitignore</span>
+</pre></div>
+
+
+<p>Pronto, agora você pode dar o primeiro commit.</p>
+<h2>Criando o projeto e a App</h2>
+<p>Para criar o projeto digite</p>
+<div class="highlight"><pre><span></span>$ django-admin startproject myproject .
+</pre></div>
+
+
+<p>repare no ponto no final do comando, isto permite que o arquivo <code>manage.py</code> fique nesta mesma pasta <em>django2-pythonclub</em> .</p>
+<p>Agora vamos criar a <em>app</em> <strong>bands</strong>, mas vamos deixar esta <em>app</em> dentro da pasta <em>myproject</em>. Então entre na pasta</p>
+<div class="highlight"><pre><span></span>$ <span class="nb">cd</span> myproject
+</pre></div>
+
+
+<p>e digite</p>
+<div class="highlight"><pre><span></span>$ python ../manage.py startapp bands
+</pre></div>
+
+
+<p>A intenção é que os arquivos tenham a seguinte hierarquia nas pastas:</p>
+<div class="highlight"><pre><span></span><span class="err">.</span>
+<span class="err">├── manage.py</span>
+<span class="err">├── myproject</span>
+<span class="err">│ ├── bands</span>
+<span class="err">│ │ ├── admin.py</span>
+<span class="err">│ │ ├── apps.py</span>
+<span class="err">│ │ ├── models.py</span>
+<span class="err">│ │ ├── tests.py</span>
+<span class="err">│ │ └── views.py</span>
+<span class="err">│ ├── settings.py</span>
+<span class="err">│ ├── urls.py</span>
+<span class="err">│ └── wsgi.py</span>
+<span class="err">└── requirements.txt</span>
+</pre></div>
+
+
+<p>Agora permaneça sempre na pasta <code>django2-pythonclub</code></p>
+<div class="highlight"><pre><span></span><span class="err">cd ..</span>
+</pre></div>
+
+
+<p>e digite</p>
+<div class="highlight"><pre><span></span>$ python manage.py migrate
+</pre></div>
+
+
+<p>para criar a primeira <em>migração</em> (isto cria o banco de dados SQLite), e depois rode a aplicação com</p>
+<div class="highlight"><pre><span></span>$ python manage.py runserver
+</pre></div>
+
+
+<p>e veja que a aplicação já está funcionando. Veja o endereço da url aqui</p>
+<div class="highlight"><pre><span></span><span class="err">Django version 2.2.2, using settings 'myproject.settings'</span>
+<span class="err">Starting development server at http://127.0.0.1:8000/</span>
+<span class="err">Quit the server with CONTROL-C.</span>
+</pre></div>
+
+
+<h2>Editando settings.py</h2>
+<p>Em <code>INSTALLED_APPS</code> acrescente as linhas abaixo.</p>
+<div class="highlight"><pre><span></span><span class="n">INSTALLED_APPS</span> <span class="o">=</span> <span class="p">(</span>
+ <span class="o">...</span>
+ <span class="s1">'widget_tweaks'</span><span class="p">,</span>
+ <span class="s1">'django_extensions'</span><span class="p">,</span>
+ <span class="s1">'myproject.bands'</span><span class="p">,</span>
+<span class="p">)</span>
+</pre></div>
+
+
+<p>E mude também o idioma.</p>
+<p><code>LANGUAGE_CODE = 'pt-br'</code></p>
+<p>E caso você queira o mesmo horário de Brasília-BR</p>
+<p><code>TIME_ZONE = 'America/Sao_Paulo'</code></p>
+<p>Já que falamos do python-decouple, precisamos de mais alguns ajustes</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">decouple</span> <span class="kn">import</span> <span class="n">config</span><span class="p">,</span> <span class="n">Csv</span>
+
+<span class="c1"># SECURITY WARNING: keep the secret key used in production secret!</span>
+<span class="n">SECRET_KEY</span> <span class="o">=</span> <span class="n">config</span><span class="p">(</span><span class="s1">'SECRET_KEY'</span><span class="p">)</span>
+
+<span class="c1"># SECURITY WARNING: don't run with debug turned on in production!</span>
+<span class="n">DEBUG</span> <span class="o">=</span> <span class="n">config</span><span class="p">(</span><span class="s1">'DEBUG'</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">cast</span><span class="o">=</span><span class="nb">bool</span><span class="p">)</span>
+
+<span class="n">ALLOWED_HOSTS</span> <span class="o">=</span> <span class="n">config</span><span class="p">(</span><span class="s1">'ALLOWED_HOSTS'</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="p">[],</span> <span class="n">cast</span><span class="o">=</span><span class="n">Csv</span><span class="p">())</span>
+</pre></div>
+
+
+<p>Veja que é importante manter sua SECRET_KEY bem guardada (em outro lugar).</p>
+<p>Então crie um arquivo <code>.env</code> e guarde sua SECRET_KEY dentro dele, exemplo:</p>
+<div class="highlight"><pre><span></span><span class="err">SECRET_KEY=your_secret_key</span>
+<span class="err">DEBUG=True</span>
+<span class="err">ALLOWED_HOSTS=127.0.0.1,.localhost</span>
+</pre></div>
+
+
+<h2>Editando models.py</h2>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>
+<span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">reverse_lazy</span>
+
+
+<span class="k">class</span> <span class="nc">Band</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
+
+ <span class="sd">"""A model of a rock band."""</span>
+ <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">200</span><span class="p">)</span>
+ <span class="n">can_rock</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">BooleanField</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
+
+ <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
+ <span class="n">ordering</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'name'</span><span class="p">,)</span>
+ <span class="n">verbose_name</span> <span class="o">=</span> <span class="s1">'band'</span>
+ <span class="n">verbose_name_plural</span> <span class="o">=</span> <span class="s1">'bands'</span>
+
+ <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span>
+
+ <span class="k">def</span> <span class="nf">get_absolute_url</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="c1"># retorna a url no formato /bands/1/</span>
+ <span class="k">return</span> <span class="n">reverse_lazy</span><span class="p">(</span><span class="s1">'band_detail'</span><span class="p">,</span> <span class="n">kwargs</span><span class="o">=</span><span class="p">{</span><span class="s1">'pk'</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pk</span><span class="p">})</span>
+
+ <span class="k">def</span> <span class="nf">get_members_count</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="c1"># count members by band</span>
+ <span class="c1"># conta os membros por banda</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">band</span><span class="o">.</span><span class="n">count</span><span class="p">()</span>
+
+
+<span class="k">class</span> <span class="nc">Member</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
+
+ <span class="sd">"""A model of a rock band member."""</span>
+ <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="s2">"Member's name"</span><span class="p">,</span> <span class="n">max_length</span><span class="o">=</span><span class="mi">200</span><span class="p">)</span>
+ <span class="n">instrument</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">choices</span><span class="o">=</span><span class="p">(</span>
+ <span class="p">(</span><span class="s1">'g'</span><span class="p">,</span> <span class="s2">"Guitar"</span><span class="p">),</span>
+ <span class="p">(</span><span class="s1">'b'</span><span class="p">,</span> <span class="s2">"Bass"</span><span class="p">),</span>
+ <span class="p">(</span><span class="s1">'d'</span><span class="p">,</span> <span class="s2">"Drums"</span><span class="p">),</span>
+ <span class="p">(</span><span class="s1">'v'</span><span class="p">,</span> <span class="s2">"Vocal"</span><span class="p">),</span>
+ <span class="p">(</span><span class="s1">'p'</span><span class="p">,</span> <span class="s2">"Piano"</span><span class="p">),</span>
+ <span class="p">),</span>
+ <span class="n">max_length</span><span class="o">=</span><span class="mi">1</span>
+ <span class="p">)</span>
+
+ <span class="n">band</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s2">"Band"</span><span class="p">,</span> <span class="n">related_name</span><span class="o">=</span><span class="s1">'band'</span><span class="p">,</span> <span class="n">on_delete</span><span class="o">=</span><span class="n">models</span><span class="o">.</span><span class="n">CASCADE</span><span class="p">)</span>
+
+ <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
+ <span class="n">ordering</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'name'</span><span class="p">,)</span>
+ <span class="n">verbose_name</span> <span class="o">=</span> <span class="s1">'member'</span>
+ <span class="n">verbose_name_plural</span> <span class="o">=</span> <span class="s1">'members'</span>
+
+ <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span>
+</pre></div>
+
+
+<p>Tem algumas coisas que eu não estou explicando aqui para o tutorial ficar curto, mas uma coisa importante é que, como nós editamos o models.py vamos precisar criar um arquivo de migração do novo modelo. Para isso digite</p>
+<div class="highlight"><pre><span></span><span class="err">python manage.py makemigrations</span>
+<span class="err">python manage.py migrate</span>
+</pre></div>
+
+
+<p>O primeiro comando cria o arquivo de migração e o segundo o executa, criando as tabelas no banco de dados.</p>
+<h2>Editando urls.py</h2>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">include</span><span class="p">,</span> <span class="n">path</span>
+<span class="kn">from</span> <span class="nn">myproject.bands</span> <span class="kn">import</span> <span class="n">views</span> <span class="k">as</span> <span class="n">v</span>
+<span class="kn">from</span> <span class="nn">django.contrib</span> <span class="kn">import</span> <span class="n">admin</span>
+
+<span class="n">app_name</span> <span class="o">=</span> <span class="s1">'bands'</span>
+
+<span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
+ <span class="n">path</span><span class="p">(</span><span class="s1">''</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">home</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'home'</span><span class="p">),</span>
+ <span class="c1"># path('bands/', v.band_list, name='bands'),</span>
+ <span class="c1"># path('bands/<int:pk>/', v.band_detail, name='band_detail'),</span>
+ <span class="c1"># path('bandform/', v.BandCreate.as_view(), name='band_form'),</span>
+ <span class="c1"># path('memberform/', v.MemberCreate.as_view(), name='member_form'),</span>
+ <span class="c1"># path('contact/', v.band_contact, name='contact'),</span>
+ <span class="c1"># path('protected/', v.protected_view, name='protected'),</span>
+ <span class="c1"># path('accounts/login/', v.message),</span>
+ <span class="n">path</span><span class="p">(</span><span class="s1">'admin/'</span><span class="p">,</span> <span class="n">admin</span><span class="o">.</span><span class="n">site</span><span class="o">.</span><span class="n">urls</span><span class="p">),</span>
+<span class="p">]</span>
+</pre></div>
+
+
+<p>Obs: deixei as demais urls comentada porque precisa da função em views.py para que cada url funcione. Descomente cada url somente depois que você tiver definido a função em classe em views.py a seguir.</p>
+<h2>Editando views.py</h2>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.shortcuts</span> <span class="kn">import</span> <span class="n">render</span>
+<span class="kn">from</span> <span class="nn">django.http</span> <span class="kn">import</span> <span class="n">HttpResponse</span>
+<span class="kn">from</span> <span class="nn">django.contrib.auth.decorators</span> <span class="kn">import</span> <span class="n">login_required</span>
+<span class="kn">from</span> <span class="nn">django.views.generic</span> <span class="kn">import</span> <span class="n">CreateView</span>
+<span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">reverse_lazy</span>
+<span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Band</span><span class="p">,</span> <span class="n">Member</span>
+<span class="c1"># from .forms import BandContactForm, BandForm, MemberForm</span>
+</pre></div>
+
+
+<p>Obs: Deixei a última linha comentada porque ainda não chegamos em forms.</p>
+<p>A função a seguir retorna um <strong>HttpResponse</strong>, ou seja, uma mensagem simples no navegador.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">home</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">HttpResponse</span><span class="p">(</span><span class="s1">'Welcome to the site!'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>A próxima função (use uma ou outra) renderiza um template, uma página html no navegador.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">home</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">'home.html'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>A função <code>band_list</code> retorna todas as bandas.</p>
+<p>Para fazer a <strong>busca</strong> por nome de banda usamos o comando <code>search = request.GET.get('search_box')</code>, onde <code>search_box</code> é o nome do campo no template <strong>band_list.html</strong>.</p>
+<p>E os nomes são retornados a partir do comando <code>bands = bands.filter(name__icontains=search)</code>. Onde <code>icontains</code> procura um texto que contém a palavra, ou seja, você pode digitar o nome incompleto (ignora maiúsculo ou minúsculo).</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">band_list</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="sd">""" A view of all bands. """</span>
+ <span class="n">bands</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">search</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">GET</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'search_box'</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">search</span><span class="p">:</span>
+ <span class="n">bands</span> <span class="o">=</span> <span class="n">bands</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">name__icontains</span><span class="o">=</span><span class="n">search</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">'bands/band_list.html'</span><span class="p">,</span> <span class="p">{</span><span class="s1">'bands'</span><span class="p">:</span> <span class="n">bands</span><span class="p">})</span>
+</pre></div>
+
+
+<p>Em urls.py pode descomentar a linha a seguir:</p>
+<div class="highlight"><pre><span></span><span class="n">path</span><span class="p">(</span><span class="s1">'bands/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">band_list</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'bands'</span><span class="p">),</span>
+</pre></div>
+
+
+<p>A função <code>band_contact</code> mostra como tratar um formulário na view. Esta função requer <code>BandContactForm</code>, explicado em forms.py.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">band_contact</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="sd">""" A example of form """</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'POST'</span><span class="p">:</span>
+ <span class="n">form</span> <span class="o">=</span> <span class="n">BandContactForm</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">POST</span><span class="p">)</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="n">form</span> <span class="o">=</span> <span class="n">BandContactForm</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">'bands/band_contact.html'</span><span class="p">,</span> <span class="p">{</span><span class="s1">'form'</span><span class="p">:</span> <span class="n">form</span><span class="p">})</span>
+</pre></div>
+
+
+<p>Em urls.py pode descomentar a linha a seguir:</p>
+<div class="highlight"><pre><span></span><span class="n">path</span><span class="p">(</span><span class="s1">'contact/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">band_contact</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'contact'</span><span class="p">),</span>
+</pre></div>
+
+
+<p>A função <code>band_detail</code> retorna todos os membros de cada banda, usando o <code>pk</code> da banda junto com o comando <code>filter</code> em members.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">band_detail</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">):</span>
+ <span class="sd">""" A view of all members by bands. """</span>
+ <span class="n">band</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">pk</span><span class="o">=</span><span class="n">pk</span><span class="p">)</span>
+ <span class="n">members</span> <span class="o">=</span> <span class="n">Member</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">band</span><span class="o">=</span><span class="n">band</span><span class="p">)</span>
+ <span class="n">context</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'members'</span><span class="p">:</span> <span class="n">members</span><span class="p">,</span> <span class="s1">'band'</span><span class="p">:</span> <span class="n">band</span><span class="p">}</span>
+ <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">'bands/band_detail.html'</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Em urls.py pode descomentar a linha a seguir:</p>
+<div class="highlight"><pre><span></span><span class="n">path</span><span class="p">(</span><span class="s1">'bands/<int:pk>/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">band_detail</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'band_detail'</span><span class="p">),</span>
+</pre></div>
+
+
+<p><code>BandCreate</code> e <code>MemberCreate</code> usam o <a href="https://docs.djangoproject.com/en/2.2/ref/class-based-views/">Class Based View</a> para tratar formulário de uma forma mais simplificada usando a classe <code>CreateView</code>. O <code>reverse_lazy</code> serve para tratar a url de retorno de página.</p>
+<p>As classes a seguir requerem <code>BandForm</code> e <code>MemberForm</code>, explicado em forms.py.</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">BandCreate</span><span class="p">(</span><span class="n">CreateView</span><span class="p">):</span>
+ <span class="n">model</span> <span class="o">=</span> <span class="n">Band</span>
+ <span class="n">form_class</span> <span class="o">=</span> <span class="n">BandForm</span>
+ <span class="n">template_name</span> <span class="o">=</span> <span class="s1">'bands/band_form.html'</span>
+ <span class="n">success_url</span> <span class="o">=</span> <span class="n">reverse_lazy</span><span class="p">(</span><span class="s1">'bands'</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">MemberCreate</span><span class="p">(</span><span class="n">CreateView</span><span class="p">):</span>
+ <span class="n">model</span> <span class="o">=</span> <span class="n">Member</span>
+ <span class="n">form_class</span> <span class="o">=</span> <span class="n">MemberForm</span>
+ <span class="n">template_name</span> <span class="o">=</span> <span class="s1">'bands/member_form.html'</span>
+ <span class="n">success_url</span> <span class="o">=</span> <span class="n">reverse_lazy</span><span class="p">(</span><span class="s1">'bands'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Em urls.py pode descomentar a linha a seguir:</p>
+<div class="highlight"><pre><span></span><span class="n">path</span><span class="p">(</span><span class="s1">'bandform/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">BandCreate</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span> <span class="n">name</span><span class="o">=</span><span class="s1">'band_form'</span><span class="p">),</span>
+<span class="n">path</span><span class="p">(</span><span class="s1">'memberform/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">MemberCreate</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span> <span class="n">name</span><span class="o">=</span><span class="s1">'member_form'</span><span class="p">),</span>
+</pre></div>
+
+
+<p>A próxima função requer que você entre numa página somente quando estiver logado.</p>
+<p><code>[@login_required](https://docs.djangoproject.com/en/2.2/topics/auth/default/#the-login-required-decorator)</code> é um <strong>decorator</strong>.</p>
+<p><code>login_url='/accounts/login/'</code> é página de erro, ou seja, quando o usuário não conseguiu logar.</p>
+<p>E <code>render(request, 'bands/protected.html',...</code> é página de sucesso.</p>
+<div class="highlight"><pre><span></span><span class="nd">@login_required</span><span class="p">(</span><span class="n">login_url</span><span class="o">=</span><span class="s1">'/accounts/login/'</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">protected_view</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="sd">""" A view that can only be accessed by logged-in users """</span>
+ <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">'bands/protected.html'</span><span class="p">,</span> <span class="p">{</span><span class="s1">'current_user'</span><span class="p">:</span> <span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="p">})</span>
+</pre></div>
+
+
+<p><code>HttpResponse</code> retorna uma mensagem simples no navegador sem a necessidade de um template.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">message</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="sd">""" Message if is not authenticated. Simple view! """</span>
+ <span class="k">return</span> <span class="n">HttpResponse</span><span class="p">(</span><span class="s1">'Access denied!'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Em urls.py pode descomentar a linha a seguir:</p>
+<div class="highlight"><pre><span></span><span class="n">path</span><span class="p">(</span><span class="s1">'protected/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">protected_view</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'protected'</span><span class="p">),</span>
+<span class="n">path</span><span class="p">(</span><span class="s1">'accounts/login/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">message</span><span class="p">),</span>
+</pre></div>
+
+
+<h2>Comandos básicos do manage.py</h2>
+<p>Para criar novas migrações com base nas alterações feitas nos seus modelos</p>
+<p><code>$ python manage.py makemigrations bands</code></p>
+<p>Obs: talvez dê erro porque está faltando coisas de forms.py, explicado mais abaixo.</p>
+<p>Para aplicar as migrações</p>
+<p><code>$ python manage.py migrate</code></p>
+<p>Para criar um usuário e senha para o admin</p>
+<p><code>$ python manage.py createsuperuser</code></p>
+<p>Para rodar a aplicação localmente</p>
+<p><code>$ python manage.py runserver</code></p>
+<p>Após criar um super usuário você pode entrar em localhost:8000/admin</p>
+<p>Obs: Se você entrar agora em localhost:8000 vai faltar o template home.html. Explicado mais abaixo.</p>
+<h2>shell_plus</h2>
+<p>É o <strong>interpretador interativo do python</strong> rodando <strong>via terminal</strong> direto na aplicação do django.</p>
+<p>Com o comando a seguir abrimos o shell do Django.</p>
+<p><code>$ python manage.py shell</code></p>
+<p>Mas se você está usando o django-extensions (mostrei como configurá-lo no settings.py), então basta digitar</p>
+<p><code>$ python manage.py shell_plus</code></p>
+<p>Veja a seguir como inserir dados direto pelo shell.</p>
+<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="kn">from</span> <span class="nn">myproject.bands.models</span> <span class="kn">import</span> <span class="n">Band</span><span class="p">,</span> <span class="n">Member</span>
+<span class="o">>>></span> <span class="c1"># Com django-extensions não precisa fazer o import</span>
+<span class="o">>>></span> <span class="c1"># criando o objeto e salvando</span>
+<span class="o">>>></span> <span class="n">band</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'Metallica'</span><span class="p">)</span>
+<span class="o">>>></span> <span class="n">band</span><span class="o">.</span><span class="n">name</span>
+<span class="o">>>></span> <span class="n">band</span><span class="o">.</span><span class="n">can_rock</span>
+<span class="o">>>></span> <span class="n">band</span><span class="o">.</span><span class="n">id</span>
+<span class="o">>>></span> <span class="c1"># criando uma instancia da banda a partir do id</span>
+<span class="o">>>></span> <span class="n">b</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">band</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
+<span class="o">>>></span> <span class="c1"># criando uma instancia do Membro e associando o id da banda a ela</span>
+<span class="o">>>></span> <span class="n">m</span> <span class="o">=</span> <span class="n">Member</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'James Hetfield'</span><span class="p">,</span> <span class="n">instrument</span><span class="o">=</span><span class="s1">'b'</span><span class="p">,</span> <span class="n">band</span><span class="o">=</span><span class="n">b</span><span class="p">)</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">name</span>
+<span class="o">>>></span> <span class="c1"># retornando o instrumento</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">instrument</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">get_instrument_display</span><span class="p">()</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">band</span>
+<span class="o">>>></span> <span class="c1"># salvando</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+<span class="o">>>></span> <span class="c1"># listando todas as bandas</span>
+<span class="o">>>></span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+<span class="o">>>></span> <span class="c1"># listando todos os membros</span>
+<span class="o">>>></span> <span class="n">Member</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+<span class="o">>>></span> <span class="c1"># criando mais uma banda</span>
+<span class="o">>>></span> <span class="n">band</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'The Beatles'</span><span class="p">)</span>
+<span class="o">>>></span> <span class="n">band</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'The Beatles'</span><span class="p">)</span>
+<span class="o">>>></span> <span class="n">band</span><span class="o">.</span><span class="n">id</span>
+<span class="o">>>></span> <span class="n">b</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">band</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
+<span class="o">>>></span> <span class="c1"># criando mais um membro</span>
+<span class="o">>>></span> <span class="n">m</span> <span class="o">=</span> <span class="n">Member</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'John Lennon'</span><span class="p">,</span> <span class="n">instrument</span><span class="o">=</span><span class="s1">'v'</span><span class="p">,</span> <span class="n">band</span><span class="o">=</span><span class="n">b</span><span class="p">)</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+<span class="o">>>></span> <span class="c1"># listando tudo novamente</span>
+<span class="o">>>></span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+<span class="o">>>></span> <span class="n">Member</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+<span class="o">>>></span> <span class="n">exit</span><span class="p">()</span>
+</pre></div>
+
+
+<h2>Criando os templates</h2>
+<p>Você pode criar os templates com os comandos a seguir...</p>
+<div class="highlight"><pre><span></span>$ mkdir -p myproject/bands/templates/bands
+$ touch myproject/bands/templates/<span class="o">{</span>menu,base,home<span class="o">}</span>.html
+$ touch myproject/bands/templates/bands/<span class="o">{</span>band_list,band_detail,band_form,band_contact,member_form,protected<span class="o">}</span>.html
+</pre></div>
+
+
+<p>... ou pegar os templates já prontos direto do Github.</p>
+<div class="highlight"><pre><span></span><span class="err">mkdir -p myproject/bands/templates/bands</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/base.html -P myproject/bands/templates/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/home.html -P myproject/bands/templates/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/menu.html -P myproject/bands/templates/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/band_contact.html -P myproject/bands/templates/bands/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/band_detail.html -P myproject/bands/templates/bands/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/band_form.html -P myproject/bands/templates/bands/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/band_list.html -P myproject/bands/templates/bands/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/member_form.html -P myproject/bands/templates/bands/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/protected.html -P myproject/bands/templates/bands/</span>
+</pre></div>
+
+
+<h2>forms.py</h2>
+<p><code>$ touch myproject/bands/forms.py</code></p>
+<p>Edite o forms.py.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django</span> <span class="kn">import</span> <span class="n">forms</span>
+<span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Band</span><span class="p">,</span> <span class="n">Member</span>
+
+
+<span class="k">class</span> <span class="nc">BandContactForm</span><span class="p">(</span><span class="n">forms</span><span class="o">.</span><span class="n">Form</span><span class="p">):</span>
+ <span class="n">subject</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">widget</span><span class="o">=</span><span class="n">forms</span><span class="o">.</span><span class="n">Textarea</span><span class="p">)</span>
+ <span class="n">sender</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">EmailField</span><span class="p">()</span>
+ <span class="n">cc_myself</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">BooleanField</span><span class="p">(</span><span class="n">required</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">BandForm</span><span class="p">(</span><span class="n">forms</span><span class="o">.</span><span class="n">ModelForm</span><span class="p">):</span>
+
+ <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
+ <span class="n">model</span> <span class="o">=</span> <span class="n">Band</span>
+ <span class="n">fields</span> <span class="o">=</span> <span class="s1">'__all__'</span>
+
+
+<span class="k">class</span> <span class="nc">MemberForm</span><span class="p">(</span><span class="n">forms</span><span class="o">.</span><span class="n">ModelForm</span><span class="p">):</span>
+
+ <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
+ <span class="n">model</span> <span class="o">=</span> <span class="n">Member</span>
+ <span class="n">fields</span> <span class="o">=</span> <span class="s1">'__all__'</span>
+</pre></div>
+
+
+<p>Lembra que eu deixei o código comentado em views.py?</p>
+<p>Descomente ele por favor</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">.forms</span> <span class="kn">import</span> <span class="n">BandContactForm</span><span class="p">,</span> <span class="n">BandForm</span><span class="p">,</span> <span class="n">MemberForm</span>
+</pre></div>
+
+
+<h2>admin.py</h2>
+<p>Criamos uma customização para o admin onde em members aparece um filtro por bandas.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.contrib</span> <span class="kn">import</span> <span class="n">admin</span>
+<span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Band</span><span class="p">,</span> <span class="n">Member</span>
+
+
+<span class="k">class</span> <span class="nc">MemberAdmin</span><span class="p">(</span><span class="n">admin</span><span class="o">.</span><span class="n">ModelAdmin</span><span class="p">):</span>
+ <span class="sd">"""Customize the look of the auto-generated admin for the Member model."""</span>
+ <span class="n">list_display</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'name'</span><span class="p">,</span> <span class="s1">'instrument'</span><span class="p">)</span>
+ <span class="n">list_filter</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'band'</span><span class="p">,)</span>
+
+
+<span class="n">admin</span><span class="o">.</span><span class="n">site</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">Band</span><span class="p">)</span> <span class="c1"># Use the default options</span>
+<span class="n">admin</span><span class="o">.</span><span class="n">site</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">Member</span><span class="p">,</span> <span class="n">MemberAdmin</span><span class="p">)</span> <span class="c1"># Use the customized options</span>
+</pre></div>
+
+
+<h2>Carregando dados de um CSV</h2>
+<p>Vamos baixar alguns arquivos para criar os dados no banco a partir de um CSV.</p>
+<div class="highlight"><pre><span></span><span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/create_data.py</span>
+<span class="err">mkdir fix</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/fix/bands.csv -P fix/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/fix/members.csv -P fix/</span>
+</pre></div>
+
+
+<p>Estando na pasta principal, rode o comando</p>
+<div class="highlight"><pre><span></span><span class="err">python create_data.py</span>
+</pre></div>
+
+
+<p>que ele vai carregar alguns dados pra você.</p>
+<p>Veja o código de <a href="https://github.com/rg3915/django2-pythonclub/blob/master/create_data.py">create_data.py</a>.</p>
+<p>Veja o código completo em https://github.com/rg3915/django2-pythonclub</p>
+<p><code>git clone https://github.com/rg3915/django2-pythonclub.git</code></p>
+
+ Regis da Silva
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Scientific Rust #rust2019
+
+ tag:blog.luizirber.org,2019-01-05:/2019/01/05/rust-2019/
+ 2019-01-05T19:00:00+00:00
+ <p>The Rust community requested feedback last year for where the language should go
+in 2018, and now they are running it again for 2019.
+Last year I was too new in Rust to organize a blog post,
+but after an year using it I feel more comfortable writing this!</p>
+<p>(Check my previous post about <a href="https://blog.luizirber.org/2018/08/23/sourmash-rust/">replacing the C++ core in sourmash with Rust</a> for more details on how I spend my year in Rust).</p>
+<h2>What counts as "scientific Rust"?</h2>
+<p>Anything that involves doing science using computers counts as
+scientific programming. It includes from embedded software
+<a href="https://www.youtube.com/watch?v=y5Yd3FC-kh8">running on satellites</a> to climate models
+running in supercomputers, from shell scripts running tools in a pipeline to
+data analysis using notebooks.</p>
+<p>It also makes the discussion harder, because it's too general! But it is very
+important to keep in mind, because scientists are not your regular user: they
+are highly qualified in their field of expertise, and they are also pushing the
+boundaries of what we know (and this might need flexibility in their tools).</p>
+<p>In this post I will be focusing more in two areas: array computing (what most
+people consider 'scientific programming' to be) and "data structures".</p>
+<h3>Array computing</h3>
+<p>This one is booming in the last couple of years due to industry interest in data
+sciences and deep learning (where they will talk about tensors instead of arrays),
+and has its roots in models running in supercomputers (a field where Fortran is
+still king!). Data tends to be quite regular (representable with matrices) and
+amenable to parallel processing.</p>
+<p>A good example is the SciPy stack in Python, built on top of NumPy and SciPy.
+The adoption of the SciPy stack (both in academia and industry) is staggering,
+and many <a href="https://github.com/cupy/cupy">alternative implementations</a> try to provide a NumPy-like API to try to
+capture its mindshare.</p>
+<p>This is the compute-intensive side science (be it CPU or GPU/TPU), and also the kind
+of data that pushed CPU evolution and is still very important in defining policy
+in scientific computing funding (see countries competing for the largest
+supercomputers and measuring performance in floating point operations per second).</p>
+<h3>Data structures for efficient data representation</h3>
+<p>For data that is not so regular the situation is a bit different. I'll use
+bioinformatics as an example: the data we get out of nucleotide sequencers is usually
+represented by long strings (of ACGT), and algorithms will do a lot of string processing
+(be it building string-overlap graphs for assembly, or searching for substrings
+in large collections). This is only one example: there are many analyses that
+will work with other types of data, and most of them don't have a
+universal data representation as in the array computing case.</p>
+<p>This is the memory-intensive science, and it's hard to measure performance in
+floating point operations because... most of the time you're not even using
+floating point numbers. It also suffers from limited data locality (which is
+almost a prerequisite for compute-intensive performance).</p>
+<h2>High performance core, interactive API</h2>
+<p>There is something common in both cases: while performance-intensive
+code is implemented in C/C++/Fortran, users usually interact with the API from
+other languages (especially Python or R) because it's faster to iterate and
+explore the data, and many of the tools already available in these languages are
+very helpful for these tasks (think Jupyter/pandas or RStudio/tidyverse).
+These languages are used to define the computation, but it is a lower-level core
+library that drives it (NumPy or Tensorflow follow this idea, for example).</p>
+<h2>How to make Rust better for science?</h2>
+<p>The biggest barrier to learning Rust is the ownership model, and while we can
+agree it is an important feature it is also quite daunting for newcomers,
+especially if they don't have previous programming experience and exposure to
+what bugs are being prevented. I don't see it being the first language we teach
+to scientists any time soon, because the majority of scientists are not system
+programmers, and have very different expectations for a programming language.
+That doesn't mean that they can't benefit from Rust!</p>
+<p>Rust is already great for building the performance-intensive parts,
+and thanks to Cargo it is also a better alternative for sharing this code around,
+since they tend to get 'stuck' inside Python or R packages.
+And the 'easy' approach of vendoring C/C++ instead of having packages make it
+hard to keep track of changes and doesn't encourage reusable code.</p>
+<p>And, of course, if this code is Rust instead of C/C++ it also means that Rust
+users can use them directly, without depending on the other languages. Seems
+like a good way to bootstrap a scientific community in Rust =]</p>
+<h2>What I would like to see in 2019?</h2>
+<h3>An attribute proc-macro like <code>#[wasm_bindgen]</code> but for FFI</h3>
+<p>While FFI is an integral part of Rust goals (interoperability with C/C++), I
+have serious envy of the structure and tooling developed for WebAssembly! (Even
+more now that it works in stable too)</p>
+<p>We already have <code>#[no_mangle]</code> and <code>pub extern "C"</code>, but they are quite
+low-level. I would love to see something closer to what wasm-bindgen does,
+and define some traits (like <a href="https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/convert/trait.IntoWasmAbi.html"><code>IntoWasmAbi</code></a>) to make it easier to
+pass more complex data types through the FFI.</p>
+<p>I know it's not that simple, and there are different design restrictions than
+WebAssembly to take into account... The point here is not having the perfect
+solution for all use cases, but something that serves as an entry point and helps
+to deal with the complexity while you're still figuring out all the quirks and
+traps of FFI. You can still fallback and have more control using the lower-level
+options when the need rises.</p>
+<h3>More -sys and Rust-like crates for interoperability with the larger ecosystems</h3>
+<p>There are new projects bringing more interoperability to <a href="https://arrow.apache.org/">dataframes</a> and <a href="https://xnd.io/">tensors</a>.
+While this ship has already sailed and they are implemented in C/C++,
+it would be great to be a first-class citizen,
+and not reinvent the wheel.
+(Note: the arrow project already have pretty good Rust support!)</p>
+<p>In my own corner (bioinformatics), the <a href="https://github.com/rust-bio">Rust-bio community</a> is doing a
+great job of wrapping <a href="https://github.com/rust-bio/rust-htslib">useful libraries in C/C++</a> and exposing them to
+Rust (and also a shout-out to 10X Genomics for doing this work for
+<a href="https://github.com/10XGenomics/rust-bwa">other tools</a> while also contributing to Rust-bio!).</p>
+<h3>More (bioinformatics) tools using Rust!</h3>
+<p>We already have great examples like <a href="https://github.com/onecodex/finch-rs">finch</a> and <a href="https://github.com/natir/yacrd">yacrd</a>,
+since Rust is great for single binary distribution of programs.
+And with bioinformatics focusing so much in independent tools chained together in workflows,
+I think we can start convincing people to try it out =]</p>
+<h3>A place to find other scientists?</h3>
+<p>Another idea is to draw inspiration from <a href="https://ropensci.org/about/">rOpenSci</a> and have a Rust equivalent,
+where people can get feedback about their projects and how to better integrate it with other crates.
+This is quite close to the working group idea,
+but I think it would serve more as a gateway to other groups,
+more focused on developing entry-level docs and bringing more scientists to the
+community.</p>
+<h2>Final words</h2>
+<p>In the end, I feel like this post ended up turning into my 'wishful TODO list'
+for 2019, but I would love to find more people sharing these goals (or willing
+to take any of this and just run with it, I do have a PhD to finish! =P)</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/101367104235280253">Thread on Mastodon</a></li>
+<li><a href="https://twitter.com/luizirber/status/1081729107170193408">Thread on Twitter</a></li>
+</ul>
+
+ luizirber
+ https://blog.luizirber.org/
+
+
+ Gabbleblotchits
+ Vogon Poetry, Computers and (some) biology
+
+ https://blog.luizirber.org/
+ 2025-04-17T02:14:15+00:00
+
+
+
+
+ Algoritmos de Ordenação
+
+ tag:pythonclub.com.br,2018-11-29:/algoritmos-ordenacao.html
+ 2018-11-29T15:10:00+00:00
+ <p>Fala pessoal, tudo bom?</p>
+<p>Nos vídeos abaixo, vamos aprender como implementar alguns dos algoritmos de ordenação usando Python.</p>
+<div class="section" id="bubble-sort">
+<h2>Bubble Sort</h2>
+<p>Como o algoritmo funciona: Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=Doy64STkwlI&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r&t=0s&index=3">https://www.youtube.com/watch?v=Doy64STkwlI</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=B0DFF0fE4rk&index=3&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r">https://www.youtube.com/watch?v=B0DFF0fE4rk</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Código do algoritmo</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">sort</span><span class="p">(</span><span class="n">array</span><span class="p">):</span>
+
+ <span class="k">for</span> <span class="n">final</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">),</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span>
+ <span class="n">exchanging</span> <span class="o">=</span> <span class="kc">False</span>
+
+ <span class="k">for</span> <span class="n">current</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">final</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">array</span><span class="p">[</span><span class="n">current</span><span class="p">]</span> <span class="o">></span> <span class="n">array</span><span class="p">[</span><span class="n">current</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]:</span>
+ <span class="n">array</span><span class="p">[</span><span class="n">current</span> <span class="o">+</span> <span class="mi">1</span><span class="p">],</span> <span class="n">array</span><span class="p">[</span><span class="n">current</span><span class="p">]</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="n">current</span><span class="p">],</span> <span class="n">array</span><span class="p">[</span><span class="n">current</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
+ <span class="n">exchanging</span> <span class="o">=</span> <span class="kc">True</span>
+
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">exchanging</span><span class="p">:</span>
+ <span class="k">break</span>
+</pre></div>
+</div>
+<div class="section" id="selection-sort">
+<h2>Selection Sort</h2>
+<p>Como o algoritmo funciona: Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=vHxtP9BC-AA&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r&index=4">https://www.youtube.com/watch?v=vHxtP9BC-AA</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=0ORfCwwhF_I&index=5&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r&index=5">https://www.youtube.com/watch?v=0ORfCwwhF_I</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Código do algoritmo</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">sort</span><span class="p">(</span><span class="n">array</span><span class="p">):</span>
+ <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">)):</span>
+ <span class="n">min_index</span> <span class="o">=</span> <span class="n">index</span>
+
+ <span class="k">for</span> <span class="n">right</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">index</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">)):</span>
+ <span class="k">if</span> <span class="n">array</span><span class="p">[</span><span class="n">right</span><span class="p">]</span> <span class="o"><</span> <span class="n">array</span><span class="p">[</span><span class="n">min_index</span><span class="p">]:</span>
+ <span class="n">min_index</span> <span class="o">=</span> <span class="n">right</span>
+
+ <span class="n">array</span><span class="p">[</span><span class="n">index</span><span class="p">],</span> <span class="n">array</span><span class="p">[</span><span class="n">min_index</span><span class="p">]</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="n">min_index</span><span class="p">],</span> <span class="n">array</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
+</pre></div>
+</div>
+<div class="section" id="insertion-sort">
+<h2>Insertion Sort</h2>
+<p>Como o algoritmo funciona: Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=O_E-Lj5HuRU&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r&t=0s&index=6">https://www.youtube.com/watch?v=O_E-Lj5HuRU</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=Sy_Z1pqMgko&index=7&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r">https://www.youtube.com/watch?v=Sy_Z1pqMgko</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Código do algoritmo</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">sort</span><span class="p">(</span><span class="n">array</span><span class="p">):</span>
+ <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">)):</span>
+ <span class="n">current_element</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="n">p</span><span class="p">]</span>
+
+ <span class="k">while</span> <span class="n">p</span> <span class="o">></span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">array</span><span class="p">[</span><span class="n">p</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">></span> <span class="n">current_element</span><span class="p">:</span>
+ <span class="n">array</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="n">p</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span>
+ <span class="n">p</span> <span class="o">-=</span> <span class="mi">1</span>
+
+ <span class="n">array</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="n">current_element</span>
+</pre></div>
+</div>
+<div class="section" id="merge-sort">
+<h2>Merge Sort</h2>
+<p>Como o algoritmo funciona: Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=Lnww0ibU0XM&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r&t=0s&index=8">https://www.youtube.com/watch?v=Lnww0ibU0XM</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Como implementar o algoritmo usando Python - Parte I: <a class="reference external" href="https://www.youtube.com/watch?v=cXJHETlYyVk&index=9&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r">https://www.youtube.com/watch?v=cXJHETlYyVk</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Código do algoritmo</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">sort</span><span class="p">(</span><span class="n">array</span><span class="p">):</span>
+ <span class="n">sort_half</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
+
+
+<span class="k">def</span> <span class="nf">sort_half</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">start</span> <span class="o">>=</span> <span class="n">end</span><span class="p">:</span>
+ <span class="k">return</span>
+
+ <span class="n">middle</span> <span class="o">=</span> <span class="p">(</span><span class="n">start</span> <span class="o">+</span> <span class="n">end</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span>
+
+ <span class="n">sort_half</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">middle</span><span class="p">)</span>
+ <span class="n">sort_half</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">middle</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">end</span><span class="p">)</span>
+
+ <span class="n">merge</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">)</span>
+
+
+<span class="k">def</span> <span class="nf">merge</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">):</span>
+ <span class="n">array</span><span class="p">[</span><span class="n">start</span><span class="p">:</span> <span class="n">end</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">array</span><span class="p">[</span><span class="n">start</span><span class="p">:</span> <span class="n">end</span> <span class="o">+</span> <span class="mi">1</span><span class="p">])</span>
+</pre></div>
+</div>
+
+ Lucas Magnum
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Trabalhando com operadores ternários
+
+ tag:pythonclub.com.br,2018-10-06:/trabalhando-com-operadores-ternarios.html
+ 2018-10-06T12:21:00+00:00
+ <p>Quando estamos escrevendo um código qualquer, possivelmente
+a expressão que mais utilizamos é o <code>if</code>. Para qualquer
+tarefas que buscamos automatizar ou problemas que buscamos
+resolver, sempre acabamos caindo em lógicas como "Se isso
+acontecer, então faça aquilo, senão faça aquele outro...".</p>
+<p>Quando estamos falando de ações a serem executadas, pessoalmente
+gosto da forma com que o código fica organizado em python quando
+usamos este tipo de condições, por exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">vencer_o_thanos</span><span class="p">:</span>
+ <span class="n">restaurar_a_paz</span><span class="p">()</span>
+
+<span class="k">else</span><span class="p">:</span>
+ <span class="n">foo</span><span class="p">()</span>
+</pre></div>
+
+
+<p>Graças a indentação e ao espaçamento, vemos onde onde começa e/ou
+termina o bloco executado caso a varável <code>vencer_o_thanos</code> seja
+<code>True</code>. Quanto mais <code>if</code>'s você aninhar, mais bonito seu
+código fica e em momento algum o mesmo se torna mais confuso
+(ao menos, não deveria se tornar). Entretanto, sempre fico
+extremamente incomodado quando tenho de escrever um bloco apenas
+marcar uma variável, como por exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">vencer_o_thanos</span><span class="p">:</span>
+ <span class="n">paz</span> <span class="o">=</span> <span class="kc">True</span>
+
+<span class="k">else</span><span class="p">:</span>
+ <span class="n">paz</span> <span class="o">=</span> <span class="kc">False</span>
+</pre></div>
+
+
+<p>Por isso, para trabalhar com variáveis que possuem um valor condicional,
+gosto sempre de trabalhar com expressões condicionais, ou como costumam
+ser chamadas, <strong>operadores ternários</strong>.</p>
+<p>Operadores ternários são todos os operadores que podem receber três
+operandos. Como as expressões condicionais costumam ser os operadores
+ternários mais populares nas linguagens em que aparecem, acabamos por
+associar estes nomes e considerar que são a mesma coisa. Cuidado ao tirar
+este tipo de conclusão, mesmo que toda vogal esteja no alfabeto, o
+alfabeto não é composto apenas por vogais.</p>
+<p>A estrutura de uma expressão condicional é algo bem simples, veja só:</p>
+<div class="highlight"><pre><span></span><span class="n">paz</span> <span class="o">=</span> <span class="kc">True</span> <span class="k">if</span> <span class="n">vencer_o_thanos</span> <span class="k">else</span> <span class="kc">False</span>
+<span class="n">tipo_de_x</span> <span class="o">=</span> <span class="s2">"Par"</span> <span class="k">if</span> <span class="n">x</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="s2">"impar"</span>
+</pre></div>
+
+
+<p>Resumidamente, teremos <strong>um valor seguido de uma condição e por fim seu
+valor caso a condição seja falsa</strong>. Pessoalmente acredito que apesar de um
+pouco diferente, essa forma de escrita para casos como o exemplificado acima
+é muito mais clara, mais explicita.</p>
+<p>Se você fizer uma tradução literal das booleanas utilizadas no primeiro exemplo,
+lerá algo como <code>paz é verdadeira caso vencer_o_thanos, caso contrário é Falsa.</code>
+já o segundo exemplo fica mais claro ainda, pois lemos algo como
+<code>tipo_de_x é par caso o resto da divisão de x por 2 seja 0, se não, tipo_de_x é impar.</code>.</p>
+<p>Interpretar código dessa forma pode ser estranho para um programador. Interpretar
+uma abertura de chave ou uma indentação já é algo mas natural. Todavia, para aqueles
+que estão começando, o raciocínio ocorre de forma muito mais parecida com a descrita
+acima. Espero que tenham gostado do texto e que esse conhecimento lhes seja útil.</p>
+
+ Vitor Hugo de Oliveira Vargas
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ What open science is about
+
+ tag:blog.luizirber.org,2018-09-24:/2018/09/24/open-science/
+ 2018-09-24T20:00:00+00:00
+ <p>Today I got a pleasant surprise: <a href="http://www.olgabotvinnik.com/">Olga Botvinnik</a> posted on <a href="https://twitter.com/olgabot/status/1044292704513839104">Twitter</a>
+about a <a href="https://github.com/czbiohub/kmer-hashing/blob/olgabot/search-compare-ignore-abundance/figures/presentations/2018-09-24_beyond_the_cell_atlas_poster/2018-09-24_Beyond_the_Cell_Atlas.pdf">poster</a> she is presenting at the Beyond the Cell Atlas conference
+and she name-dropped a bunch of people that helped her. The cool thing? They
+are all open source developers, and Olga interacted thru GitHub to <a href="https://github.com/dib-lab/sourmash/pull/543">ask</a> <a href="https://github.com/dib-lab/sourmash/issues/545">for</a> <a href="https://github.com/betteridiot/bamnostic/issues/15">features</a>,
+<a href="https://github.com/betteridiot/bamnostic/issues/20">report bugs</a> and even <a href="https://github.com/dib-lab/sourmash/pull/543">submit</a> <a href="https://github.com/dib-lab/sourmash/pull/539">pull</a> <a href="https://github.com/dib-lab/sourmash/pull/529">requests</a>.</p>
+<p>That's what open science is about: collaboration, good practices, and in the end coming up
+with something that is larger than each individual piece. Now sourmash is better,
+bamnostic is better, reflow is better. I would like to see this becoming more and
+more common =]</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/100783375923088450">Thread on Mastodon</a></li>
+<li><a href="https://twitter.com/luizirber/status/1044369382233665536">Thread on Twitter</a></li>
+</ul>
+
+ luizirber
+ https://blog.luizirber.org/
+
+
+ Gabbleblotchits
+ Vogon Poetry, Computers and (some) biology
+
+ https://blog.luizirber.org/
+ 2025-04-17T02:14:15+00:00
+
+
+
+
+ New crate: nthash
+
+ tag:blog.luizirber.org,2018-09-13:/2018/09/13/nthash/
+ 2018-09-13T20:00:00+00:00
+ <p>A quick announcement: I wrote a <a href="https://github.com/luizirber/nthash">Rust implementation</a> of <a href="https://github.com/bcgsc/ntHash">ntHash</a> and published
+it in <a href="https://crates.io/crates/nthash">crates.io</a>. It implements an <code>Iterator</code> to take advantage of the
+rolling properties of <code>ntHash</code> which make it so useful in bioinformatics (where
+we work a lot with sliding windows over sequences).</p>
+<p>It's a pretty small crate, and probably was a better project to learn Rust than
+doing a <a href="https://blog.luizirber.org/2018/08/23/sourmash-rust/">sourmash implementation</a> because it doesn't involve gnarly FFI
+issues. I also put <a href="https://github.com/luizirber/nthash/blob/d0c16d7deb0a78b8aeb29090db91bba954c14fe8/src/lib.rs#L91">some docs</a>, <a href="https://github.com/luizirber/nthash/blob/d0c16d7deb0a78b8aeb29090db91bba954c14fe8/benches/nthash.rs#L11">benchmarks</a> using <a href="https://japaric.github.io/criterion.rs/">criterion</a>,
+and even an <a href="https://github.com/luizirber/nthash/blob/d0c16d7deb0a78b8aeb29090db91bba954c14fe8/tests/nthash.rs#L80">oracle property-based test</a> with <a href="https://github.com/BurntSushi/quickcheck">quickcheck</a>.</p>
+<p>More info <a href="https://docs.rs/nthash/">in the docs</a>, and if you want an <s>optimization</s> versioning bug
+discussion be sure to check the <a href="https://github.com/luizirber/nthash_bug"><code>ntHash bug?</code></a> repo,
+which has a (slow) Python implementation and a pretty nice <a href="https://nbviewer.jupyter.org/github/luizirber/nthash_bug/blob/master/analysis.ipynb">analysis</a> notebook.</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/100721133117928424">Thread on Mastodon</a></li>
+<li><a href="https://twitter.com/luizirber/status/1040386666089705472">Thread on Twitter</a></li>
+</ul>
+
+ luizirber
+ https://blog.luizirber.org/
+
+
+ Gabbleblotchits
+ Vogon Poetry, Computers and (some) biology
+
+ https://blog.luizirber.org/
+ 2025-04-17T02:14:15+00:00
+
+
+
+
+ Oxidizing sourmash: WebAssembly
+
+ tag:blog.luizirber.org,2018-08-27:/2018/08/27/sourmash-wasm/
+ 2018-08-27T18:30:00+00:00
+ <p>sourmash calculates MinHash signatures for genomic datasets,
+meaning we are reducing the data (via subsampling) to a small
+representative subset (a signature) capable of answering one question:
+how similar is this dataset to another one? The key here is that a dataset with
+10-100 GB will be reduced to something in the megabytes range, and two approaches
+for doing that are:</p>
+<ul>
+<li>The user install our software in their computer.
+ This is not so bad anymore (yay bioconda!), but still requires knowledge
+ about command line interfaces and how to install all this stuff. The user
+ data never leaves their computer, and they can share the signatures later
+ if they want to.</li>
+<li>Provide a web service to calculate signatures. In this case no software
+ need to be installed, but it's up to someone (me?) to maintain a server running with
+ an API and frontend to interact with the users. On top of requiring more
+ maintenance, another drawback is that
+ the user need to send me the data, which is very inefficient network-wise
+ and lead to questions about what I can do with their raw data (and I'm not
+ into surveillance capitalism, TYVM).</li>
+</ul>
+<h2>But... what if there is a third way?</h2>
+<p>What if we could keep the frontend code from the web service (very
+user-friendly) but do all the calculations client-side (and avoid the network
+bottleneck)? The main hurdle
+here is that our software is implemented in Python (and C++), which are not
+supported in browsers. My first solution was to write the core features of
+<a href="https://github.com/luizirber/sourmash-node">sourmash in JavaScript</a>, but that quickly started hitting annoying things
+like JavaScript not supporting 64-bit integers. There is also the issue of
+having another codebase to maintain and keep in sync with the original sourmash,
+which would be a relevant burden for us. I gave a <a href="https://drive.google.com/open?id=1JvXiDaEA4J3hmEKw6sV-VHMpuHG_sxls3fLxJOht28E">lab meeting</a> about this
+approach, using a <a href="https://soursigs-dnd-luizirber.hashbase.io/">drag-and-drop UI as proof of concept</a>. It did work but it
+was finicky (dealing with the 64-bit integer hashes is not fun). The good thing
+is that at least I had a working UI for further testing<sup id="sf-sourmash-wasm-1-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-1" class="simple-footnote" title="even if horrible, I need to get some design classes =P">1</a></sup></p>
+<p>In "<a href="https://blog.luizirber.org/2018/08/23/sourmash-rust/">Oxidizing sourmash: Python and FFI</a>" I described my road to learn Rust,
+but something that I omitted was that around the same time the <code>WebAssembly</code>
+support in Rust started to look better and better and was a huge influence in
+my decision to learn Rust. Reimplementing the sourmash C++ extension in Rust and
+use the same codebase in the browser sounded very attractive,
+and now that it was working I started looking into how to use the WebAssembly
+target in Rust.</p>
+<h2>WebAssembly?</h2>
+<p>From the <a href="https://webassembly.org/">official site</a>,</p>
+<blockquote>
+<p>WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine.
+Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust,
+enabling deployment on the web for client and server applications.</p>
+</blockquote>
+<p>You can write WebAssembly by hand, but the goal is to have it as lower level
+target for other languages. For me the obvious benefit is being able to use
+something that is not JavaScript in the browser, even though the goal is not to replace
+JS completely but complement it in a big pain point: performance. This also
+frees JavaScript from being the target language for other toolchains,
+allowing it to grow into other important areas (like language ergonomics).</p>
+<p>Rust is not the only language targeting WebAssembly: Go 1.11 includes
+<a href="https://golang.org/doc/go1.11#wasm">experimental support for WebAssembly</a>, and there are even projects bringing
+the <a href="https://github.com/iodide-project/pyodide">scientific Python to the web</a> using WebAssembly. </p>
+<h2>But does it work?</h2>
+<p>With the <a href="https://github.com/luizirber/sourmash-rust">Rust implementation in place</a> and with all tests working on sourmash, I
+added the finishing touches using <a href="https://github.com/rustwasm/wasm-bindgen"><code>wasm-bindgen</code></a> and built an NPM package using
+<a href="https://github.com/rustwasm/wasm-pack"><code>wasm-pack</code></a>: <a href="https://www.npmjs.com/package/sourmash">sourmash</a> is a Rust codebase compiled to WebAssembly and ready
+to use in JavaScript projects.</p>
+<p>(Many thanks to Madicken Munk, who also presented during SciPy about how they used
+<a href="https://munkm.github.io/2018-07-13-scipy/">Rust and WebAssembly to do interactive visualization in Jupyter</a>
+and helped with a good example on how to do this properly =] )</p>
+<p>Since I already had the working UI from the previous PoC, I <a href="https://github.com/luizirber/wort-dnd">refactored the code</a>
+to use the new WebAssembly module and voilà! <a href="https://wort-dnd.hashbase.io/">It works!</a><sup id="sf-sourmash-wasm-2-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-2" class="simple-footnote" title="the first version of this demo only worked in Chrome because they implemented the BigInt proposal, which is not in the official language yet. The funny thing is that BigInt would have made the JS implementation of sourmash viable, and I probably wouldn't have written the Rust implementation =P. Turns out that I didn't need the BigInt support if I didn't expose any 64-bit integers to JS, and that is what I'm doing now.">2</a></sup>.
+<sup id="sf-sourmash-wasm-3-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-3" class="simple-footnote" title="Along the way I ended up writing a new FASTQ parser... because it wouldn't be bioinformatics if it didn't otherwise, right? =P">3</a></sup>
+But that was the demo from a year ago with updated code and I got a bit
+better with frontend development since then, so here is the new demo:</p>
+<div id="files" class="box">
+ <h2>sourmash + Wasm</h2>
+ <div id="drag-container">
+ <p><b>Drag & drop</b> a FASTA or FASTQ file here to calculate the sourmash signature.</p>
+ </div>
+
+ <div id="progress-container">
+ <div id="progress-bar"></div>
+ </div>
+ <div class="columns">
+ <fieldset class="box input-button" id="params">
+ <label for="ksize-input">k-mer size:</label>
+ <input id="ksize-input" type="number" value="21" />
+ <label for="scaled-input">scaled:</label>
+ <input id="scaled-input" type="number" value="0" />
+ <label for="num-input">number of hashes:</label>
+ <input id="num-input" type="number" value="500" />
+ <label for="dna-protein-group">Input type:</label>
+ <div id="dna-protein-group">
+ <input id="dna-input" name="dna-protein-input" type="radio" value="DNA/RNA" checked="checked" />
+ <label for="dna-input">DNA/RNA</label>
+ <input id="protein-input" name="dna-protein-input" type="radio" value="Protein" />
+ <label for="protein-input">Protein</label>
+ </div>
+ <label for="track-abundance-input">Track abundance?</label>
+ <input id="track-abundance-input" type="checkbox" checked="checked" />
+ </fieldset>
+ <div class="box" id="download">
+ <button id="download_btn" type="button" disabled="disabled">Download</button>
+ </div>
+ </div>
+</div>
+
+<p>
+</p>
+<p>For the source code for this demo, check the <a href="https://blog.luizirber.org/static/sourmash-wasm/index.html">sourmash-wasm</a> directory.</p>
+<h2>Next steps</h2>
+<p>The proof of concept works, but it is pretty useless right now.
+I'm thinking about building it as a <a href="https://www.webcomponents.org/">Web Component</a> and making it really easy
+to add to any webpage<sup id="sf-sourmash-wasm-4-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-4" class="simple-footnote" title="or maybe a React component? I really would like to have something that works independent of framework, but not sure what is the best option in this case...">4</a></sup>.</p>
+<p>Another interesting feature would be supporting more input formats (the GMOD
+project implemented a lot of those!), but more features are probably better
+after something simple but functional is released =P</p>
+<h2>Next time!</h2>
+<p>Where we will go next? Maybe explore some decentralized web technologies like
+IPFS and dat, hmm? =]</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/100624574917435477">Thread on Mastodon</a></li>
+<li><a href="https://www.reddit.com/r/rust/comments/9atie8/blog_post_clientside_bioinformatics_in_the/">Thread on reddit</a></li>
+<li><a href="https://twitter.com/luizirber/status/1034206952773935104">Thread on Twitter</a></li>
+</ul>
+<h2>Updates</h2>
+<ul>
+<li>2018-08-30: Added a demo in the blog post.</li>
+</ul><hr /><h2>Footnotes</h2><ol><li id="sf-sourmash-wasm-1"><p>even if horrible, I
+need to get some design classes =P <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-1-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-wasm-2"><p>the first version
+of this demo only worked in Chrome because they implemented the <a href="https://github.com/tc39/proposal-bigint">BigInt proposal</a>,
+which is not in the official language yet. The funny thing is that BigInt would
+have made the JS implementation of sourmash viable, and I probably wouldn't have
+written the Rust implementation =P.
+Turns out that I didn't need the BigInt support if I didn't expose any 64-bit
+integers to JS, and that is what I'm doing now. <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-2-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-wasm-3"><p>Along the way I ended up writing a new FASTQ parser... because it wouldn't
+be bioinformatics if it didn't otherwise, right? =P <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-3-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-wasm-4"><p>or maybe a React component? I really would like to
+have something that works independent of framework, but not sure what is the
+best option in this case... <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-4-back" class="simple-footnote-back">↩</a></p></li></ol>
+
+ luizirber
+ https://blog.luizirber.org/
+
+
+ Gabbleblotchits
+ Vogon Poetry, Computers and (some) biology
+
+ https://blog.luizirber.org/
+ 2025-04-17T02:14:15+00:00
+
+
+
+
+ Oxidizing sourmash: Python and FFI
+
+ tag:blog.luizirber.org,2018-08-23:/2018/08/23/sourmash-rust/
+ 2018-08-23T20:00:00+00:00
+ <p>I think the first time I heard about Rust was because Frank Mcsherry chose it to
+write a <a href="https://github.com/frankmcsherry/timely-dataflow">timely dataflow</a> implementation.
+Since then it started showing more and more in my news sources,
+leading to Armin Ronacher publishing a post in the Sentry blog last November about
+writing <a href="https://blog.sentry.io/2017/11/14/evolving-our-rust-with-milksnake">Python extensions in Rust</a>.</p>
+<p>Last December I decided to give it a run:
+I spent some time porting the C++ bits of <a href="https://github.com/dib-lab/sourmash">sourmash</a>
+to Rust.
+The main advantage here is that it's a problem I know well,
+so I know what the code is supposed to do and can focus on figuring out
+syntax and the mental model for the language.
+I started digging into the <code>symbolic</code> codebase and understanding what they did,
+and tried to mirror or improve it for my use cases.</p>
+<p>(About the post title: The process of converting a codebase to Rust is referred as <a href="https://wiki.mozilla.org/Oxidation">"Oxidation"</a> in
+the Rust community, following the codename Mozilla chose for the process of
+integrating Rust components into the Firefox codebase.
+<sup id="sf-sourmash-rust-1-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-1" class="simple-footnote" title="The creator of the language is known to keep making up different explanations for the name of the language, but in this case ">1</a></sup>
+Many of these components were tested and derived in Servo, an experimental
+browser engine written in Rust, and are being integrated into Gecko,
+the current browser engine (mostly written in C++).)</p>
+<h2>Why Rust?</h2>
+<p>There are other programming languages more focused on scientific software
+that could be used instead, like Julia<sup id="sf-sourmash-rust-2-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-2" class="simple-footnote" title="Even more now that it hit 1.0, it is a really nice language">2</a></sup>. Many programming languages start from a
+specific niche (like R and statistics,
+or Maple and mathematics) and grow into larger languages over time.
+While Rust goal is not to be a scientific language,
+its focus on being a general purpose language allows a phenomenon similar
+to what happened with Python, where people from many areas pushed the
+language in different directions (system scripting, web development,
+numerical programming...) allowing developers to combine all these things
+in their systems.</p>
+<p>But by far my interest in Rust is for the many best practices it brings to the default experience:
+integrated package management (with Cargo),
+documentation (with rustdoc), testing and benchmarking.
+It's understandable that older languages like C/C++ need
+more effort to support some of these features (like modules and an unified
+build system), since they are designed by standard and need to keep backward
+compatibility with codebases that already exist.
+Nonetheless, the lack of features increase the effort needed to have good
+software engineering practices, since you need to choose a solution that might
+not be compatible with other similar but slightly different options,
+leading to fragmentation and increasing the impedance to use these features.</p>
+<p>Another big reason is that Rust doesn't aim to completely replace what already
+exists, but complement and extend it. Two very good talks about how to do this,
+one by <a href="https://ashleygwilliams.github.io/rustfest-2017/#1">Ashley Williams</a>, another by <a href="http://talks.edunham.net/lca2018/should-you-rewrite-in-rust/beamer.pdf">E. Dunham</a>.</p>
+<h2>Converting from a C++ extension to Rust</h2>
+<p>The current implementation of the core data structures in sourmash is in a
+C++ extension wrapped with Cython. My main goals for converting the code are:</p>
+<ul>
+<li>
+<p>support additional languages and platforms. sourmash is available as a Python
+ package and CLI, but we have R users in the lab that would benefit from having
+ an R package, and ideally we wouldn't need to rewrite the software every time
+ we want to support a new language.</p>
+</li>
+<li>
+<p>reducing the number of wheel packages necessary (one for each OS/platform).</p>
+</li>
+<li>
+<p>in the long run, use the Rust memory management concepts (lifetimes, borrowing)
+to increase parallelism in the code.</p>
+</li>
+</ul>
+<p>Many of these goals are attainable with our current C++ codebase, and
+"rewrite in a new language" is rarely the best way to solve a problem.
+But the reduced burden in maintenance due to better tooling,
+on top of features that would require careful planning to execute
+(increasing the parallelism without data races) while maintaining compatibility
+with the current codebase are promising enough to justify this experiment.
+<a href="https://github.com/luizirber/2018-python-rust/blob/master/03.current_impl.md"><img src="https://blog.luizirber.org/images/arch_cpp.png" title="" /></a></p>
+<p>Cython provides a nice gradual path to migrate code from Python to C++,
+since it is a superset of the Python syntax. It also provides low overhead
+for many C++ features, especially the STL containers, with makes it easier
+to map C++ features to the Python equivalent.
+For research software this also lead to faster exploration of solutions before
+having to commit to lower level code, but without a good process it might also
+lead to code never crossing into the C++ layer and being stuck in the Cython
+layer. This doesn't make any difference for a Python user, but it becomes
+harder from users from other languages to benefit from this code (since your
+language would need some kind of support to calling Python code, which is not
+as readily available as calling C code).</p>
+<p>Depending on the requirements, a downside is that Cython is tied to the CPython API,
+so generating the extension requires a development environment set up with
+the appropriate headers and compiler. This also makes the extension specific
+to a Python version: while this is not a problem for source distributions,
+generating wheels lead to one wheel for each OS and Python version supported.</p>
+<h2>The new implementation</h2>
+<p>This is the overall architecture of the Rust implementation:
+<a href="https://github.com/luizirber/2018-python-rust/blob/master/04.rust_impl.md"><img src="https://blog.luizirber.org/images/arch_rust.png" title="" /></a>
+It is pretty close to what <code>symbolic</code> does,
+so let's walk through it.</p>
+<h3>The Rust code</h3>
+<p>If you take a look at my Rust code, you will see it is very... C++. A lot of the
+code is very similar to the original implementation, which is both a curse and a
+blessing: I'm pretty sure that are more idiomatic and performant ways of doing
+things, but most of the time I could lean on my current mental model for C++ to
+translate code. The biggest exception was the <code>merge</code> function, were I was doing
+something on the C++ implementation that the borrow checker didn't like.
+Eventually I found it was because it couldn't keep track of the lifetime
+correctly and putting braces around it fixed the problem,
+which was both an epiphany and a WTF moment. <a href="https://play.rust-lang.org/?gist=eae9de12950d1b2a7699cd49a3571c37&version=stable">Here</a> is an example that triggers
+the problem, and the <a href="https://play.rust-lang.org/?gist=c8733c5125766930a589c8d0412af99c&version=stable">solution</a>.</p>
+<p>"Fighting the borrow checker" seems to be a common theme while learning Rust,
+but the compiler really tries to help you to understand what is happening and
+(most times) how to fix it. A lot of people grow to hate the borrow checker,
+but I see it more as a 'eat your vegetables' situation: you might not like it
+at first, but it's better in the long run. Even though I don't have a big
+codebase in Rust yet, it keeps you from doing things that will come back to bite
+you hard later.</p>
+<h3>Generating C headers for Rust code: cbindgen</h3>
+<p>With the Rust library working, the next step was taking the Rust code and generate C headers describing the
+functions and structs we expose with the <code>#[no_mangle]</code> attribute in Rust
+(these are defined in the <a href="https://github.com/luizirber/sourmash-rust/blob/ead9ae0ed3b2d16c9e3b8379919f3bfd2efd21ae/src/ffi.rs"><code>ffi.rs</code></a> module in <code>sourmash-rust</code>).
+This attribute tells the Rust compiler to generate names that are compatible
+with the C ABI, and so can be called from other languages that implement FFI
+mechanisms. FFI (the foreign function interface) is quite low-level,
+and pretty much defines things that C can represent: integers, floats, pointers
+and structs. It doesn't support higher level concepts like objects or generics,
+so in a sense it looks like a feature funnel.
+This might sound bad, but ends up being something that other languages can
+understand without needing too much extra functionality in their runtimes,
+which means that most languages have support to calling code through an FFI.</p>
+<p>Writing the C header by hand is possible, but is very error prone.
+A better solution is to use <a href="https://github.com/eqrion/cbindgen"><code>cbindgen</code></a>,
+a program that takes Rust code and generate a C header file automatically.
+<code>cbindgen</code> is developed primarily to generate the C headers for <a href="https://github.com/servo/webrender/">webrender</a>,
+the GPU-based renderer for servo,
+so it's pretty likely that if it can handle a complex codebase it will work
+just fine for the majority of projects.</p>
+<h3>Interfacing with Python: CFFI and Milksnake</h3>
+<p>Once we have the C headers, we can use the FFI to
+call Rust code in Python. Python has a FFI module in the standard library: <code>ctypes</code>,
+but the Pypy developers also created CFFI, which has more features.</p>
+<p>The C headers generated by cbindgen can be interpreted by CFFI to generate
+a low-level Python interface for the code. This is the equivalent of declaring
+the functions/methods and structs/classes in a <code>pxd</code> file (in the Cython
+world): while the code is now usable in Python, it is not well adapted to
+the features and idioms available in the language.</p>
+<p>Milksnake is the package developed by Sentry that takes care of running cargo
+for the Rust compilation and generating the CFFI boilerplate,
+making it easy to load the low-level CFFI bindings in Python.
+With this low-level binding available we can now write something more Pythonic
+(the <code>pyx</code> file in Cython), and I ended up just renaming the <code>_minhash.pyx</code> file
+back to <code>minhash.py</code> and doing one-line fixes to replace the Cython-style code
+with the equivalent CFFI calls.</p>
+<p>All of these changes should be transparent to the Python code, and to guarantee
+that I made sure that all the current tests that we have (both for the Python
+module and the command line interface) are still working after the changes.
+It also led to finding some quirks in the implementation,
+and even improvements in the current C++ code (because we were moving a lot of
+data from C++ to Python).</p>
+<h2>Where I see this going</h2>
+<p>It seems it worked as an experiment,
+and I presented a <a href="https://github.com/luizirber/2018-python-rust">poster</a> at <a href="https://gccbosc2018.sched.com/event/FEWp/b23-oxidizing-python-writing-extensions-in-rust">GCCBOSC 2018</a> and <a href="https://scipy2018.scipy.org/ehome/index.php?eventid=299527&tabid=712461&cid=2233543&sessionid=21618890&sessionchoice=1&">SciPy 2018</a> that
+was met with excitement by many people.
+Knowing that it is possible,
+I want to reiterate some points why Rust is pretty exciting for bioinformatics
+and science in general.</p>
+<h3>Bioinformatics as libraries (and command line tools too!)</h3>
+<p>Bioinformatics is an umbrella term for many different methods, depending on
+what analysis you want to do with your data (or model).
+In this sense, it's distinct from other scientific areas where it is possible
+to rely on a common set of libraries (numpy in linear algebra, for example), since a
+library supporting many disjoint methods tend to grow too big and hard to
+maintain.</p>
+<p>The environment also tends to be very diverse, with different languages being
+used to implement the software. Because it is hard to interoperate,
+the methods tend to be implemented in command line programs that are stitched
+together in pipelines, a workflow describing how to connect the input and output of many different tools to
+generate results.
+Because the basic unit is a command-line tool,
+pipelines tend to rely on standard operating system abstractions like
+files and pipes to make the tools communicate with each other. But since tools
+might have input requirements distinct from what the previous tool provides,
+many times it is necessary to do format conversion or other adaptations to make the
+pipeline work.</p>
+<p>Using tools as blackboxes, controllable through specific parameters at the
+command-line level, make exploratory analysis and algorithm reuse harder:
+if something needs to be investigated the user needs to resort to perturbations
+of the parameters or the input data, without access to the more feature-rich and
+meaningful abstraction happening inside the tool.</p>
+<p>Even if many languages are used for writing the software, most of the time there
+is some part written in C or C++ for performance reasons, and these tend to be
+the core data structures of the computational method. Because it is not easy to
+package your C/C++ code in a way that other people can readily use it,
+most of this code is reinvented over and over again, or is copy and pasted into
+codebases and start diverging over time. Rust helps solve this problem with the
+integrated package management, and due to the FFI it can also be reused inside
+other programs written in other languages.</p>
+<p>sourmash is not going to be Rust-only and abandon Python,
+and it would be crazy to do so when it has so many great exploratory tools
+for scientific discovery. But now we can also use our method in other languages
+and environment, instead of having our code stuck in one language.</p>
+<h3>Don't rewrite it all!</h3>
+<p>I could have gone all the way and rewrite sourmash in Rust<sup id="sf-sourmash-rust-3-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-3" class="simple-footnote" title="and risk being kicked out of the lab =P">3</a></sup>, but it would be incredibly disruptive for
+the current sourmash users and it would take way longer to pull off. Because
+Rust is so focused in supporting existing code, you can do a slow transition and
+reuse what you already have while moving into more and more Rust code.
+A great example is this one-day effort by Rob Patro to bring <a href="https://github.com/COMBINE-lab/cqf-rust">CQF</a> (a C
+codebase) into Rust, using <code>bindgen</code> (a generator of C bindings for Rust).
+Check <a href="https://blog.luizirber.org/2018/08/27/sourmash-wasm/">the Twitter thread</a> for more =]</p>
+<h3>Good scientific citizens</h3>
+<p>There is another MinHash implementation already written in Rust, <a href="https://github.com/onecodex/finch-rs">finch</a>.
+Early in my experiment I got an email from them asking if I wanted to work
+together, but since I wanted to learn the language I kept doing my thing. (They
+were totally cool with this, by the way). But the fun thing is that Rust has a
+pair of traits called <a href="https://doc.rust-lang.org/rust-by-example/conversion/from_into.html"><code>From</code> and <code>Into</code></a> that you can implement for your
+type, and so I <a href="https://github.com/luizirber/sourmash-rust/pull/1">did that</a> and now we can have interoperable
+implementations. This synergy allows <code>finch</code> to use <code>sourmash</code> methods,
+and vice versa.</p>
+<p>Maybe this sounds like a small thing, but I think it is really exciting. We can
+stop having incompatible but very similar methods, and instead all benefit from
+each other advances in a way that is supported by the language.</p>
+<h2>Next time!</h2>
+<p>Turns out Rust supports WebAssembly as a target,
+so... what if we run sourmash in the browser?
+That's what I'm covering in the <a href="https://blog.luizirber.org/2018/08/27/sourmash-wasm/">next blog post</a>,
+so stay tuned =]</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/100602525575698239">Thread on Mastodon</a></li>
+<li><a href="https://www.reddit.com/r/rust/comments/99vakd/blog_post_converting_c_to_rust_and_interoperate/">Thread on reddit</a></li>
+<li><a href="https://twitter.com/luizirber/status/1032779995129597952">Thread on Twitter</a></li>
+</ul>
+<!-- rust libs -->
+
+<!-- why rust -->
+
+<!-- WASM and demos -->
+
+<!-- python and FFI -->
+
+<!-- rust examples -->
+
+<!-- cbindgen -->
+
+<!-- rust for pythonistas -->
+
+<!-- understanding rust -->
+
+<!-- rust and bio -->
+
+<!-- rust error handling -->
+
+<!-- other notes
+
+- Implement Default trait for defaults (less verbose than ::new...)
+ https://doc.rust-lang.org/std/default/trait.Default.html
+
+--><hr /><h2>Footnotes</h2><ol><li id="sf-sourmash-rust-1"><p>The creator of the language is known to keep making up different
+explanations for the <a href="https://www.reddit.com/r/rust/comments/27jvdt/internet_archaeology_the_definitive_endall_source/">name of the language</a>,
+but in this case "oxidation" refers to the chemical process that creates
+rust, and rust is the closest thing to metal (metal being the hardware).
+There are many terrible puns in the Rust community. <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-1-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-rust-2"><p>Even more now that it hit 1.0,
+it is a really nice language <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-2-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-rust-3"><p>and risk
+being kicked out of the lab =P <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-3-back" class="simple-footnote-back">↩</a></p></li></ol>
+
+ luizirber
+ https://blog.luizirber.org/
+
+
+ Gabbleblotchits
+ Vogon Poetry, Computers and (some) biology
+
+ https://blog.luizirber.org/
+ 2025-04-17T02:14:15+00:00
+
+
+
+
+ Upload de Arquivos com Socket e Struct
+
+ tag:pythonclub.com.br,2018-05-17:/upload-de-arquivos-com-socket-e-struct.html
+ 2018-05-17T22:24:00+00:00
+ <p>Apesar de termos muitas formas de enviarmos arquivos para servidores hoje em dia, como por exemplo o <em>scp</em> e <em>rsync</em>, podemos usar o python com seus módulos <em>built-in</em> para enviar arquivos a servidores usando struct para serializar os dados e socket para criar uma conexão cliente/servidor.</p>
+<h3><em>Struct</em></h3>
+<p>O módulo <a href="https://docs.python.org/3/library/struct.html">struct</a> é usado para converter bytes no python em formatos do struct em C.
+Com ele podemos enviar num único conjunto de dados o nome de um arquivo e os bytes referentes ao seus dados.</p>
+<p>Struct também é utilizado para serializar diversos tipos de dados diferentes, como bytes, inteiros, floats além de outros, no nosso caso usaremos apenas bytes.</p>
+<p>Vamos criar um arquivo para serializar.</p>
+<div class="highlight"><pre><span></span><span class="err">!</span><span class="n">echo</span> <span class="s2">"Upload de arquivos com sockets e struct</span><span class="se">\n</span><span class="s2">Criando um arquivo para serializar."</span> <span class="o">></span> <span class="n">arquivo_para_upload</span>
+</pre></div>
+
+
+<p>Agora em posse de um arquivo vamos criar nossa estrutura de bytes para enviar.</p>
+<div class="highlight"><pre><span></span><span class="n">arquivo</span> <span class="o">=</span> <span class="s2">"arquivo_para_upload"</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">arquivo</span><span class="p">,</span> <span class="s1">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">arq</span><span class="p">:</span>
+ <span class="n">dados_arquivo</span> <span class="o">=</span> <span class="n">arq</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
+ <span class="n">serializar</span> <span class="o">=</span> <span class="n">struct</span><span class="o">.</span><span class="n">Struct</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">s </span><span class="si">{}</span><span class="s2">s"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">arquivo</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">dados_arquivo</span><span class="p">)))</span>
+ <span class="n">dados_upload</span> <span class="o">=</span> <span class="n">serializar</span><span class="o">.</span><span class="n">pack</span><span class="p">(</span><span class="o">*</span><span class="p">[</span><span class="n">arquivo</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="n">dados_arquivo</span><span class="p">])</span>
+</pre></div>
+
+
+<p>Por padrão, struct usa caracteres no início da sequência dos dados para definir a ordem dos bytes, tamanho e alinhamento dos bytes nos dados empacotados.
+Esses caracteres podem ser vistos na <a href="https://docs.python.org/3/library/struct.html#byte-order-size-and-alignment">seção 7.1.2.1</a> da documentação.
+Como não definimos, será usado o <strong>@</strong> que é o padrão.</p>
+<p>Nessa linha:</p>
+<div class="highlight"><pre><span></span><span class="n">serializar</span> <span class="o">=</span> <span class="n">struct</span><span class="o">.</span><span class="n">Struct</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">s </span><span class="si">{}</span><span class="s2">s"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">arquivo</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">dados_arquivo</span><span class="p">)))</span>
+</pre></div>
+
+
+<p>definimos que nossa estrutura serializada seria de dois conjuntos de caracteres, a primeira com o tamanho do nome do arquivo, e a segunda com o tamanho total dos dados lidos em</p>
+<div class="highlight"><pre><span></span><span class="n">dados_arquivo</span> <span class="o">=</span> <span class="n">arq</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
+</pre></div>
+
+
+<p>Se desempacotarmos os dados, teremos uma lista com o nome do arquivo e os dados lidos anteriormente.</p>
+<div class="highlight"><pre><span></span><span class="n">serializar</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">dados_upload</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="err">b'arquivo_para_upload'</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="n">serializar</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">dados_upload</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="err">b'Upload de arquivos com sockets e struct\\nCriando um arquivo para serializar.\n'</span>
+</pre></div>
+
+
+<p>Agora de posse dos nossos dados já serializados, vamos criar um cliente e um servidor com socket para transferirmos nosso arquivo.</p>
+<h3><em>Socket</em></h3>
+<p>O modulo <a href="https://docs.python.org/3/library/socket.html">socket</a> prove interfaces de socket BSD, disponiveis em praticamente todos os sistemas operacionais.</p>
+<h4>Familias de sockets</h4>
+<p>Diversas famílias de sockets podem ser usadas para termos acessos a objetos que nos permitam fazer chamadas de sistema.
+Mais informações sobre as famílias podem ser encontradas na <a href="https://docs.python.org/3/library/socket.html#socket-families">seção 18.1.1</a> da documentação. No nosso exemplo usaremos a AF_INET.</p>
+<h4>AF_INET</h4>
+<p><strong>AF_INET</strong> precisa basicamente de um par de dados, contendo um endereço IPv4 e uma porta para ser instanciada.
+Para endereços IPv6 o modulo disponibiliza o <strong>AF_INET6</strong></p>
+<h4>Constante [SOCK_STREAM]</h4>
+<p>As constantes representam as famílias de sockets, como a constante AF_INET e os protocolos usados como parâmetros para o modulo socket.
+Um dos protocolos mais usados encontrados na maioria dos sistemas é o SOCK_STREAM.</p>
+<p>Ele é um protocolo baseado em comunicação que permite que duas partes estabeleçam uma conexão e conversem entre si.</p>
+<h3><em>Servidor e cliente socket</em></h3>
+<p>Como vamos usar um protocolo baseado em comunicação, iremos construir o servidor e cliente paralelamente para um melhor entendimento.</p>
+<blockquote>
+<p>Servidor</p>
+</blockquote>
+<p>Para esse exemplo eu vou usar a porta 6124 para o servidor, ela esta fora da range reservada pela IANA para sistemas conhecidos, que vai de 0-1023.</p>
+<p>Vamos importar a bilioteca socket e definir um host e porta para passarmos como parametro para a constante AF_INET.</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">socket</span>
+<span class="n">host</span> <span class="o">=</span> <span class="s2">"127.0.0.1"</span>
+<span class="n">porta</span> <span class="o">=</span> <span class="mi">6124</span>
+<span class="n">sock</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Agora usaremos o metodo bind para criarmos um ponto de conexão para nosso cliente. Esse método espera por uma tupla contento o host e porta como parâmetros.</p>
+<div class="highlight"><pre><span></span><span class="n">sock</span><span class="o">.</span><span class="n">bind</span><span class="p">((</span><span class="n">host</span><span class="p">,</span> <span class="n">porta</span><span class="p">))</span>
+</pre></div>
+
+
+<p>Agora vamos colocar nosso servidor socket em modo escuta com o metodo listen. Esse método recebe como parâmetro um número inteiro (<strong>backlog</strong>) definindo qual o tamanho da fila que será usada para receber pacotes SYN até dropar a conexão. Usaremos um valor baixo o que evita SYN flood na rede. Mais informações sobre <em>backlog</em> podem ser encontradas na <a href="https://tools.ietf.org/html/rfc7413">RFC 7413</a>.</p>
+<div class="highlight"><pre><span></span><span class="n">sock</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Agora vamos colocar o nosso socket em um loop esperando por uma conexão e um início de conversa. Pra isso vamos usar o metodo <em>accept</em> que nos devolve uma tupla, onde o primeiro elemento é um novo objeto socket para enviarmos e recebermos informações, e o segundo contendo informações sobre o endereço de origem e porta usada pelo cliente.</p>
+<p><strong>Vamos criar um diretório para salvar nosso novo arquivo.</strong></p>
+<div class="highlight"><pre><span></span><span class="err">!</span><span class="n">mkdir</span> <span class="n">arquivos_recebidos</span>
+</pre></div>
+
+
+<p><em>Os dados são enviados sempre em bytes</em>. <strong>Leia os comentários</strong></p>
+<div class="highlight"><pre><span></span><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="n">novo_sock</span><span class="p">,</span> <span class="n">cliente</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span>
+ <span class="k">with</span> <span class="n">novo_sock</span><span class="p">:</span> <span class="c1"># Caso haja uma nova conexão</span>
+ <span class="n">ouvir</span> <span class="o">=</span> <span class="n">novo_sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> <span class="c1"># Colocamos nosso novo objeto socket para ouvir</span>
+ <span class="k">if</span> <span class="n">ouvir</span> <span class="o">!=</span> <span class="sa">b</span><span class="s2">""</span><span class="p">:</span> <span class="c1"># Se houver uma mensagem...</span>
+ <span class="sd">"""</span>
+<span class="sd"> Aqui usaremos os dados enviados na mensagem para criar nosso serielizador.</span>
+
+<span class="sd"> Com ele criado poderemos desempacotar os dados assim que recebermos.</span>
+<span class="sd"> Veja no cliente mais abaixo qual a primeira mensagem enviada.</span>
+<span class="sd"> """</span>
+ <span class="n">mensagem</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">dados</span> <span class="o">=</span> <span class="n">ouvir</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">":"</span><span class="p">)</span>
+ <span class="n">serializar</span> <span class="o">=</span> <span class="n">struct</span><span class="o">.</span><span class="n">Struct</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">s </span><span class="si">{}</span><span class="s2">s"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">nome</span><span class="o">.</span><span class="n">split</span><span class="p">()[</span><span class="mi">0</span><span class="p">]),</span> <span class="nb">int</span><span class="p">(</span><span class="n">dados</span><span class="o">.</span><span class="n">split</span><span class="p">()[</span><span class="mi">0</span><span class="p">])))</span>
+ <span class="n">novo_sock</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"Pode enviar!"</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span> <span class="c1"># Enviaremos uma mensagem para o cliente enviar os dados.</span>
+ <span class="n">dados</span> <span class="o">=</span> <span class="n">novo_sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> <span class="c1"># Agora iremos esperar por eles.</span>
+ <span class="n">nome</span><span class="p">,</span> <span class="n">arquivo</span> <span class="o">=</span> <span class="n">serializar</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">dados</span><span class="p">)</span> <span class="c1"># Vamos desempacotar os dados</span>
+ <span class="sd">"""</span>
+<span class="sd"> Agora enviamos uma mensagem dizendo que o arquivo foi recebido.</span>
+
+<span class="sd"> E iremos salva-lo no novo diretório criado.</span>
+<span class="sd"> """</span>
+ <span class="n">novo_sock</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"Os dados do arquivo </span><span class="si">{}</span><span class="s2"> foram enviados."</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">nome</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span>
+ <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s2">"arquivos_recebidos/</span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">nome</span><span class="o">.</span><span class="n">decode</span><span class="p">()),</span> <span class="s1">'wb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">novo_arquivo</span><span class="p">:</span>
+ <span class="n">novo_arquivo</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">arquivo</span><span class="p">)</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s2">"Arquivo </span><span class="si">{}</span><span class="s2"> salvo em arquivos_recebidos."</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">nome</span><span class="o">.</span><span class="n">decode</span><span class="p">()))</span>
+
+ <span class="n">Arquivo</span> <span class="n">arquivo_para_upload</span> <span class="n">salvo</span> <span class="n">em</span> <span class="n">arquivos_recebidos</span><span class="o">.</span>
+</pre></div>
+
+
+<blockquote>
+<p>Cliente</p>
+</blockquote>
+<p>Nosso cliente irá usar o metodo <em>connect</em> para se conectar no servidor e a partir dai começar enviar e receber mensagens. Ele também recebe como parâmetros uma tupla com o host e porta de conexão do servidor.</p>
+<div class="highlight"><pre><span></span><span class="n">host</span> <span class="o">=</span> <span class="s1">'127.0.0.1'</span>
+<span class="n">porta</span> <span class="o">=</span> <span class="mi">6124</span>
+<span class="n">sock</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span> <span class="c1"># Cria nosso objeto socket</span>
+<span class="n">sock</span><span class="o">.</span><span class="n">connect</span><span class="p">((</span><span class="n">host</span><span class="p">,</span> <span class="n">porta</span><span class="p">))</span>
+<span class="n">sock</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"Enviarei um arquivo chamado: </span><span class="si">{}</span><span class="s2"> contendo: </span><span class="si">{}</span><span class="s2"> bytes"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
+ <span class="n">arquivo</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">dados_arquivo</span><span class="p">))</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span> <span class="c1"># Enviamos a mensagem com o nome e tamanho do arquivo.</span>
+<span class="n">ouvir</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> <span class="c1"># Aguardamos uma mensagem de confirmação do servidor.</span>
+<span class="k">if</span> <span class="n">ouvir</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> <span class="o">==</span> <span class="s2">"Pode enviar!"</span><span class="p">:</span>
+ <span class="n">sock</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">dados_upload</span><span class="p">)</span> <span class="c1"># Enviamos os dados empacotados.</span>
+ <span class="n">resposta</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> <span class="c1"># Aguardamos a confirmação de que os dados foram enviados.</span>
+ <span class="nb">print</span><span class="p">(</span><span class="n">resposta</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span>
+
+ <span class="n">Os</span> <span class="n">dados</span> <span class="n">do</span> <span class="n">arquivo</span> <span class="n">arquivo_para_upload</span> <span class="n">foram</span> <span class="n">enviados</span><span class="o">.</span>
+</pre></div>
+
+
+<p>Agora podemos checar nossos arquivos e ver se eles foram salvos corretamente.</p>
+<div class="highlight"><pre><span></span><span class="err">!</span><span class="n">md5sum</span> <span class="n">arquivo_para_upload</span><span class="p">;</span> <span class="n">md5sum</span> <span class="n">arquivos_recebidos</span><span class="o">/</span><span class="n">arquivos_para_upload</span>
+
+ <span class="mf">605e99</span><span class="n">b3d873df0b91d8834ff292d320</span> <span class="n">arquivo_para_upload</span>
+ <span class="mf">605e99</span><span class="n">b3d873df0b91d8834ff292d320</span> <span class="n">arquivos_recebidos</span><span class="o">/</span><span class="n">arquivo_para_upload</span>
+</pre></div>
+
+
+<p>Com base nesse exemplo, podem ser enviados diversos arquivos, sendo eles texto, arquivos compactados ou binários.</p>
+<p>Sem mais delongas, fiquem com Cher e até a próxima!</p>
+<p>Abraços.</p>
+
+ Silvio Ap Silva
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Monitorando Ips Duplicados na Rede
+
+ tag:pythonclub.com.br,2018-05-15:/monitorando-ips-duplicados-na-rede.html
+ 2018-05-15T13:24:00+00:00
+ <p>Muitos administradores de redes e sysadmins encontram problemas de conectividade nos ambientes que administram e por muitas vezes o problema é um simples IP duplicado causando todo mal estar. Agora veremos como usar o scapy e defaultdict da lib collections para monitorar esses IPs.</p>
+<h3>Scapy</h3>
+<p>O Scapy é uma poderosa biblioteca de manipulação de pacotes interativa, com abrangencia a uma enorme quantidade de protocolos provenientes da suite TCP/IP.
+Mais informações sobre o scpay pode ser encontrada na <a href="http://scapy.readthedocs.io/en/latest/index.html"><strong>documentação oficial</strong></a>.
+Nesse caso em especifico iremos utilizar do scapy a metaclasse <em>ARP</em> e a função <em>sniff</em>.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">scapy.all</span> <span class="kn">import</span> <span class="n">ARP</span><span class="p">,</span> <span class="n">sniff</span>
+</pre></div>
+
+
+<h4>sniff</h4>
+<p>Vamos usar a função sniff para monitorar os pacotes que trafegam na rede usando o protocolo ARP.
+Pra isso vamos utilizar dela quatro parametros basicos:</p>
+<div class="highlight"><pre><span></span><span class="n">sniff</span><span class="p">(</span><span class="n">prn</span><span class="o">=</span><span class="n">pacotes</span><span class="p">,</span> <span class="nb">filter</span><span class="o">=</span><span class="s2">"arp"</span><span class="p">,</span> <span class="n">iface</span><span class="o">=</span><span class="n">interface</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
+</pre></div>
+
+
+<ul>
+<li>
+<p>prn, chama uma função para ser aplicada a cada pacote capturado pelo sniff.</p>
+</li>
+<li>
+<p>filter, irá filtrar todos os pacotes que contiverem o protocolo ARP.</p>
+</li>
+<li>
+<p>iface, determina a interface de rede que será monitorada.</p>
+</li>
+<li>
+<p>timeout, irá determinar que nosso monitoramento da rede se dara por 60 segundos.</p>
+</li>
+</ul>
+<h4>ARP</h4>
+<p>ARP é uma metaclasse de pacotes com dados sobre o protocolo arp pertencente a camada de enlace de dados.
+Iremos utilizar essa metaclasse para filtrar os pacotes com informações de pacotes com respostas a requisições arp. (opcode == 2 [is at])
+As informações sobre o protocolo ARP podem serm encontradas na <a href="https://tools.ietf.org/html/rfc826">rfc826</a> no site do IETF.</p>
+<h3>collections.defaultdict</h3>
+<p>defaultdict é uma subclasse de dict que prove uma instancia de variavel para a chamada de uma chave inexistente.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">defaultdict</span>
+<span class="n">list_ips</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">set</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Basicamente nossa função irá monitorar por um certo tempo o trafego de pacotes pela rede adicionar a nossa variavel <em>list_ips</em> o endereço ou endereços MAC encontrados.</p>
+<h3>Definindo a função que será passada como parametro para o sniff.</h3>
+<p>Para cada pacote capturado pela função sniff, será checado se o opcode corresponde a um response do protocolo arp.
+Caso seja, sera adicionado a nossa defaultdict.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">pacotes</span><span class="p">(</span><span class="n">pacote</span><span class="p">):</span>
+ <span class="sd">"""Checa se o valor do opcode dentro do protocolo arp é igual a 2."""</span>
+ <span class="k">if</span> <span class="n">pacote</span><span class="p">[</span><span class="n">ARP</span><span class="p">]</span><span class="o">.</span><span class="n">op</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
+ <span class="c1"># Se for adiciona o ip de origem e seu mac à dict list_ips</span>
+ <span class="n">list_ips</span><span class="p">[</span><span class="n">pacote</span><span class="p">[</span><span class="n">ARP</span><span class="p">]</span><span class="o">.</span><span class="n">psrc</span><span class="p">]</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">pacote</span><span class="p">[</span><span class="n">ARP</span><span class="p">]</span><span class="o">.</span><span class="n">hwsrc</span><span class="p">)</span>
+</pre></div>
+
+
+<h3>Limpando a tabela arp</h3>
+<p>Para que seja feita novas requisições arp, iremos limpar nossa tabela arp e iniciar o monitoramento da rede.
+Pra isso iremos usar o comando arp, dentro do shell do sistema. <em>(Como uso <a href="https://www.freebsd.org">FreeBSD</a> vou definir uma função chamando um comando pelo csh)</em></p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">os</span>
+<span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s1">'which arp'</span><span class="p">)</span>
+<span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">sbin</span><span class="o">/</span><span class="n">arp</span>
+</pre></div>
+
+
+<p>Com posse do caminho do comando arp, irei definir uma função que limpe a tabela e inicie o monitore a rede por 60 segundos.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">monitorar</span><span class="p">(</span><span class="n">interface</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> O comando arp no FreeBSD usa os parametros:</span>
+
+<span class="sd"> -d para deletar as entradas</span>
+<span class="sd"> -i para declarar a interface</span>
+<span class="sd"> -a para representar todas entradas a serem deletas.</span>
+<span class="sd"> """</span>
+ <span class="n">cmd</span> <span class="o">=</span> <span class="s2">"/usr/sbin/arp -d -i </span><span class="si">{}</span><span class="s2"> -a"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">interface</span><span class="p">)</span>
+ <span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="n">cmd</span><span class="p">)</span>
+ <span class="n">sniff</span><span class="p">(</span><span class="n">prn</span><span class="o">=</span><span class="n">pacotes</span><span class="p">,</span> <span class="nb">filter</span><span class="o">=</span><span class="s2">"arp"</span><span class="p">,</span> <span class="n">iface</span><span class="o">=</span><span class="n">interface</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
+</pre></div>
+
+
+<p>E por ultimo chamar a função de monitoramento.
+No meu caso eu vou monitorar a interface <strong>em0</strong>.</p>
+<div class="highlight"><pre><span></span><span class="n">monitorar</span><span class="p">(</span><span class="s2">"em0"</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Agora só conferir as entradas em nossa dict.</p>
+<div class="highlight"><pre><span></span><span class="k">for</span> <span class="n">ip</span> <span class="ow">in</span> <span class="n">list_ips</span><span class="p">:</span>
+ <span class="nb">print</span> <span class="s2">"IP: </span><span class="si">{}</span><span class="s2"> -> MACs: </span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ip</span><span class="p">,</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">list_ips</span><span class="p">[</span><span class="n">ip</span><span class="p">])))</span>
+
+<span class="n">IP</span><span class="p">:</span> <span class="mf">192.168</span><span class="o">.</span><span class="mf">213.1</span> <span class="o">-></span> <span class="n">MACs</span><span class="p">:</span> <span class="mi">00</span><span class="p">:</span><span class="mi">90</span><span class="p">:</span><span class="mi">0</span><span class="n">b</span><span class="p">:</span><span class="mi">49</span><span class="p">:</span><span class="mi">3</span><span class="n">d</span><span class="p">:</span><span class="mi">0</span><span class="n">a</span>
+<span class="n">IP</span><span class="p">:</span> <span class="mf">192.168</span><span class="o">.</span><span class="mf">213.10</span> <span class="o">-></span> <span class="n">MACs</span><span class="p">:</span> <span class="mi">08</span><span class="p">:</span><span class="mi">00</span><span class="p">:</span><span class="mi">27</span><span class="p">:</span><span class="n">bf</span><span class="p">:</span><span class="mi">52</span><span class="p">:</span><span class="mi">6</span><span class="n">d</span><span class="p">,</span> <span class="n">a0</span><span class="p">:</span><span class="n">f3</span><span class="p">:</span><span class="n">c1</span><span class="p">:</span><span class="mi">03</span><span class="p">:</span><span class="mi">74</span><span class="p">:</span><span class="mi">6</span><span class="n">a</span>
+</pre></div>
+
+
+<p>Eu uso um script rodando nos switchs e gateway da rede que me enviam mensagens assim que ips duplicados são encontrados na rede.
+Também da pra usar o <strong>arping</strong> do scpay para fazer as requisições arp e coletar os responses.</p>
+<p>Abraços.</p>
+
+ Silvio Ap Silva
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ C++ para Pythonistas – Introdução e iteradores
+
+ http://lauro.wordpress.com/?p=591
+ 2018-05-01T02:10:28+00:00
+ C++ pode ter a fama de ser uma linguagem com complexidade comparável à legislação tributária brasileira, mas ao mesmo tempo é uma linguagem extremamente poderosa, podendo ser usada tanto desde microcontroladores até super computadores, sem contar satélites espaciais. Tamanha complexidade pode assustar quem vem de linguagens mais simples como Python (malloc? rvalue references?). Uma ajuda … <a class="more-link" href="https://lauro.wordpress.com/2018/04/30/c-para-pythonistas-introducao-e-iteradores/">Continuar lendo <span class="meta-nav">→</span></a>
+
+ Lauro Moura
+ https://lauro.wordpress.com
+
+
+ Lauro Moura
+ Software Livre, programação, etc...
+
+ https://lauro.wordpress.com/feed/
+ 2025-04-17T02:14:45+00:00
+
+
+
+
+ Algumas Razões Para Amar o PostgresSQL
+
+ http://mindbending.org/pt/algumas-razoes-para-amar-o-postgressql
+ 2018-04-19T18:02:00+00:00
+ <p>Geralmente, quando se fala em SGBDs OpenSource, a primeira resposta que se ouve é MySQL/MariaDB. Eu sempre torço meu nariz para respostas como essa… Implicancia pessoal? Talvez um pouco, mas existem <a class="reference external" href="https://www.cybertec-postgresql.com/en/why-favor-postgresql-over-mariadb-mysql/" target="_blank">muitos fundamentos</a>…</p>
+<p><a href="http://mindbending.org/pt/algumas-razoes-para-amar-o-postgressql">Algumas Razões Para Amar o PostgresSQL</a> é um artigo original de <a href="http://mindbending.org/pt">Mind Bending</a></p>
+
+ Magnun Leno
+ http://mindbending.org/pt/
+
+
+ Mind Bending em Portugês
+ Feeds RSS para os posts em português do blog Mind Bending
+
+ http://feeds.feedburner.com/MindBending-pt
+ 2025-04-17T02:14:46+00:00
+
+
+
+
+ Preservando a ordem de sequências ao remover duplicatas
+
+ http://pythonhelp.wordpress.com/?p=1984
+ 2018-02-26T01:31:38+00:00
+ Imagine que você tenha uma lista com as URLs extraídas de uma página web e queira eliminar as duplicatas da mesma. Transformar a lista em um conjunto (set) talvez seja a forma mais comum de se fazer isso. Tipo assim: Porém, observe que perdemos a ordem original dos elementos. Esse é um efeito colateral indesejado […]
+
+ Python Help
+ https://pythonhelp.wordpress.com
+
+
+ python – Python Help
+
+ https://pythonhelp.wordpress.com/tag/python/feed/
+ 2025-04-17T02:14:52+00:00
+
+
+
+
+ Django Rest Framework - #3 Class Based Views
+
+ tag:pythonclub.com.br,2018-02-15:/django-rest-framework-class-based-views.html
+ 2018-02-16T01:00:00+00:00
+ <ul>
+<li>0 - <a href="http://pythonclub.com.br/django-rest-framework-quickstart.html">Quickstart</a></li>
+<li>1 - <a href="http://pythonclub.com.br/django-rest-framework-serialization.html">Serialization</a></li>
+<li>2 - <a href="http://pythonclub.com.br/django-rest-framework-requests-responses.html">Requests & Responses</a></li>
+<li>3 - <strong>Class based views</strong></li>
+</ul>
+<p>Este post é continuação do post <a href="http://pythonclub.com.br/django-rest-framework-requests-responses.html">Django Rest Framework Requests & Responses</a>.</p>
+<p>Finalmente chegamos as views baseadas em classes. A grande vantagem é que com poucas linhas de código já temos nossa API pronta.</p>
+<p>Veja como fica a <a href="https://github.com/rg3915/drf/blob/b0aa989ffc756e6dc5f65e172dfb43d47127d743/core/views.py">views.py</a>:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.http</span> <span class="kn">import</span> <span class="n">Http404</span>
+<span class="kn">from</span> <span class="nn">rest_framework.views</span> <span class="kn">import</span> <span class="n">APIView</span>
+<span class="kn">from</span> <span class="nn">rest_framework.response</span> <span class="kn">import</span> <span class="n">Response</span>
+<span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">status</span>
+<span class="kn">from</span> <span class="nn">core.models</span> <span class="kn">import</span> <span class="n">Person</span>
+<span class="kn">from</span> <span class="nn">core.serializers</span> <span class="kn">import</span> <span class="n">PersonSerializer</span>
+
+
+<span class="k">class</span> <span class="nc">PersonList</span><span class="p">(</span><span class="n">APIView</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> List all persons, or create a new person.</span>
+<span class="sd"> """</span>
+
+ <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+ <span class="n">persons</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">persons</span><span class="p">,</span> <span class="n">many</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">serializer</span><span class="o">.</span><span class="n">is_valid</span><span class="p">():</span>
+ <span class="n">serializer</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_201_CREATED</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">errors</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_400_BAD_REQUEST</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">PersonDetail</span><span class="p">(</span><span class="n">APIView</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Retrieve, update or delete a person instance.</span>
+<span class="sd"> """</span>
+
+ <span class="k">def</span> <span class="nf">get_object</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">pk</span><span class="p">):</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">pk</span><span class="o">=</span><span class="n">pk</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">Person</span><span class="o">.</span><span class="n">DoesNotExist</span><span class="p">:</span>
+ <span class="k">raise</span> <span class="n">Http404</span>
+
+ <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+ <span class="n">person</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_object</span><span class="p">(</span><span class="n">pk</span><span class="p">)</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">person</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">put</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+ <span class="n">person</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_object</span><span class="p">(</span><span class="n">pk</span><span class="p">)</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">person</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">serializer</span><span class="o">.</span><span class="n">is_valid</span><span class="p">():</span>
+ <span class="n">serializer</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">errors</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_400_BAD_REQUEST</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">delete</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+ <span class="n">person</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_object</span><span class="p">(</span><span class="n">pk</span><span class="p">)</span>
+ <span class="n">person</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_204_NO_CONTENT</span><span class="p">)</span>
+</pre></div>
+
+
+<p>E <code>urls.py</code>:</p>
+<div class="highlight"><pre><span></span><span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
+ <span class="n">path</span><span class="p">(</span><span class="s1">'persons/'</span><span class="p">,</span> <span class="n">views</span><span class="o">.</span><span class="n">PersonList</span><span class="o">.</span><span class="n">as_view</span><span class="p">()),</span>
+ <span class="n">path</span><span class="p">(</span><span class="s1">'persons/<int:pk>/'</span><span class="p">,</span> <span class="n">views</span><span class="o">.</span><span class="n">PersonDetail</span><span class="o">.</span><span class="n">as_view</span><span class="p">()),</span>
+<span class="p">]</span>
+</pre></div>
+
+
+<h2>Usando Mixins</h2>
+<p>Repare que no exemplo anterior tivemos que definir os métodos <code>get()</code>, <code>post()</code>, <code>put()</code> e <code>delete()</code>. Podemos reduzir ainda mais esse código com o uso de mixins.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">mixins</span>
+<span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">generics</span>
+<span class="kn">from</span> <span class="nn">core.models</span> <span class="kn">import</span> <span class="n">Person</span>
+<span class="kn">from</span> <span class="nn">core.serializers</span> <span class="kn">import</span> <span class="n">PersonSerializer</span>
+
+
+<span class="k">class</span> <span class="nc">PersonList</span><span class="p">(</span><span class="n">mixins</span><span class="o">.</span><span class="n">ListModelMixin</span><span class="p">,</span>
+ <span class="n">mixins</span><span class="o">.</span><span class="n">CreateModelMixin</span><span class="p">,</span>
+ <span class="n">generics</span><span class="o">.</span><span class="n">GenericAPIView</span><span class="p">):</span>
+ <span class="n">queryset</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">PersonSerializer</span>
+
+ <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">PersonDetail</span><span class="p">(</span><span class="n">mixins</span><span class="o">.</span><span class="n">RetrieveModelMixin</span><span class="p">,</span>
+ <span class="n">mixins</span><span class="o">.</span><span class="n">UpdateModelMixin</span><span class="p">,</span>
+ <span class="n">mixins</span><span class="o">.</span><span class="n">DestroyModelMixin</span><span class="p">,</span>
+ <span class="n">generics</span><span class="o">.</span><span class="n">GenericAPIView</span><span class="p">):</span>
+ <span class="n">queryset</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">PersonSerializer</span>
+
+ <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">retrieve</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">put</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">delete</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">destroy</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+</pre></div>
+
+
+<h2>Usando generic class-based views</h2>
+<p>E para finalizar usamos <code>ListCreateAPIView</code> e <code>RetrieveUpdateDestroyAPIView</code> que já tem todos os métodos embutidos.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">generics</span>
+<span class="kn">from</span> <span class="nn">core.models</span> <span class="kn">import</span> <span class="n">Person</span>
+<span class="kn">from</span> <span class="nn">core.serializers</span> <span class="kn">import</span> <span class="n">PersonSerializer</span>
+
+
+<span class="k">class</span> <span class="nc">PersonList</span><span class="p">(</span><span class="n">generics</span><span class="o">.</span><span class="n">ListCreateAPIView</span><span class="p">):</span>
+ <span class="n">queryset</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">PersonSerializer</span>
+
+
+<span class="k">class</span> <span class="nc">PersonDetail</span><span class="p">(</span><span class="n">generics</span><span class="o">.</span><span class="n">RetrieveUpdateDestroyAPIView</span><span class="p">):</span>
+ <span class="n">queryset</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">PersonSerializer</span>
+</pre></div>
+
+
+<p>Versão final de <a href="https://github.com/rg3915/drf/blob/263ca63e5c8e2dd2e0f5d6c88c5733fcfdab4f74/core/views.py">views.py</a>.</p>
+<p>Abraços.</p>
+
+ Regis da Silva
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Django Rest Framework - #2 Requests and Responses
+
+ tag:pythonclub.com.br,2018-02-14:/django-rest-framework-requests-responses.html
+ 2018-02-15T01:00:00+00:00
+ <ul>
+<li>0 - <a href="http://pythonclub.com.br/django-rest-framework-quickstart.html">Quickstart</a></li>
+<li>1 - <a href="http://pythonclub.com.br/django-rest-framework-serialization.html">Serialization</a></li>
+<li>2 - <strong>Requests & Responses</strong></li>
+<li>3 - <a href="http://pythonclub.com.br/django-rest-framework-class-based-views.html">Class based views</a></li>
+</ul>
+<p>Este post é continuação do post <a href="http://pythonclub.com.br/django-rest-framework-serialization.html">Django Rest Framework Serialization</a>.</p>
+<p>O uso de <em>requests</em> e <em>responses</em> torna nossa api mais flexível. A funcionalidade principal do objeto <strong>Request</strong> é o atributo <code>request.data</code>, que é semelhante ao <code>request.POST</code>, mas é mais útil para trabalhar com APIs.</p>
+<h2>Objeto Response</h2>
+<p>Introduzimos aqui um objeto <code>Response</code>, que é um tipo de <code>TemplateResponse</code> que leva conteúdo não renderizado e usa a negociação de conteúdo para determinar o tipo de conteúdo correto para retornar ao cliente.</p>
+<div class="highlight"><pre><span></span><span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="c1"># Renderiza para o tipo de conteúdo conforme solicitado pelo cliente.</span>
+</pre></div>
+
+
+<p>Repare também no uso de <em>status code</em> pré definidos, exemplo: <code>status.HTTP_400_BAD_REQUEST</code>.</p>
+<p>E usamos o decorador <code>@api_view</code> para trabalhar com funções. Ou <code>APIView</code> para classes.</p>
+<p>Nosso código ficou assim:</p>
+<div class="highlight"><pre><span></span><span class="c1"># views.py</span>
+<span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">status</span>
+<span class="kn">from</span> <span class="nn">rest_framework.decorators</span> <span class="kn">import</span> <span class="n">api_view</span>
+<span class="kn">from</span> <span class="nn">rest_framework.response</span> <span class="kn">import</span> <span class="n">Response</span>
+<span class="kn">from</span> <span class="nn">core.models</span> <span class="kn">import</span> <span class="n">Person</span>
+<span class="kn">from</span> <span class="nn">core.serializers</span> <span class="kn">import</span> <span class="n">PersonSerializer</span>
+
+
+<span class="nd">@api_view</span><span class="p">([</span><span class="s1">'GET'</span><span class="p">,</span> <span class="s1">'POST'</span><span class="p">])</span>
+<span class="k">def</span> <span class="nf">person_list</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> List all persons, or create a new person.</span>
+<span class="sd"> """</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'GET'</span><span class="p">:</span>
+ <span class="n">persons</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">persons</span><span class="p">,</span> <span class="n">many</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+
+ <span class="k">elif</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'POST'</span><span class="p">:</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">serializer</span><span class="o">.</span><span class="n">is_valid</span><span class="p">():</span>
+ <span class="n">serializer</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_201_CREATED</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">errors</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_400_BAD_REQUEST</span><span class="p">)</span>
+
+
+<span class="nd">@api_view</span><span class="p">([</span><span class="s1">'GET'</span><span class="p">,</span> <span class="s1">'PUT'</span><span class="p">,</span> <span class="s1">'DELETE'</span><span class="p">])</span>
+<span class="k">def</span> <span class="nf">person_detail</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Retrieve, update or delete a person instance.</span>
+<span class="sd"> """</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">person</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">pk</span><span class="o">=</span><span class="n">pk</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">Person</span><span class="o">.</span><span class="n">DoesNotExist</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_404_NOT_FOUND</span><span class="p">)</span>
+
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'GET'</span><span class="p">:</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">person</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+
+ <span class="k">elif</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'PUT'</span><span class="p">:</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">person</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">serializer</span><span class="o">.</span><span class="n">is_valid</span><span class="p">():</span>
+ <span class="n">serializer</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">errors</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_400_BAD_REQUEST</span><span class="p">)</span>
+
+ <span class="k">elif</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'DELETE'</span><span class="p">:</span>
+ <span class="n">person</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_204_NO_CONTENT</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Veja no <a href="https://github.com/rg3915/drf/commit/69205da9262415eaf83ff04f22a635e912880a60">GitHub</a>.</p>
+<h2>Usando sufixo opcional</h2>
+<p>Em <code>core/urls.py</code> acrescente</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">rest_framework.urlpatterns</span> <span class="kn">import</span> <span class="n">format_suffix_patterns</span>
+
+<span class="o">...</span>
+
+<span class="n">urlpatterns</span> <span class="o">=</span> <span class="n">format_suffix_patterns</span><span class="p">(</span><span class="n">urlpatterns</span><span class="p">)</span>
+</pre></div>
+
+
+<p>E em <code>views.py</code> acrescente <code>format=None</code> como parâmetro das funções a seguir:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">person_list</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+
+<span class="k">def</span> <span class="nf">person_detail</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+</pre></div>
+
+
+<p>Com isso você pode chamar a api da seguinte forma:</p>
+<div class="highlight"><pre><span></span>http http://127.0.0.1:8000/persons.json <span class="c1"># ou</span>
+http http://127.0.0.1:8000/persons.api
+</pre></div>
+
+
+<p>Até a próxima.</p>
+
+ Regis da Silva
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Programação funcional com Python #2 - Iteraveis e iteradores
+
+ tag:pythonclub.com.br,2017-12-23:/progrmacao-funcional-com-python-2.html
+ 2017-12-24T00:50:00+00:00
+ <h1>2. Iteráveis e iteradores</h1>
+<p>O que são iteráveis? Basicamente e a grosso modo, iteráveis em python são todos os objetos que implementam o método <code>__getitem__</code> ou <code>__iter__</code>. Beleza, vamos partir do simples.</p>
+<p>Quase todos os tipos de dados em python são iteráveis, por exemplo: listas, strings, tuplas, dicionários, conjuntos, etc...</p>
+<p>Vamos aos exemplos, eles são sempre mágicos:</p>
+<div class="highlight"><pre><span></span><span class="n">lista</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">]</span>
+
+<span class="c1"># iteração</span>
+<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">lista</span><span class="p">:</span>
+ <span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
+
+<span class="c1"># 1</span>
+<span class="c1"># 2</span>
+<span class="c1"># 3</span>
+<span class="c1"># 4</span>
+<span class="c1"># 5</span>
+</pre></div>
+
+
+<p>Era só isso? Sim, nem doeu, fala a verdade.</p>
+<p>Em python, o comando <code>for</code> nos fornece um iterador implícito. O que? Não entendi.</p>
+<p>O laço <code>for</code> em python itera em cada elemento da sequência. Como no exemplo, o <code>for</code>, ou <code>foreach</code>, no caso vai passando por cada elemento da sequência. Não é necessária a implementação de um index como na linguagem C, onde a iteração é explícita:</p>
+<div class="highlight"><pre><span></span><span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">></span> <span class="mi">10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
+ <span class="n">sequencia</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<h2>2.1 <code>__getitem__</code></h2>
+<p>O padrão de projeto iterator em python já vem implementado por padrão, como já foi dito antes. Basta que um objeto tenha os métodos <code>__iter__</code> ou <code>__getitem__</code> para que um laço possa ser utilizado.</p>
+<p>Vamos exemplificar:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">iteravel</span><span class="p">:</span>
+ <span class="sd">"""</span>
+<span class="sd"> Um objeto que implementa o `__getitem__` pode ser acessado por posição</span>
+<span class="sd"> """</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sequencia</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">seq</span> <span class="o">=</span> <span class="n">sequencia</span>
+
+ <span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">posicao</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Por exemplo, quando tentamos acessar um elemento da sequência usando</span>
+<span class="sd"> slice:</span>
+<span class="sd"> >>> iteravel[2]</span>
+
+<span class="sd"> O interpretador python chama o __getitem__ do objeto e nos retorna</span>
+<span class="sd"> a posição solicitada</span>
+
+<span class="sd"> um exemplo:</span>
+<span class="sd"> IN: lista = [1, 2, 3, 4]</span>
+<span class="sd"> IN: lista[0]</span>
+<span class="sd"> OUT: 1</span>
+<span class="sd"> """</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">seq</span><span class="p">[</span><span class="n">posicao</span><span class="p">]</span>
+</pre></div>
+
+
+<p>Então, pode-se compreender, sendo bem rústico, que todos os objetos que implementam <code>__getitem__</code> são iteráveis em python.</p>
+<h2>2.2 <code>__iter__</code></h2>
+<p>Agora os objetos que implementam <code>__iter__</code> tem algumas peculiaridades. Por exemplo, quando o iterável (vamos pensar no <code>for</code>) chamar a sequência, ela vai pedir o <code>__iter__</code> que vai retornar uma instância de si mesmo para o <code>for</code> e ele vai chamar o <code>__next__</code> até que a exceção <code>StopIteration</code> aconteça.</p>
+<p>Uma classe que implementa <code>__iter__</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">iteravel</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sequencia</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">sequencia</span>
+
+ <span class="k">def</span> <span class="fm">__next__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Neste caso, como o método pop do objeto data é chamado</span>
+<span class="sd"> (vamos pensar em uma lista) ele vai retornar o primeiro elemento</span>
+<span class="sd"> (o de index 0) e vai remove-lo da lista</span>
+
+<span class="sd"> E quando a sequência estiver vazia ele vai nos retornar um StopIteration</span>
+<span class="sd"> O que vai fazer com que a iteração acabe.</span>
+
+<span class="sd"> Porém, estamos iterando com pop, o que faz a nossa iteração ser a única,</span>
+<span class="sd"> pois ela não pode ser repetida, dado que os elementos foram</span>
+<span class="sd"> removidos da lista</span>
+<span class="sd"> """</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">sequencia</span><span class="p">:</span>
+ <span class="k">raise</span> <span class="ne">StopIteration</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">sequencia</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span>
+</pre></div>
+
+
+<p>Como é possível notar, o objeto com <code>__iter__</code> não necessita de um <code>__getitem__</code> e vise-versa. As diferenças partem do ponto em que um pode ser acessado por index/slice e outro não. Também um bom ponto é que nesse nosso caso, removemos os elementos da sequência, ou seja, ela se torna descartável.</p>
+<p>Esse conceito, de ser descartado, pode parecer um pouco estranho no início, mas economiza muita memória. E, como estamos falando de programação funcional, pode-se dizer que nossa sequência se torna imutável, pois não existe uma maneira de mudar os valores contidos na lista do objeto.</p>
+<p>Seguem dois links maravilhosos explicando sobre iteração em python:</p>
+<ul>
+<li>
+<p><a href="https://www.python.org/dev/peps/pep-0234/">PEP0243</a></p>
+</li>
+<li>
+<p><a href="https://www.youtube.com/watch?v=ULj7ejvuzI8">Iteração em Python: do básico ao genial</a></p>
+</li>
+</ul>
+<p>O primeiro é a PEP sobre as estruturas dos iteráveis e o segundo um video do Guru Luciano Ramalho explicando tudo sobre iteradores.</p>
+<p>Ah... Ia quase me esquecendo, se você não entendeu muita coisa sobre os dunders, você pode ler o <a href="https://docs.python.org/3/reference/datamodel.html#special-method-names">Python data model</a>. Obs: não me responsabilizo pelo programador melhor que você sairá desta página.</p>
+<p>Embora esse tópico seja talvez o mais curto, ele vai ser de fundamental importância para o entendimento de um pouco de tudo nesse 'Curso'. É sério. Vamos entender como trabalhar com iteráveis de uma maneira bonita no próximo tópico.</p>
+
+ Eduardo Mendes
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Programação funcional com Python #1 - Funções
+
+ tag:pythonclub.com.br,2017-12-08:/progrmacao-funcional-com-python-1.html
+ 2017-12-08T15:30:00+00:00
+ <h1>1. Funções</h1>
+<p>Como nem tudo são flores, vamos começar do começo e entender algumas características das funções do python (o objeto função) e dar uma revisada básica em alguns conceitos de função só pra gente não se perder no básico depois. Então o primeiro tópico vai se limitar a falar da estrutura básica das funções em python, sem entrar profundamente em cada um dos tópicos. Será uma explanação de código e abrir a cabeça para novas oportunidades de código mais pythonicos e que preferencialmente gere menos efeito colateral. Mas calma, não vamos ensinar a fazer funções, você já está cheio disso.</p>
+<h2>1.1 Funções como objeto de primeira classe</h2>
+<p>Funções como objeto de primeira classe, são funções que se comportam como qualquer tipo nativo de uma determinada linguagem. Por exemplo:</p>
+<div class="highlight"><pre><span></span><span class="c1"># uma lista</span>
+
+<span class="n">lista</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="s1">'str'</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">],</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">),</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">},</span> <span class="p">{</span><span class="mi">1</span><span class="p">:</span> <span class="s1">'um'</span><span class="p">}]</span>
+</pre></div>
+
+
+<p>Todos esses exemplos são tipos de objetos de primeira classe em Python, mas no caso as funções também são. Como assim? Pode-se passar funções como parâmetro de uma outra função, podemos armazenar funções em variáveis, pode-se definir funções em estruturas de dados:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Funções como objeto de primeira classe</span>
+
+<span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="c1"># a função anônima, lambda, foi armazenada em uma variável</span>
+
+<span class="k">def</span> <span class="nf">func_2</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">2</span>
+
+<span class="n">lista</span> <span class="o">=</span> <span class="p">[</span><span class="n">func</span><span class="p">,</span> <span class="n">func_2</span><span class="p">]</span> <span class="c1"># a variável que armazena a função foi inserida em uma estrutura, assim como uma função gerada com def</span>
+
+<span class="n">lista_2</span> <span class="o">=</span> <span class="p">[</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="c1"># aqui as funções foram definidas dentro de uma estrutura</span>
+</pre></div>
+
+
+<p>Como é possível notar, em python, as funções podem ser inseridas em qualquer contexto e também geradas em tempo de execução. Com isso nós podemos, além de inserir funções em estruturas, retornar funções, passar funções como parâmetro (HOFs), definir funções dentro de funções(closures) e assim por diante. Caso você tenha aprendido a programar usando uma linguagem em que as funções não são objetos de primeira classe, não se assuste. Isso faz parte da rotina comum do python. Preferencialmente, e quase obrigatoriamente, é melhor fazer funções simples, pequenas e de pouca complexidade para que elas não sofram interferência do meio externo, gerem menos manutenção e o melhor de tudo, possam ser combinadas em outras funções. Então vamos lá!</p>
+<h2>1.2 Funções puras</h2>
+<p>Funções puras são funções que não sofrem interferência do meio externo. Vamos começar pelo exemplo ruim:</p>
+<div class="highlight"><pre><span></span><span class="n">valor</span> <span class="o">=</span> <span class="mi">5</span>
+
+<span class="k">def</span> <span class="nf">mais_cinco</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">valor</span>
+
+<span class="k">assert</span> <span class="n">mais_cinco</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="mi">10</span> <span class="c1"># True</span>
+
+<span class="n">valor</span> <span class="o">=</span> <span class="mi">7</span>
+
+<span class="k">assert</span> <span class="n">mais_cinco</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="mi">10</span> <span class="c1"># AssertionError</span>
+</pre></div>
+
+
+<p><code>mais_cinco()</code> é o exemplo claro de uma função que gera efeito colateral. Uma função pura deve funcionar como uma caixa preta, todas as vezes em que o mesmo input for dado nela, ela terá que retornar o mesmo valor. Agora vamos usar o mesmo exemplo, só alterando a linha do return:</p>
+<div class="highlight"><pre><span></span><span class="n">valor</span> <span class="o">=</span> <span class="mi">5</span>
+
+<span class="k">def</span> <span class="nf">mais_cinco</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">5</span>
+
+<span class="k">assert</span> <span class="n">mais_cinco</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="mi">10</span> <span class="c1"># True</span>
+
+<span class="n">valor</span> <span class="o">=</span> <span class="mi">7</span>
+
+<span class="k">assert</span> <span class="n">mais_cinco</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="mi">10</span> <span class="c1"># True</span>
+</pre></div>
+
+
+<p>Pode parecer trivial, mas muitas vezes por comodidade deixamos o meio influenciar no comportamento de uma função. Por definição o Python só faz possível, e vamos falar disso em outro tópico, a leitura de variáveis externas. Ou seja, dentro do contexto da função as variáveis externas não podem ser modificadas, mas isso não impede que o contexto externo a modifique. Se você for uma pessoa inteligente como o Jaber deve saber que nunca é uma boa ideia usar valores externos. Mas, caso seja necessário, você pode sobrescrever o valor de uma variável no contexto global usando a palavra reservada <code>global</code>. O que deve ficar com uma cara assim:</p>
+<div class="highlight"><pre><span></span><span class="n">valor</span> <span class="o">=</span> <span class="mi">5</span>
+
+<span class="k">def</span> <span class="nf">teste</span><span class="p">():</span>
+ <span class="k">global</span> <span class="n">valor</span> <span class="c1"># aqui é feita a definição</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="mi">7</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">valor</span><span class="p">)</span> <span class="c1"># 7</span>
+</pre></div>
+
+
+<p>Só lembre-se de ser sempre coerente quando fizer isso, as consequências podem ser imprevisíveis. Nessa linha de funções puras e pequeninas, podemos caracterizar, embora isso não as defina, funções de ordem superior, que são funções que recebem uma função como argumento, ou as devolvem, e fazem a chamada das mesmas dentro do contexto da função que a recebeu como parâmetro. Isso resulta em uma composição de funções, o que agrega muito mais valor caso as funções não gerem efeitos colaterais.</p>
+<h2>1.3 Funções de ordem superior (HOFs)</h2>
+<p>Funções de ordem superior são funções que recebem funções como argumento(s) e/ou retornam funções como resposta. Existem muitas funções embutidas em python de ordem superior, como: <code>map, filter, zip</code> e praticamente todo o módulo functools <code>import functools</code>. Porém, nada impede de criarmos novas funções de ordem superior. Um ponto a ser lembrado é que map e filter não tem mais a devida importância em python com a entrada das comprehensions (embora eu as adore), o que nos faz escolher única e exclusivamente por gosto, apesar de comprehensions serem mais legíveis (vamos falar disso em outro contexto), existem muitos casos onde elas ainda fazem sentido. Mas sem me estender muito, vamos ao código:</p>
+<div class="highlight"><pre><span></span><span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">+</span><span class="mi">2</span> <span class="c1"># uma função simples, soma mais 2 a qualquer inteiro</span>
+
+<span class="k">def</span> <span class="nf">func_mais_2</span><span class="p">(</span><span class="n">funcao</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Executa a função passada por parâmetro e retorna esse valor somado com dois</span>
+
+<span class="sd"> Ou seja, é uma composição de funções:</span>
+
+<span class="sd"> Dado que func(valor) é processado por func_func:</span>
+<span class="sd"> func_mais_2(func(valor)) == f(g(x))</span>
+<span class="sd"> """</span>
+ <span class="k">return</span> <span class="n">funcao</span><span class="p">(</span><span class="n">valor</span><span class="p">)</span> <span class="o">+</span> <span class="mi">2</span>
+</pre></div>
+
+
+<p>Um ponto a tocar, e o que eu acho mais bonito, é que a função vai retornar diferentes respostas para o mesmo valor, variando a entrada da função. Nesse caso, dada a entrada de um inteiro ele será somado com 2 e depois com mais dois. Mas, vamos estender este exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">2</span> <span class="c1"># uma função simples, soma mais 2 a qualquer inteiro</span>
+
+<span class="k">def</span> <span class="nf">func_mais_2</span><span class="p">(</span><span class="n">funcao</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Função que usamos antes.</span>
+<span class="sd"> """</span>
+ <span class="k">return</span> <span class="n">funcao</span><span class="p">(</span><span class="n">valor</span><span class="p">)</span> <span class="o">+</span> <span class="mi">2</span>
+
+<span class="k">assert</span> <span class="n">func_mais_2</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">6</span> <span class="c1"># true</span>
+
+<span class="k">def</span> <span class="nf">func_quadrada</span><span class="p">(</span><span class="n">val</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Eleva o valor de entrada ao quadrado.</span>
+<span class="sd"> """</span>
+ <span class="k">return</span> <span class="n">val</span> <span class="o">*</span> <span class="n">val</span>
+
+<span class="k">assert</span> <span class="n">func_mais_2</span><span class="p">(</span><span class="n">func_quadrada</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">6</span> <span class="c1"># true</span>
+</pre></div>
+
+
+<h3>1.3.1 Um exemplo usando funções embutidas:</h3>
+<p>Muitas das funções embutidas em python são funções de ordem superior (HOFs) como a função map, que é uma das minhas preferidas. Uma função de map recebe uma função, que recebe um único argumento e devolve para nós uma nova lista com a função aplicada a cada elemento da lista:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="n">arg</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">arg</span> <span class="o">+</span> <span class="mi">2</span>
+
+<span class="n">lista</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">]</span>
+
+<span class="nb">list</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="n">lista</span><span class="p">))</span> <span class="o">==</span> <span class="p">[</span><span class="mi">4</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span> <span class="c1"># true</span>
+</pre></div>
+
+
+<p>Mas fique tranquilo, falaremos muito mais sobre isso.</p>
+<h2>1.4 <code>__call__</code></h2>
+<p>Por que falar de classes? Lembre-se, Python é uma linguagem construída em classes, e todos os objetos que podem ser chamados/invocados implementam o método <code>__call__</code>:</p>
+<p>Em uma função anônima:</p>
+<div class="highlight"><pre><span></span><span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span>
+
+<span class="s1">'__call__'</span> <span class="ow">in</span> <span class="nb">dir</span><span class="p">(</span><span class="n">func</span><span class="p">)</span> <span class="c1"># True</span>
+</pre></div>
+
+
+<p>Em funções tradicionais:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">x</span>
+
+<span class="s1">'__call__'</span> <span class="ow">in</span> <span class="nb">dir</span><span class="p">(</span><span class="n">func</span><span class="p">)</span> <span class="c1"># True</span>
+</pre></div>
+
+
+<p>Isso quer dizer que podemos gerar classes que se comportam como funções?</p>
+<p>SIIIIIM. Chupa Haskell</p>
+<p>Essa é uma parte interessante da estrutura de criação do Python a qual veremos mais em outro momento sobre introspecção de funções, mas vale a pena dizer que classes, funções nomeadas, funções anônimas e funções geradoras usam uma base comum para funcionarem, essa é uma das coisas mais bonitas em python e que em certo ponto fere a ortogonalidade da linguagem, pois coisas iguais tem funcionamentos diferentes, mas facilita o aprendizado da linguagem, mas não é nosso foco agora.</p>
+<h2>1.5 Funções geradoras</h2>
+<p>Embora faremos um tópico extremamente focado em funções geradoras, não custa nada dar uma palinha, não?</p>
+<p>Funções geradoras são funções que nos retornam um iterável. Mas ele é lazy (só é computado quando invocado). Para exemplo de uso, muitos conceitos precisam ser esclarecidos antes de entendermos profundamente o que acontece com elas, mas digo logo: são funções lindas <3</p>
+<p>Para que uma função seja geradora, em tese, só precisamos trocar o return por yield:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">gen</span><span class="p">(</span><span class="n">lista</span><span class="p">):</span>
+ <span class="k">for</span> <span class="n">elemento</span> <span class="ow">in</span> <span class="n">lista</span><span class="p">:</span>
+ <span class="k">yield</span> <span class="n">elemento</span>
+
+<span class="n">gerador</span> <span class="o">=</span> <span class="n">gen</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">])</span>
+
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># 1</span>
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># 2</span>
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># 3</span>
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># 4</span>
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># 5</span>
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># StopIteration</span>
+</pre></div>
+
+
+<p>Passando bem por cima, uma função geradora nos retorna um iterável que é preguiçoso. Ou seja, ele só vai efetuar a computação quando for chamado.</p>
+<h2>1.6 Funções anônimas (lambda)</h2>
+<p>Funções anônimas, ou funções lambda, são funções que podem ser declaradas em qualquer contexto. Tá... Todo tipo de função em python pode ser declarada em tempo de execução. Porém funções anônimas podem ser atribuídas a variáveis, podem ser definidas dentro de sequências e declaradas em um argumento de função. Vamos olhar sua sintaxe:</p>
+<div class="highlight"><pre><span></span><span class="k">lambda</span> <span class="n">argumento</span><span class="p">:</span> <span class="n">argumento</span>
+</pre></div>
+
+
+<p>A palavra reservada <code>lambda</code> define a função, assim como uma <code>def</code>. Porém em uma <code>def</code> quase que instintivamente sempre quebramos linha:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func</span><span class="p">():</span>
+ <span class="k">pass</span>
+</pre></div>
+
+
+<p>Uma das diferenças triviais em python é que as funções anônimas não tem nome. Tá, isso era meio óbvio, mas vamos averiguar:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func</span><span class="p">():</span>
+ <span class="k">pass</span>
+
+<span class="n">func</span><span class="o">.</span><span class="vm">__name__</span> <span class="c1"># func</span>
+
+<span class="n">lambda_func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">arg</span><span class="p">:</span> <span class="n">arg</span>
+
+<span class="n">lambda_func</span><span class="o">.</span><span class="vm">__name__</span> <span class="c1"># '<lambda>'</span>
+</pre></div>
+
+
+<p>O resultado <code>'<lambda>'</code> será o mesmo para qualquer função. Isso torna sua depuração praticamente impossível em python. Por isso os usuários de python (e nisso incluo todos os usuários, até aqueles que gostam de funcional) não encorajam o uso de funções lambda a todos os contextos da linguagem. Mas, em funções que aceitam outra funções isso é meio que uma tradição, caso a função (no caso a que executa o código a ser usado pelo lambda) não esteja definida e nem seja reaproveitada em outro contexto. Eu gosto de dizer que lambdas são muito funcionais em aplicações parciais de função. Porém, os lambdas não passam de açúcar sintático em Python, pois não há nada que uma função padrão (definida com <code>def</code>), não possa fazer de diferente. Até a introspecção retorna o mesmo resultado:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func</span><span class="p">():</span>
+ <span class="k">pass</span>
+
+<span class="nb">type</span><span class="p">(</span><span class="n">func</span><span class="p">)</span> <span class="c1"># function</span>
+
+<span class="n">lambda_func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">arg</span><span class="p">:</span> <span class="n">arg</span>
+
+<span class="nb">type</span><span class="p">(</span><span class="n">lambda_func</span><span class="p">)</span> <span class="c1"># function</span>
+</pre></div>
+
+
+<p>Uma coisa que vale ser lembrada é que funções anônimas em python só executam uma expressão. Ou seja, não podemos usar laços de repetição (<code>while</code>, <code>for</code>), tratamento de exceções (<code>try</code>, <code>except</code>, <code>finally</code>). Um simples <code>if</code> com uso de <code>elif</code> também não pode ser definido. Como sintaticamente só são aceitas expressões, o único uso de um <code>if</code> é o ternário:</p>
+<div class="highlight"><pre><span></span><span class="n">valor_1</span> <span class="k">if</span> <span class="n">condicao</span> <span class="k">else</span> <span class="n">valor_2</span>
+</pre></div>
+
+
+<p>O que dentro de um lambda teria essa aparência:</p>
+<div class="highlight"><pre><span></span><span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">argumento</span><span class="p">:</span> <span class="n">argumento</span> <span class="o">+</span> <span class="mi">2</span> <span class="k">if</span> <span class="n">argumento</span> <span class="o">></span> <span class="mi">0</span> <span class="k">else</span> <span class="n">argumento</span> <span class="o">-</span> <span class="mi">2</span>
+</pre></div>
+
+
+<p>Funções lambda também podem ter múltiplos argumentos, embora seu processamento só possa ocorrer em uma expressão:</p>
+<div class="highlight"><pre><span></span><span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span><span class="p">,</span> <span class="n">arg_3</span><span class="p">:</span> <span class="kc">True</span> <span class="k">if</span> <span class="nb">sum</span><span class="p">([</span><span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span><span class="p">,</span> <span class="n">arg_3</span><span class="p">])</span> <span class="o">></span> <span class="mi">7</span> <span class="k">else</span> <span class="nb">min</span><span class="p">([</span><span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span><span class="p">,</span> <span class="n">arg_3</span><span class="p">])</span>
+</pre></div>
+
+
+<p>Embora essa seja uma explanação inicial sobre as funções anônimas, grande parte dos tópicos fazem uso delas e vamos poder explorar melhor sua infinitude.</p>
+<p>Mas por hoje é só e no tópico seguinte vamos discutir, mesmo que superficialmente, iteradores e iteráveis e suas relações com a programação funcional.</p>
+
+ Eduardo Mendes
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Programação funcional com Python #0 - Saindo da zona de conforto
+
+ tag:pythonclub.com.br,2017-11-20:/progrmacao-funcional-com-python-0.html
+ 2017-11-20T21:43:00+00:00
+ <h1>0. Saindo da zona de conforto</h1>
+<p>Sinta-se um vencedor, se você chegou até aqui, isso significa que quer aprender mais sobre o mundo da programação.</p>
+<p>Aprender novos paradígmas podem te trazer muitas coisas positivas, assim como aprender linguagens diferentes, pois paradígmas e linguagens transpõem maneiras, estruturas e métodos de implementação completamente diferentes. Com isso você pode ter mais ferramentas para usar no dia a dia. Você pode aumentar sua capacidade de expressar ideias de diferentes maneiras. Eu penso que o maior limitador de um programador é a linguagem de programação em que ele tem domínio. Quando você aprende linguagens imperativas, como C, Python, Java e etc..., você se vê limitado ao escopo de criar e manipular variáveis. Não que isso seja uma coisa ruim, porém existem outras maneiras de resolver problemas e quando você tem conhecimento disso consegue avaliar melhor quando implementar cada tipo de coisa.</p>
+<p>Você pode me dizer que aprender diferentes tipos de estruturas e maneiras de computar é uma coisa negativa pois tudo é variável nesse contexto. Mas eu penso exatamente o contrário, quanto mais você aprender da sua língua nativa, no caso estamos falando em português, maior o campo de domínio que você tem sobre como se comunicar e expressar ideias. Assim como aprender outras línguas te darão mais fundamentos para expressar ideias em outros idiomas, que não são melhores que os seu, mas diferentes e compõem diferentes estruturas, e isso pode ser libertador. Não quero me prolongar nesse assunto, mas dizer que isso pode acrescentar muito na suas habilidades cognitivas, até mesmo para usar ferramentas que você já usa no seu dia a dia.</p>
+<p>Vamos começar fazendo uma tentativa de entender os paradígmas de programação, sem muito falatório e complicações. Um exemplo muito legal é do David Mertz em "Functional Programming in Python":</p>
+<ul>
+<li>
+<p>Usa-se programação funcional quando se programa em Lisp, Haskell, Scala, Erlang, F# etc..</p>
+</li>
+<li>
+<p>Do mesmo modo que se usa programação imperativa quando se programada C/C++, Pascal, Java, Python etc...</p>
+</li>
+<li>
+<p>Também quando se programa Prolog estamos programando usando o paradígma lógico.</p>
+</li>
+</ul>
+<p>Apesar de não ser uma definição muito elegante, talvez seja a melhor a ser dada em muitas ocasiões. Vamos tentar ser um pouco mais objetivos em relação ao estilo de computação, embora essa discussão não tenha fim:</p>
+<ul>
+<li>
+<p>O foco de usar programação imperativa está no ato de mudar variáveis. A computação se dá pela modificação dos estados das variáveis iniciais. Sendo assim, vamos pensar que tudo é definido no início e vai se modificando até que o resultado esperado seja obtido.</p>
+</li>
+<li>
+<p>Na programação funcional, se tem a noção de que o estado deve ser substituído, no caso da avaliação, para criação de um novo 'objeto' que no caso são funções.</p>
+</li>
+</ul>
+<h2>0.1 Mas de onde vem a programação funcional?</h2>
+<p>O florescer da programação funcional nasce no Lisp (acrônomo para List Processing) para tentar resolver alguns problemas de inteligência artificial que eram provenientes da linguística, que tinha foco em processamento de linguagem natural que por sua vez eram focados em processamento de listas em geral. Isso justifica uma grande parte do conteúdo que vamos ver aqui e seus tipos de dados variam somente entre listas e átomos. E assim foi mantido o foco de processamento de listas em todas as linguagens funcionais e suas funções e abstrações para resolver problemas relativos a listas e estruturas iteráveis. Uma curiosidade é que para quem não sabe porque em lisp existem tantos parênteses é que ele é baseado em s-expression, uma coisa que temos um "equivalente" evoluído em python, que parte dos teoremas de gramáticas livres de contexto:</p>
+<div class="highlight"><pre><span></span><span class="p">(</span><span class="nb">+ </span><span class="mi">4</span> <span class="mi">5</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Sim, isso é uma soma em lisp. Diferente das linguagens imperativas como costumamos ver:</p>
+<div class="highlight"><pre><span></span><span class="mi">4</span> <span class="o">+</span> <span class="mi">5</span>
+</pre></div>
+
+
+<p>Uma assertiva pode ser feita dessa maneira:</p>
+<ul>
+<li>Funcional (ou declarativa)</li>
+</ul>
+<div class="highlight"><pre><span></span><span class="p">(</span><span class="nb">= </span><span class="mi">4</span> <span class="p">(</span><span class="nb">+ </span><span class="mi">2</span> <span class="mi">2</span><span class="p">))</span>
+</pre></div>
+
+
+<ul>
+<li>Imperativa</li>
+</ul>
+<div class="highlight"><pre><span></span><span class="p">(</span><span class="mi">2</span> <span class="o">+</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">4</span>
+</pre></div>
+
+
+<p>Chega de enrolação e vamos correr com essa introdução, não viemos aqui para aprender Lisp ou C. Mas acho que parte desse contexto pode nos ajudar e muito quando formos nos aprofundar em alguns tópicos. Pretendo sempre que iniciar uma nova ferramenta da programação funcional ao menos explicar em que contexto ela foi desenvolvida e para resolver cada tipo de problema.</p>
+<h2>0.2 Técnicas usadas por linguagens funcionais</h2>
+<p>Vamos tentar mapear o que as linguagens funcionais fazem de diferente das linguagens imperativas, mas não vamos nos aprofundar nesse tópicos agora, pois são coisas às vezes complexas sem o entendimento prévio de outros contextos, mas vamos tentar só explanar pra que você se sinta empolgado por estar aqui:</p>
+<ul>
+<li>
+<p>Funções como objetos de primeira classe:</p>
+<ul>
+<li>São funções que podem estar em qualquer lugar (em estruturas, declaradas em tempo de execução).</li>
+</ul>
+</li>
+<li>
+<p>Funções de ordem superior:</p>
+<ul>
+<li>São funções que podem receber funções como argumentos e retornar funções.</li>
+</ul>
+</li>
+<li>
+<p>Funções puras:</p>
+<ul>
+<li>São funções que não sofrem interferências de meios externos (variáveis de fora). Evita efeitos colaterais.</li>
+</ul>
+</li>
+<li>
+<p>Recursão, como oposição aos loops:</p>
+<ul>
+<li>Frequentemente a recursão na matemática é uma coisa mais intuitiva e é só chamar tudo outra vez, no lugar de ficar voltando ao ponto inicial da iteração.</li>
+</ul>
+</li>
+<li>
+<p>Foco em processamento de iteráveis:</p>
+<ul>
+<li>Como dito anteriormente, pensar em como as sequências podem nos ajudar a resolver problemas.</li>
+</ul>
+</li>
+<li>
+<p>O que deve ser computado, não como computar:</p>
+<ul>
+<li>Não ser tão expressivo e aceitar que as intruções não tem necessidade de estar explicitas todas as vezes, isso ajuda em legibilidade.</li>
+</ul>
+</li>
+<li>
+<p>Lazy evaluation:</p>
+<ul>
+<li>Criar sequências infinitas sem estourar nossa memória.</li>
+</ul>
+</li>
+</ul>
+<h2>0.3 Python é uma linguagem funcional?</h2>
+<h4>Não. Mas é uma linguagem que implementa muitos paradígmas e porque não usar todos de uma vez?</h4>
+<p>O objetivo desse 'conjunto de vídeos' é escrever código que gere menos efeito colateral e código com menos estados. Só que isso tudo feito na medida do possível, pois Python não é uma linguagem funcional. Porém, podemos contar o máximo possível com as features presentes do paradígma em python.</p>
+<p>Exemplos de funcional (básicos) em python:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Gerar uma lista da string # Imperativo</span>
+<span class="n">string</span> <span class="o">=</span> <span class="s1">'Python'</span>
+<span class="n">lista</span> <span class="o">=</span> <span class="p">[]</span> <span class="c1"># estado inicial</span>
+
+<span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="n">string</span><span class="p">:</span>
+ <span class="n">lista</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">l</span><span class="p">)</span> <span class="c1"># cada iteração gera um novo estado</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">lista</span><span class="p">)</span> <span class="c1"># ['P', 'y', 't', 'h', 'o', 'n']</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="c1"># Gerar uma lista da string # Funcional</span>
+<span class="n">string</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span>
+
+<span class="n">lista</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="n">string</span><span class="p">(</span><span class="s1">'Python'</span><span class="p">)))</span> <span class="c1"># atribuição a um novo objeto</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">lista</span><span class="p">)</span> <span class="c1"># ['P', 'y', 't', 'h', 'o', 'n']</span>
+</pre></div>
+
+
+<p>Como você pode ver, depois de uma explanação básica das técnicas, a segunda implementação não sofre interferência do meio externo (Funções puras), evita loops e sua saída sem o construtor de list é lazy. Mas não se assuste, vamos abordar tudo isso com calma.</p>
+<h2>0.4 A quem esse 'curso' é destinado?</h2>
+<p>Primeiramente gostaria de dizer que roubei essa ideia dos infinitos livros da O’Reilly, que sempre exibem esse tópico. Mas vamos ao assunto. Este curso é para você que sabe o básico de Python, e quando digo básico quero dizer que consegue fazer qualquer coisa com um pouco de pesquisa na internet. O básico de programação se reduz a isso. Vamos falar sobre coisas simples e coisas mais complexas, mas pretendo manter o bom senso para que todos possam absorver o máximo de conteúdo possível.</p>
+<p>Então, caso você venha do Python (OO ou procedural) você vai encontrar aqui uma introdução a programação funcional descontraída e sem uma tonelada de material difícil de entender. Caso você venha de linguagens funcionais como Haskell e Lisp, você pode se sentir um pouco estranho com tantas declarações, mas aprenderá a se expressar em Python. Caso você venha de linguagens funcionais modernas como Clojure e Scala, as coisas são bem parecidas por aqui.</p>
+<p>Então tente tirar o máximo de proveito. Vamos nos divertir.</p>
+<h2>0.5 Apresentando o Jaber</h2>
+<p>Jaber é nosso aluno de mentira, mas vamos pensar que ele é um aluno que senta na primeira fileira e pergunta de tudo, sempre que acha necessário. Roubei essa ideia do livro de expressões regulares do Aurélio. Ele tem um personagem, Piazinho, e acho que toda interação com ele é sempre graciosa e tira dúvidas quando tudo parece impossível.</p>
+<h2>0.6 Sobre as referências</h2>
+<p>Não gosto muito de citar referências pois procurei não copiar texto dos livros, mas muita coisa contida neles serve de base para o entendimento de certos tópicos. Outro motivo é o nível de complexidade dos exemplos ou explicações que tentei reduzir ao máximo enquanto escrevia esses roteiros. Para um exemplo, você pode olhar o livro do Steven Lott, cheio de fórmulas e abstrações matemáticas que em certo ponto acabam comprometendo o entendimento de quem não tem uma sólida base em computação teórica ou matemática.</p>
+<p>Como um todo, as referências serviram como guia, foi o que lí quando dúvidas para explicações surgiram. Não tiro nenhum crédito delas e as exponho para que todos saibam que existem muitos livros bons e que boa parte do que é passado aqui, foi aprendido neles.</p>
+<h2>0.7 Mais sobre o histórico das linguagens funcionais</h2>
+<p>Se você pretende realmente se aprofundar no assunto enquanto acompanha esse curso, fazer uma imersão ou coisa parecida. Tudo começa com o cálculo lambda mentalizado pelo incrível <a href="https://en.wikipedia.org/wiki/Alonzo_Church">Alonzo Church</a>. Caso você não o conheça, ele foi um matemático fantástico e teve uma carreira acadêmica brilhante. Foi o orientador de pessoas incríveis como Alan Turing, Raymond Smullyan etc...</p>
+<p>Outro grande homem e que vale a pena mencionar e ser buscado é o <a href="https://pt.wikipedia.org/wiki/Haskell_Curry">Haskell Curry</a>, um lógico que trouxe excelentes contribuições para o que chamamos hoje de programação funcional.</p>
+<p>A primeira linguagem funcional 'oficial' (não gosto muito de dizer isso) é o Lisp (List Processing) criada pelo fenomenal <a href="https://pt.wikipedia.org/wiki/John_McCarthy">John McCarthy</a> que também vale a pena ser pesquisado e estudado.</p>
+<p>Veremos o básico sobre os tipos de função no próximo tópico.</p>
+<p>OBS: <a href="https://github.com/z4r4tu5tr4/python-funcional/blob/master/referencias.md">Referências</a> usadas durante todos os tópicos.</p>
+
+ Eduardo Mendes
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Peewee - Um ORM Python minimalista
+
+ tag:pythonclub.com.br,2017-07-20:/peewee-um-orm-python-minimalista.html
+ 2017-07-21T02:45:24+00:00
+ <p><a href="http://peewee.readthedocs.io/en/latest/index.html">Peewee</a> é um ORM destinado a criar e gerenciar tabelas de banco de dados relacionais através de objetos Python. Segundo a <a href="https://pt.wikipedia.org/wiki/Mapeamento_objeto-relacional">wikipedia</a>, um ORM é:</p>
+<blockquote>
+<p>Mapeamento objeto-relacional (ou ORM, do inglês: Object-relational mapping) é uma técnica de desenvolvimento > utilizada para reduzir a impedância da programação orientada aos objetos utilizando bancos de dados relacionais. As tabelas do banco de dados são representadas através de classes e os registros de cada tabela são representados como instâncias das classes correspondentes.</p>
+</blockquote>
+<p>O que um ORM faz é, basicamente, transformar classes Python em tabelas no banco de dados, além de permitir construir <em>querys</em> usando diretamente objetos Python ao invés de SQL.</p>
+<p>O Peewee é destinado a projetos de pequeno/médio porte e se destaca pela simplicidade quando comparado a outros ORM mais conhecidos, como o SQLAlchemy. Uma analogia utilizada pelo autor da API e que acho muito interessante é que Peewee está para o SQLAlchemy assim como SQLite está para o PostgreSQL.</p>
+<p>Em relação aos recursos por ele oferecidos, podemos citar que ele possui suporte nativo a SQLite, PostgreSQL e MySQL, embora seja necessário a instalação de <em>drivers</em> para utilizá-lo com PostgreSQL e MySQL e suporta tanto Python 2.6+ quanto Python 3.4+.</p>
+<p>Neste tutorial, utilizaremos o SQLite, por sua simplicidade de uso e pelo Python possuir suporte nativo ao mesmo (usaremos o Python 3.5).</p>
+<h2>Instalação</h2>
+<p>O Peewee pode ser facilmente instalado com o gerenciador de pacotes <em>pip</em> (recomendo a instalação em um virtualenv):</p>
+<div class="highlight"><pre><span></span><span class="err">pip install peewee</span>
+</pre></div>
+
+
+<h2>Criando o banco de dados</h2>
+<p>Para criar o banco de dados é bem simples. Inicialmente passamos o nome do nosso banco de dados (a extensão <code>*.db</code> indica um arquivo do SQLite).</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">peewee</span>
+
+<span class="c1"># Aqui criamos o banco de dados</span>
+<span class="n">db</span> <span class="o">=</span> <span class="n">peewee</span><span class="o">.</span><span class="n">SqliteDatabase</span><span class="p">(</span><span class="s1">'codigo_avulso.db'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Diferente de outros bancos de dados que funcionam através um servidor, o SQLite cria um arquivo de extensão <code>*.db</code>, onde todos os nossos dados são armazenados.</p>
+<blockquote>
+<p>Caso deseje ver as tabelas existentes no arquivo <code>codigo_avulso.db</code>, instale o aplicativo <code>SQLiteBrowser</code>. Com ele fica fácil monitorar as tabelas criadas e acompanhar o tutorial.</p>
+</blockquote>
+<div class="highlight"><pre><span></span> sudo apt-get install sqlitebrowser
+</pre></div>
+
+
+<p>A título de exemplo, vamos criar um banco destinado a armazenar nomes de livros e de seus respectivos autores. Iremos chamá-lo de <code>models.py</code>.</p>
+<p>Inicialmente, vamos criar a classe base para todos os nossos <code>models</code>. Esta é uma abordagem recomendada pela documentação e é considerada uma boa prática. Também adicionaremos um log para acompanharmos as mudanças que são feitas no banco:</p>
+<div class="highlight"><pre><span></span><span class="c1"># models.py</span>
+
+<span class="kn">import</span> <span class="nn">peewee</span>
+
+<span class="c1"># Criamos o banco de dados</span>
+<span class="n">db</span> <span class="o">=</span> <span class="n">peewee</span><span class="o">.</span><span class="n">SqliteDatabase</span><span class="p">(</span><span class="s1">'codigo_avulso.db'</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">BaseModel</span><span class="p">(</span><span class="n">peewee</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
+ <span class="sd">"""Classe model base"""</span>
+
+ <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
+ <span class="c1"># Indica em qual banco de dados a tabela</span>
+ <span class="c1"># 'author' sera criada (obrigatorio). Neste caso,</span>
+ <span class="c1"># utilizamos o banco 'codigo_avulso.db' criado anteriormente</span>
+ <span class="n">database</span> <span class="o">=</span> <span class="n">db</span>
+</pre></div>
+
+
+<p>A class <code>BaseModel</code> é responsável por criar a conexão com nosso banco de dados.</p>
+<p>Agora, vamos criar a model que representa os autores:</p>
+<div class="highlight"><pre><span></span><span class="c1"># models.py</span>
+
+<span class="k">class</span> <span class="nc">Author</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
+
+ <span class="sd">"""</span>
+<span class="sd"> Classe que representa a tabela Author</span>
+<span class="sd"> """</span>
+ <span class="c1"># A tabela possui apenas o campo 'name', que receberá o nome do autor sera unico</span>
+ <span class="n">name</span> <span class="o">=</span> <span class="n">peewee</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Se observamos a model <code>Author</code>, veremos que não foi especificado nenhuma coluna como <em>primary key</em> (chave primaria), sendo assim o Peewee irá criar um campo chamado <code>id</code> do tipo inteiro com auto incremento para funcionar como chave primária.
+Em seguida, no mesmo arquivo <code>models.py</code> criamos a classe que representa os livros. Ela possui uma relação de "muitos para um" com a tabela de autores, ou seja, cada livro possui apenas um autor, mas um autor pode possuir vários livros.</p>
+<div class="highlight"><pre><span></span><span class="c1"># models.py</span>
+
+<span class="k">class</span> <span class="nc">Book</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Classe que representa a tabela Book</span>
+<span class="sd"> """</span>
+
+ <span class="c1"># A tabela possui apenas o campo 'title', que receberá o nome do livro</span>
+ <span class="n">title</span> <span class="o">=</span> <span class="n">peewee</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
+
+ <span class="c1"># Chave estrangeira para a tabela Author</span>
+ <span class="n">author</span> <span class="o">=</span> <span class="n">peewee</span><span class="o">.</span><span class="n">ForeignKeyField</span><span class="p">(</span><span class="n">Author</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Agora, adicionamos o código que cria as tabelas <code>Author</code> e <code>Book</code>.</p>
+<div class="highlight"><pre><span></span><span class="c1"># models.py</span>
+
+<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">Author</span><span class="o">.</span><span class="n">create_table</span><span class="p">()</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s2">"Tabela 'Author' criada com sucesso!"</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">peewee</span><span class="o">.</span><span class="n">OperationalError</span><span class="p">:</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s2">"Tabela 'Author' ja existe!"</span><span class="p">)</span>
+
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">Book</span><span class="o">.</span><span class="n">create_table</span><span class="p">()</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s2">"Tabela 'Book' criada com sucesso!"</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">peewee</span><span class="o">.</span><span class="n">OperationalError</span><span class="p">:</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s2">"Tabela 'Book' ja existe!"</span><span class="p">)</span>
+</pre></div>
+
+
+<p>excerpt
+Agora executamos o <code>models.py</code>:</p>
+<div class="highlight"><pre><span></span><span class="err">python models.py</span>
+</pre></div>
+
+
+<p>A estrutura do diretório ficou assim:</p>
+<div class="highlight"><pre><span></span>.
+├── codigo_avulso.db
+├── models.py
+</pre></div>
+
+
+<p>Após executarmos o código, será criado um arquivo de nome <code>codigo_avulso.db</code> no mesmo diretório do nosso arquivo <code>models.py</code>, contendo as tabelas <code>Author</code> e <code>Book</code>.</p>
+<h2>Realizando o CRUD</h2>
+<p>Agora vamos seguir com as 4 principais operações que podemos realizar em um banco de dados, também conhecida como CRUD.</p>
+<p>A sigla <code>CRUD</code> é comumente utilizada para designar as quatro operações básicas que pode-se executar em um banco de dados, sendo elas: </p>
+<div class="highlight"><pre><span></span><span class="err">- Create (criar um novo registro no banco)</span>
+<span class="err">- Read (ler/consultar um registro)</span>
+<span class="err">- Update (atualizar um registro)</span>
+<span class="err">- Delete (excluir um registro do banco)</span>
+</pre></div>
+
+
+<p>Iremos abordar cada uma dessas operações.</p>
+<h3>Create: Inserindo dados no banco</h3>
+<p>Agora, vamos popular nosso banco com alguns autores e seus respectivos livros. Para isso criamos um arquivo <code>create.py</code>. A estrutura do diretório ficou assim:</p>
+<div class="highlight"><pre><span></span>.
+├── codigo_avulso.db
+├── models.py
+├── create.py
+</pre></div>
+
+
+<p>A criação dos registros no banco pode ser feito através do método <code>create</code>, quando desejamos inserir um registro apenas; ou pelo método <code>insert_many</code>, quando desejamos inserir vários registros de uma vez em uma mesma tabela.</p>
+<div class="highlight"><pre><span></span><span class="c1"># create.py</span>
+
+<span class="kn">from</span> <span class="nn">models</span> <span class="kn">import</span> <span class="n">Author</span><span class="p">,</span> <span class="n">Book</span>
+
+<span class="c1"># Inserimos um autor de nome "H. G. Wells" na tabela 'Author'</span>
+<span class="n">author_1</span> <span class="o">=</span> <span class="n">Author</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'H. G. Wells'</span><span class="p">)</span>
+
+<span class="c1"># Inserimos um autor de nome "Julio Verne" na tabela 'Author'</span>
+<span class="n">author_2</span> <span class="o">=</span> <span class="n">Author</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'Julio Verne'</span><span class="p">)</span>
+
+<span class="n">book_1</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'title'</span><span class="p">:</span> <span class="s1">'A Máquina do Tempo'</span><span class="p">,</span>
+ <span class="s1">'author_id'</span><span class="p">:</span> <span class="n">author_1</span><span class="p">,</span>
+<span class="p">}</span>
+
+<span class="n">book_2</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'title'</span><span class="p">:</span> <span class="s1">'Guerra dos Mundos'</span><span class="p">,</span>
+ <span class="s1">'author_id'</span><span class="p">:</span> <span class="n">author_1</span><span class="p">,</span>
+<span class="p">}</span>
+
+<span class="n">book_3</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'title'</span><span class="p">:</span> <span class="s1">'Volta ao Mundo em 80 Dias'</span><span class="p">,</span>
+ <span class="s1">'author_id'</span><span class="p">:</span> <span class="n">author_2</span><span class="p">,</span>
+<span class="p">}</span>
+
+<span class="n">book_4</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'title'</span><span class="p">:</span> <span class="s1">'Vinte Mil Leguas Submarinas'</span><span class="p">,</span>
+ <span class="s1">'author_id'</span><span class="p">:</span> <span class="n">author_1</span><span class="p">,</span>
+<span class="p">}</span>
+
+<span class="n">books</span> <span class="o">=</span> <span class="p">[</span><span class="n">book_1</span><span class="p">,</span> <span class="n">book_2</span><span class="p">,</span> <span class="n">book_3</span><span class="p">,</span> <span class="n">book_4</span><span class="p">]</span>
+
+<span class="c1"># Inserimos os quatro livros na tabela 'Book'</span>
+<span class="n">Book</span><span class="o">.</span><span class="n">insert_many</span><span class="p">(</span><span class="n">books</span><span class="p">)</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span>
+</pre></div>
+
+
+<h3>Read: Consultando dados no banco</h3>
+<p>O Peewee possui comandos destinados a realizar consultas no banco. De maneira semelhante ao conhecido <code>SELECT</code>. Podemos fazer essa consulta de duas maneiras. Se desejamos o primeiro registro que corresponda a nossa pesquisa, podemos utilizar o método <code>get()</code>.</p>
+<div class="highlight"><pre><span></span><span class="c1"># read.py</span>
+
+<span class="kn">from</span> <span class="nn">models</span> <span class="kn">import</span> <span class="n">Author</span><span class="p">,</span> <span class="n">Book</span>
+
+<span class="n">book</span> <span class="o">=</span> <span class="n">Book</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">Book</span><span class="o">.</span><span class="n">title</span> <span class="o">==</span> <span class="s2">"Volta ao Mundo em 80 Dias"</span><span class="p">)</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">book</span><span class="o">.</span><span class="n">title</span><span class="p">)</span>
+
+<span class="c1"># Resultado</span>
+<span class="c1"># * Volta ao Munto em 80 Dias</span>
+</pre></div>
+
+
+<p>Porém, se desejamos mais de um registro, utilizamos o método <code>select</code>. Por exemplo, para consultar todos os livros escritos pelo autor "H. G. Wells".</p>
+<div class="highlight"><pre><span></span><span class="c1"># read.py</span>
+
+<span class="n">books</span> <span class="o">=</span> <span class="n">Book</span><span class="o">.</span><span class="n">select</span><span class="p">()</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">Author</span><span class="p">)</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="n">Author</span><span class="o">.</span><span class="n">name</span><span class="o">==</span><span class="s1">'H. G. Wells'</span><span class="p">)</span>
+
+<span class="c1"># Exibe a quantidade de registros que corresponde a nossa pesquisa</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">books</span><span class="o">.</span><span class="n">count</span><span class="p">())</span>
+
+<span class="k">for</span> <span class="n">book</span> <span class="ow">in</span> <span class="n">books</span><span class="p">:</span>
+ <span class="nb">print</span><span class="p">(</span><span class="n">book</span><span class="o">.</span><span class="n">title</span><span class="p">)</span>
+
+<span class="c1"># Resultado:</span>
+<span class="c1"># * A Máquina do Tempo</span>
+<span class="c1"># * Guerra dos Mundos</span>
+<span class="c1"># * Vinte Mil Leguas Submarinas</span>
+</pre></div>
+
+
+<p>Também podemos utilizar outras comandos do SQL como <code>limit</code> e <code>group</code> (para mais detalhes, ver a documentação <a href="http://peewee.readthedocs.io/en/latest/index.html">aqui</a>).</p>
+<p>A estrutura do diretório ficou assim:</p>
+<div class="highlight"><pre><span></span>.
+├── codigo_avulso.db
+├── models.py
+├── create.py
+├── read.py
+</pre></div>
+
+
+<h3>Update: Alterando dados no banco</h3>
+<p>Alterar dados também é bem simples. No exemplo anterior, se observarmos o resultado da consulta dos livros do autor "H. G. Wells", iremos nos deparar com o livro de título "Vinte Mil Léguas Submarinas". Se você, caro leitor, gosta de contos de ficção-científica, sabe que esta obra foi escrito por "Julio Verne", coincidentemente um dos autores que também estão cadastrados em nosso banco. Sendo assim, vamos corrigir o autor do respectivo livro.</p>
+<p>Primeiro vamos buscar o registro do autor e do livro:</p>
+<div class="highlight"><pre><span></span><span class="c1"># update.py</span>
+
+<span class="kn">from</span> <span class="nn">models</span> <span class="kn">import</span> <span class="n">Author</span><span class="p">,</span> <span class="n">Book</span>
+
+<span class="n">new_author</span> <span class="o">=</span> <span class="n">Author</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">Author</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s1">'Julio Verne'</span><span class="p">)</span>
+<span class="n">book</span> <span class="o">=</span> <span class="n">Book</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">Book</span><span class="o">.</span><span class="n">title</span><span class="o">==</span><span class="s2">"Vinte Mil Leguas Submarinas"</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Agora vamos alterar o autor e gravar essa alteração no banco.</p>
+<div class="highlight"><pre><span></span><span class="c1"># update.py</span>
+
+<span class="c1"># Alteramos o autor do livro</span>
+<span class="n">book</span><span class="o">.</span><span class="n">author</span> <span class="o">=</span> <span class="n">new_author</span>
+
+<span class="c1"># Salvamos a alteração no banco</span>
+<span class="n">book</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+</pre></div>
+
+
+<p>A estrutura do diretório ficou assim:</p>
+<div class="highlight"><pre><span></span>.
+├── codigo_avulso.db
+├── models.py
+├── create.py
+├── read.py
+├── update.py
+</pre></div>
+
+
+<h3>Delete: Deletando dados do banco</h3>
+<p>Assim como as operações anteriores, também podemos deletar registros do banco de maneira bem prática. Como exemplo, vamos deletar o livro "Guerra dos Mundos" do nosso banco de dados.</p>
+<div class="highlight"><pre><span></span><span class="c1"># delete.py</span>
+
+<span class="kn">from</span> <span class="nn">models</span> <span class="kn">import</span> <span class="n">Author</span><span class="p">,</span> <span class="n">Book</span>
+
+<span class="c1"># Buscamos o livro que desejamos excluir do banco</span>
+<span class="n">book</span> <span class="o">=</span> <span class="n">Book</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">Book</span><span class="o">.</span><span class="n">title</span><span class="o">==</span><span class="s2">"Guerra dos Mundos"</span><span class="p">)</span>
+
+<span class="c1"># Excluimos o livro do banco</span>
+<span class="n">book</span><span class="o">.</span><span class="n">delete_instance</span><span class="p">()</span>
+</pre></div>
+
+
+<p>Simples não?</p>
+<p>A estrutura do diretório ficou assim:</p>
+<div class="highlight"><pre><span></span>.
+├── codigo_avulso.db
+├── models.py
+├── create.py
+├── read.py
+├── update.py
+├── delete.py
+</pre></div>
+
+
+<h2>Conclusão</h2>
+<p>É isso pessoal. Este tutorial foi uma introdução bem enxuta sobre o Peewee. Ainda existem muitos tópicos que não abordei aqui, como a criação de <em>primary_key</em>, de campos <em>many2many</em> entre outros recursos, pois foge do escopo deste tutorial. Se você gostou do ORM, aconselho a dar uma olhada também na sua documentação, para conseguir extrair todo o potencial da ferramenta. A utilização de um ORM evita que o desenvolvedor perca tempo escrevendo <em>query</em> SQL e foque totalmente no desenvolvimento de código.</p>
+<h2>Referências</h2>
+<ul>
+<li><a href="http://peewee.readthedocs.io/en/latest/index.html">Documentação do Peewee (em inglês)</a></li>
+<li><a href="https://www.blog.pythonlibrary.org/2014/07/17/an-intro-to-peewee-another-python-orm/">An Intro to peewee – Another Python ORM</a></li>
+<li><a href="http://jonathansoma.com/tutorials/webapps/intro-to-peewee/">Introduction to peewee</a></li>
+<li><a href="https://www.novatec.com.br/livros/introducao-sql/">Introdução à Linguagem SQL</a></li>
+</ul>
+
+ Michell Stuttgart
+ https://pythonclub.com.br/
+
+
+ PythonClub
+
+ https://pythonclub.com.br/
+ 2025-04-17T02:14:55+00:00
+
+
+
+
+ Curso de introdução a criptomoedas - Aula 01
+
+ tag:blogger.com,1999:blog-840127493134277990.post-6812804658380854111
+ 2017-06-17T13:50:44+00:00
+ For the Portuguese speaking readers of this blog, I am starting an Introductory course on Cryptocurrencies and applications on the blockchain which is an online version of a standard classroom course I am starting now at FGV.
+
+This is the first lecture which is basically an intro to the topic and the structure of the course.
+
+The online version should have the main content of the lectures on
+
+ Anonymous
+ noreply@blogger.com
+ http://pyinsci.blogspot.com/
+
+
+ Python in Science
+ Scientific adventures with Python.
+
+ tag:blogger.com,1999:blog-840127493134277990
+ 2025-04-17T02:14:15+00:00
+
+
+
+
diff --git a/code/README.pydotorg b/code/README.pydotorg
deleted file mode 100644
index f4dca19..0000000
--- a/code/README.pydotorg
+++ /dev/null
@@ -1,5 +0,0 @@
-
-This holds a copy of the PlanetPlanet code (www.planetplanet.org).
-This version is a copy of the nightly tarball, taken on 2005-05-15.
-
---amk
diff --git a/code/TODO b/code/TODO
deleted file mode 100644
index 5b5f668..0000000
--- a/code/TODO
+++ /dev/null
@@ -1,85 +0,0 @@
-Feature Goals for Planet 1.0
-============================
-
- * Store and use feed history
-
- We have a huge problem at the moment, in that we only display the current
- contents of the feeds we download. The old Netscape standard was 15 items
- in a feed, so many people still use that. Unfortunately, on sites like
- Slashdot, those 15 items move *very* quickly. So as they fall off the feed,
- they fall off your Planet aggregate. That is bad, bad, bad, and we need to
- solve it. Any ideas? There are potential bugs all over this. :-)
-
- * Add multiple keyword support
-
- I hadn't announced it yet, but I have added a simple 'keyword feeds'
- feature, which allows you to subscribe to feeds and only display items that
- mention your keyword in the title, summary or description. This is totally
- arse-kickingly rad, and demonstrated on my personal Planet.
-
- However, currently it only allows you to define one keyword phrase for each
- feed, which kinda sucks. If there's some quick-and-dirty boolean phrase
- parser, we could use it really well here, for instance:
-
- keyword = (gnome OR kde) NOT sucks
-
- Maybe that's overkill - we could just support multiple ORed keyword fields.
-
- * Fix stupid UTF-8 error with current keyword support
-
- For some reason, keyword support tweaks string encoding errors, such as:
- "WARNING:root:Item wasn't in UTF-8 or ISO-8859-1, replaced all non-ASCII
- characters." This is bad.
-
- * Allow templates to use extra feed elements
-
- See Frederic's thread about Freshmeat the other day. I don't want to
- special case everything, and I don't want to stomp on item variables we
- already have, but it would be nice to bring special things from feeds up
- into the template data structure. Should we only do this for namespaced
- elements (extensions)?
-
- * Fix intermittent timezone detection / arithmetic bugs
-
- From Nick Moffitt: "Right now there's something screwy in the way planet
- deals with time zones, so that all you Australians end up claiming the top
- of my planet while merkins I want to read end up shoved down a ways."
-
- * Allow display normalisation to specified timezone
-
- Some Planet admins would like their feed to be displayed in the local
- timezone, instead of UTC.
-
- * Add "sort by entry received date" option
-
- From Nick Moffitt: "Is there a way to get planet to not do entry-date
- placement at all? Like, always put new entries up in the order they were
- first seen by planet? I use mine to implement the "show me stuff I haven't
- already read yet" feature that pretty much all Web sites lack.
-
- Answer from Scott J. Remnant: "Yeah, this would be actually pretty easy ...
- it already has this code for entries missing times entirely, I'll add a
- config option to just outright ignore feed times and sort by seen order.
- I'll make it a per-feed option, maybe "sort_order = seen/time" and if you
- want it global just stick it in [DEFAULT]."
-
- * Support OPML and foaf subscriptions
-
- This might be a bit invasive, but I want to be able to subscribe to OPML
- and FOAF files, and see each feed as if it were subscribed individually.
- Perhaps we can do this with a two-pass configuration scheme, first to pull
- the static configs, second to go fetch and generate the dynamic configs.
- The more I think about it, the less invasive it sounds. Hmm.
-
- * Provide a 'disabled' configuration option
-
- Make it easy to mark a feed as disabled, but still show it on the subs
- list. Perhaps disabled is the wrong word. Some people want to use Planet to
- do their blogroll management, but not actually show some feeds in the
- aggregation. If someone can think of a better word, please tell me.
- Perhaps 'hide'.
-
-Other Feature Goals
-===================
-
- * Port to feedparser 3.0
diff --git a/code/planet-cache.py b/code/planet-cache.py
deleted file mode 100755
index 9334583..0000000
--- a/code/planet-cache.py
+++ /dev/null
@@ -1,194 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: UTF-8 -*-
-"""Planet cache tool.
-
-"""
-
-__authors__ = [ "Scott James Remnant ",
- "Jeff Waugh " ]
-__license__ = "Python"
-
-
-import os
-import sys
-import time
-import dbhash
-import ConfigParser
-
-import planet
-
-
-def usage():
- print "Usage: planet-cache [options] CACHEFILE [ITEMID]..."
- print
- print "Examine and modify information in the Planet cache."
- print
- print "Channel Commands:"
- print " -C, --channel Display known information on the channel"
- print " -L, --list List items in the channel"
- print " -K, --keys List all keys found in channel items"
- print
- print "Item Commands (need ITEMID):"
- print " -I, --item Display known information about the item(s)"
- print " -H, --hide Mark the item(s) as hidden"
- print " -U, --unhide Mark the item(s) as not hidden"
- print
- print "Other Options:"
- print " -h, --help Display this help message and exit"
- sys.exit(0)
-
-def usage_error(msg, *args):
- print >>sys.stderr, msg, " ".join(args)
- print >>sys.stderr, "Perhaps you need --help ?"
- sys.exit(1)
-
-def print_keys(item, title):
- keys = item.keys()
- keys.sort()
- key_len = max([ len(k) for k in keys ])
-
- print title + ":"
- for key in keys:
- if item.key_type(key) == item.DATE:
- value = time.strftime(planet.TIMEFMT_ISO, item[key])
- else:
- value = str(item[key])
- print " %-*s %s" % (key_len, key, fit_str(value, 74 - key_len))
-
-def fit_str(string, length):
- if len(string) <= length:
- return string
- else:
- return string[:length-4] + " ..."
-
-
-if __name__ == "__main__":
- cache_file = None
- want_ids = 0
- ids = []
-
- command = None
-
- for arg in sys.argv[1:]:
- if arg == "-h" or arg == "--help":
- usage()
- elif arg == "-C" or arg == "--channel":
- if command is not None:
- usage_error("Only one command option may be supplied")
- command = "channel"
- elif arg == "-L" or arg == "--list":
- if command is not None:
- usage_error("Only one command option may be supplied")
- command = "list"
- elif arg == "-K" or arg == "--keys":
- if command is not None:
- usage_error("Only one command option may be supplied")
- command = "keys"
- elif arg == "-I" or arg == "--item":
- if command is not None:
- usage_error("Only one command option may be supplied")
- command = "item"
- want_ids = 1
- elif arg == "-H" or arg == "--hide":
- if command is not None:
- usage_error("Only one command option may be supplied")
- command = "hide"
- want_ids = 1
- elif arg == "-U" or arg == "--unhide":
- if command is not None:
- usage_error("Only one command option may be supplied")
- command = "unhide"
- want_ids = 1
- elif arg.startswith("-"):
- usage_error("Unknown option:", arg)
- else:
- if cache_file is None:
- cache_file = arg
- elif want_ids:
- ids.append(arg)
- else:
- usage_error("Unexpected extra argument:", arg)
-
- if cache_file is None:
- usage_error("Missing expected cache filename")
- elif want_ids and not len(ids):
- usage_error("Missing expected entry ids")
-
- # Open the cache file directly to get the URL it represents
- try:
- db = dbhash.open(cache_file)
- url = db["url"]
- db.close()
- except dbhash.bsddb._db.DBError, e:
- print >>sys.stderr, cache_file + ":", e.args[1]
- sys.exit(1)
- except KeyError:
- print >>sys.stderr, cache_file + ": Probably not a cache file"
- sys.exit(1)
-
- # Now do it the right way :-)
- my_planet = planet.Planet(ConfigParser.ConfigParser())
- my_planet.cache_directory = os.path.dirname(cache_file)
- channel = planet.Channel(my_planet, url)
-
- for item_id in ids:
- if not channel.has_item(item_id):
- print >>sys.stderr, item_id + ": Not in channel"
- sys.exit(1)
-
- # Do the user's bidding
- if command == "channel":
- print_keys(channel, "Channel Keys")
-
- elif command == "item":
- for item_id in ids:
- item = channel.get_item(item_id)
- print_keys(item, "Item Keys for %s" % item_id)
-
- elif command == "list":
- print "Items in Channel:"
- for item in channel.items(hidden=1, sorted=1):
- print " " + item.id
- print " " + time.strftime(planet.TIMEFMT_ISO, item.date)
- if hasattr(item, "title"):
- print " " + fit_str(item.title, 70)
- if hasattr(item, "hidden"):
- print " (hidden)"
-
- elif command == "keys":
- keys = {}
- for item in channel.items():
- for key in item.keys():
- keys[key] = 1
-
- keys = keys.keys()
- keys.sort()
-
- print "Keys used in Channel:"
- for key in keys:
- print " " + key
- print
-
- print "Use --item to output values of particular items."
-
- elif command == "hide":
- for item_id in ids:
- item = channel.get_item(item_id)
- if hasattr(item, "hidden"):
- print item_id + ": Already hidden."
- else:
- item.hidden = "yes"
-
- channel.cache_write()
- print "Done."
-
- elif command == "unhide":
- for item_id in ids:
- item = channel.get_item(item_id)
- if hasattr(item, "hidden"):
- del(item.hidden)
- else:
- print item_id + ": Not hidden."
-
- channel.cache_write()
- print "Done."
diff --git a/code/planet.py b/code/planet.py
deleted file mode 100755
index 41141b6..0000000
--- a/code/planet.py
+++ /dev/null
@@ -1,154 +0,0 @@
-#!/usr/bin/env python
-"""The Planet aggregator.
-
-A flexible and easy-to-use aggregator for generating websites.
-
-Visit http://www.planetplanet.org/ for more information and to download
-the latest version.
-
-Requires Python 2.1, recommends 2.3.
-"""
-
-__authors__ = [ "Scott James Remnant ",
- "Jeff Waugh " ]
-__license__ = "Python"
-
-
-import os
-import sys
-import time
-import locale
-import socket
-import urlparse
-
-import planet
-
-from ConfigParser import ConfigParser
-
-# Default configuration file path
-CONFIG_FILE = "config.ini"
-
-# Defaults for the [Planet] config section
-PLANET_NAME = "Unconfigured Planet"
-PLANET_LINK = "Unconfigured Planet"
-PLANET_FEED = None
-OWNER_NAME = "Anonymous Coward"
-OWNER_EMAIL = ""
-LOG_LEVEL = "WARNING"
-FEED_TIMEOUT = 20 # seconds
-
-# Default template file list
-TEMPLATE_FILES = "examples/basic/planet.html.tmpl"
-
-
-
-def config_get(config, section, option, default=None, raw=0, vars=None):
- """Get a value from the configuration, with a default."""
- if config.has_option(section, option):
- return config.get(section, option, raw=raw, vars=None)
- else:
- return default
-
-def main():
- config_file = CONFIG_FILE
- offline = 0
- verbose = 0
-
- for arg in sys.argv[1:]:
- if arg == "-h" or arg == "--help":
- print "Usage: planet [options] [CONFIGFILE]"
- print
- print "Options:"
- print " -v, --verbose DEBUG level logging during update"
- print " -o, --offline Update the Planet from the cache only"
- print " -h, --help Display this help message and exit"
- print
- sys.exit(0)
- elif arg == "-v" or arg == "--verbose":
- verbose = 1
- elif arg == "-o" or arg == "--offline":
- offline = 1
- elif arg.startswith("-"):
- print >>sys.stderr, "Unknown option:", arg
- sys.exit(1)
- else:
- config_file = arg
-
- # Read the configuration file
- config = ConfigParser()
- config.read(config_file)
- if not config.has_section("Planet"):
- print >>sys.stderr, "Configuration missing [Planet] section."
- sys.exit(1)
-
- # Read the [Planet] config section
- planet_name = config_get(config, "Planet", "name", PLANET_NAME)
- planet_link = config_get(config, "Planet", "link", PLANET_LINK)
- planet_feed = config_get(config, "Planet", "feed", PLANET_FEED)
- owner_name = config_get(config, "Planet", "owner_name", OWNER_NAME)
- owner_email = config_get(config, "Planet", "owner_email", OWNER_EMAIL)
- if verbose:
- log_level = "DEBUG"
- else:
- log_level = config_get(config, "Planet", "log_level", LOG_LEVEL)
- feed_timeout = config_get(config, "Planet", "feed_timeout", FEED_TIMEOUT)
- template_files = config_get(config, "Planet", "template_files",
- TEMPLATE_FILES).split(" ")
-
- # Default feed to the first feed for which there is a template
- if not planet_feed:
- for template_file in template_files:
- name = os.path.splitext(os.path.basename(template_file))[0]
- if name.find('atom')>=0 or name.find('rss')>=0:
- planet_feed = urlparse.urljoin(planet_link, name)
- break
-
- # Define locale
- if config.has_option("Planet", "locale"):
- # The user can specify more than one locale (separated by ":") as
- # fallbacks.
- locale_ok = False
- for user_locale in config.get("Planet", "locale").split(':'):
- user_locale = user_locale.strip()
- try:
- locale.setlocale(locale.LC_ALL, user_locale)
- except locale.Error:
- pass
- else:
- locale_ok = True
- break
- if not locale_ok:
- print >>sys.stderr, "Unsupported locale setting."
- sys.exit(1)
-
- # Activate logging
- planet.logging.basicConfig()
- planet.logging.getLogger().setLevel(planet.logging.getLevelName(log_level))
- log = planet.logging.getLogger("planet.runner")
- try:
- log.warning
- except:
- log.warning = log.warn
-
- if feed_timeout:
- try:
- feed_timeout = float(feed_timeout)
- except:
- log.warning("Feed timeout set to invalid value '%s', skipping", feed_timeout)
- feed_timeout = None
-
- if feed_timeout and not offline:
- socket.setdefaulttimeout(feed_timeout)
- log.debug("Socket timeout set to %d seconds", feed_timeout)
-
- # run the planet
- my_planet = planet.Planet(config)
- my_planet.run(planet_name, planet_link, template_files, offline)
-
- my_planet.generate_all_files(template_files, planet_name,
- planet_link, planet_feed, owner_name, owner_email)
-
-
-if __name__ == "__main__":
- main()
-
diff --git a/code/planet/__init__.py b/code/planet/__init__.py
deleted file mode 100644
index 929920b..0000000
--- a/code/planet/__init__.py
+++ /dev/null
@@ -1,953 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: UTF-8 -*-
-"""Planet aggregator library.
-
-This package is a library for developing web sites or software that
-aggregate RSS, CDF and Atom feeds taken from elsewhere into a single,
-combined feed.
-"""
-
-__version__ = "2.0"
-__authors__ = [ "Scott James Remnant ",
- "Jeff Waugh " ]
-__license__ = "Python"
-
-
-# Modules available without separate import
-import cache
-import feedparser
-import sanitize
-import htmltmpl
-import sgmllib
-try:
- import logging
-except:
- import compat_logging as logging
-
-# Limit the effect of "from planet import *"
-__all__ = ("cache", "feedparser", "htmltmpl", "logging",
- "Planet", "Channel", "NewsItem")
-
-
-import os
-import md5
-import time
-import dbhash
-import re
-
-try:
- from xml.sax.saxutils import escape
-except:
- def escape(data):
- return data.replace("&","&").replace(">",">").replace("<","<")
-
-# Version information (for generator headers)
-VERSION = ("Planet/%s +http://www.planetplanet.org" % __version__)
-
-# Default User-Agent header to send when retreiving feeds
-USER_AGENT = VERSION + " " + feedparser.USER_AGENT
-
-# Default cache directory
-CACHE_DIRECTORY = "cache"
-
-# Default number of items to display from a new feed
-NEW_FEED_ITEMS = 10
-
-# Useful common date/time formats
-TIMEFMT_ISO = "%Y-%m-%dT%H:%M:%S+00:00"
-TIMEFMT_822 = "%a, %d %b %Y %H:%M:%S +0000"
-
-
-# Log instance to use here
-log = logging.getLogger("planet")
-try:
- log.warning
-except:
- log.warning = log.warn
-
-# Defaults for the template file config sections
-ENCODING = "utf-8"
-ITEMS_PER_PAGE = 60
-DAYS_PER_PAGE = 0
-OUTPUT_DIR = "output"
-DATE_FORMAT = "%B %d, %Y %I:%M %p"
-NEW_DATE_FORMAT = "%B %d, %Y"
-ACTIVITY_THRESHOLD = 0
-
-class stripHtml(sgmllib.SGMLParser):
- "remove all tags from the data"
- def __init__(self, data):
- sgmllib.SGMLParser.__init__(self)
- self.result=''
- self.feed(data)
- self.close()
- def handle_data(self, data):
- if data: self.result+=data
-
-def template_info(item, date_format):
- """Produce a dictionary of template information."""
- info = {}
- for key in item.keys():
- if item.key_type(key) == item.DATE:
- date = item.get_as_date(key)
- info[key] = time.strftime(date_format, date)
- info[key + "_iso"] = time.strftime(TIMEFMT_ISO, date)
- info[key + "_822"] = time.strftime(TIMEFMT_822, date)
- else:
- info[key] = item[key]
- if 'title' in item.keys():
- info['title_plain'] = stripHtml(info['title']).result
-
- return info
-
-
-class Planet:
- """A set of channels.
-
- This class represents a set of channels for which the items will
- be aggregated together into one combined feed.
-
- Properties:
- user_agent User-Agent header to fetch feeds with.
- cache_directory Directory to store cached channels in.
- new_feed_items Number of items to display from a new feed.
- filter A regular expression that articles must match.
- exclude A regular expression that articles must not match.
- """
- def __init__(self, config):
- self.config = config
-
- self._channels = []
-
- self.user_agent = USER_AGENT
- self.cache_directory = CACHE_DIRECTORY
- self.new_feed_items = NEW_FEED_ITEMS
- self.filter = None
- self.exclude = None
-
- def tmpl_config_get(self, template, option, default=None, raw=0, vars=None):
- """Get a template value from the configuration, with a default."""
- if self.config.has_option(template, option):
- return self.config.get(template, option, raw=raw, vars=None)
- elif self.config.has_option("Planet", option):
- return self.config.get("Planet", option, raw=raw, vars=None)
- else:
- return default
-
- def gather_channel_info(self, template_file="Planet"):
- date_format = self.tmpl_config_get(template_file,
- "date_format", DATE_FORMAT, raw=1)
-
- activity_threshold = int(self.tmpl_config_get(template_file,
- "activity_threshold",
- ACTIVITY_THRESHOLD))
-
- if activity_threshold:
- activity_horizon = \
- time.gmtime(time.time()-86400*activity_threshold)
- else:
- activity_horizon = 0
-
- channels = {}
- channels_list = []
- for channel in self.channels(hidden=1):
- channels[channel] = template_info(channel, date_format)
- channels_list.append(channels[channel])
-
- # identify inactive feeds
- if activity_horizon:
- latest = channel.items(sorted=1)
- if len(latest)==0 or latest[0].date < activity_horizon:
- channels[channel]["message"] = \
- "no activity in %d days" % activity_threshold
-
- # report channel level errors
- if not channel.url_status: continue
- status = int(channel.url_status)
- if status == 403:
- channels[channel]["message"] = "403: forbidden"
- elif status == 404:
- channels[channel]["message"] = "404: not found"
- elif status == 408:
- channels[channel]["message"] = "408: request timeout"
- elif status == 410:
- channels[channel]["message"] = "410: gone"
- elif status == 500:
- channels[channel]["message"] = "internal server error"
- elif status >= 400:
- channels[channel]["message"] = "http status %s" % status
-
- return channels, channels_list
-
- def gather_items_info(self, channels, template_file="Planet", channel_list=None):
- items_list = []
- prev_date = []
- prev_channel = None
-
- date_format = self.tmpl_config_get(template_file,
- "date_format", DATE_FORMAT, raw=1)
- items_per_page = int(self.tmpl_config_get(template_file,
- "items_per_page", ITEMS_PER_PAGE))
- days_per_page = int(self.tmpl_config_get(template_file,
- "days_per_page", DAYS_PER_PAGE))
- new_date_format = self.tmpl_config_get(template_file,
- "new_date_format", NEW_DATE_FORMAT, raw=1)
-
- for newsitem in self.items(max_items=items_per_page,
- max_days=days_per_page,
- channels=channel_list):
- item_info = template_info(newsitem, date_format)
- chan_info = channels[newsitem._channel]
- for k, v in chan_info.items():
- item_info["channel_" + k] = v
-
- # Check for the start of a new day
- if prev_date[:3] != newsitem.date[:3]:
- prev_date = newsitem.date
- item_info["new_date"] = time.strftime(new_date_format,
- newsitem.date)
-
- # Check for the start of a new channel
- if item_info.has_key("new_date") \
- or prev_channel != newsitem._channel:
- prev_channel = newsitem._channel
- item_info["new_channel"] = newsitem._channel.url
-
- items_list.append(item_info)
-
- return items_list
-
- def run(self, planet_name, planet_link, template_files, offline = False):
- log = logging.getLogger("planet.runner")
-
- # Create a planet
- log.info("Loading cached data")
- if self.config.has_option("Planet", "cache_directory"):
- self.cache_directory = self.config.get("Planet", "cache_directory")
- if self.config.has_option("Planet", "new_feed_items"):
- self.new_feed_items = int(self.config.get("Planet", "new_feed_items"))
- self.user_agent = "%s +%s %s" % (planet_name, planet_link,
- self.user_agent)
- if self.config.has_option("Planet", "filter"):
- self.filter = self.config.get("Planet", "filter")
-
- # The other configuration blocks are channels to subscribe to
- for feed_url in self.config.sections():
- if feed_url == "Planet" or feed_url in template_files:
- continue
-
- # Create a channel, configure it and subscribe it
- channel = Channel(self, feed_url)
- self.subscribe(channel)
-
- # Update it
- try:
- if not offline and not channel.url_status == '410':
- channel.update()
- except KeyboardInterrupt:
- raise
- except:
- log.exception("Update of <%s> failed", feed_url)
-
- def generate_all_files(self, template_files, planet_name,
- planet_link, planet_feed, owner_name, owner_email):
-
- log = logging.getLogger("planet.runner")
- # Go-go-gadget-template
- for template_file in template_files:
- manager = htmltmpl.TemplateManager()
- log.info("Processing template %s", template_file)
- try:
- template = manager.prepare(template_file)
- except htmltmpl.TemplateError:
- template = manager.prepare(os.path.basename(template_file))
- # Read the configuration
- output_dir = self.tmpl_config_get(template_file,
- "output_dir", OUTPUT_DIR)
- date_format = self.tmpl_config_get(template_file,
- "date_format", DATE_FORMAT, raw=1)
- encoding = self.tmpl_config_get(template_file, "encoding", ENCODING)
-
- # We treat each template individually
- base = os.path.splitext(os.path.basename(template_file))[0]
- url = os.path.join(planet_link, base)
- output_file = os.path.join(output_dir, base)
-
- # Gather information
- channels, channels_list = self.gather_channel_info(template_file)
- items_list = self.gather_items_info(channels, template_file)
-
- # Gather item information
-
- # Process the template
- tp = htmltmpl.TemplateProcessor(html_escape=0)
- tp.set("Items", items_list)
- tp.set("Channels", channels_list)
-
- # Generic information
- tp.set("generator", VERSION)
- tp.set("name", planet_name)
- tp.set("link", planet_link)
- tp.set("owner_name", owner_name)
- tp.set("owner_email", owner_email)
- tp.set("url", url)
-
- if planet_feed:
- tp.set("feed", planet_feed)
- tp.set("feedtype", planet_feed.find('rss')>=0 and 'rss' or 'atom')
-
- # Update time
- date = time.gmtime()
- tp.set("date", time.strftime(date_format, date))
- tp.set("date_iso", time.strftime(TIMEFMT_ISO, date))
- tp.set("date_822", time.strftime(TIMEFMT_822, date))
-
- try:
- log.info("Writing %s", output_file)
- output_fd = open(output_file, "w")
- if encoding.lower() in ("utf-8", "utf8"):
- # UTF-8 output is the default because we use that internally
- output_fd.write(tp.process(template))
- elif encoding.lower() in ("xml", "html", "sgml"):
- # Magic for Python 2.3 users
- output = tp.process(template).decode("utf-8")
- output_fd.write(output.encode("ascii", "xmlcharrefreplace"))
- else:
- # Must be a "known" encoding
- output = tp.process(template).decode("utf-8")
- output_fd.write(output.encode(encoding, "replace"))
- output_fd.close()
- except KeyboardInterrupt:
- raise
- except:
- log.exception("Write of %s failed", output_file)
-
- def channels(self, hidden=0, sorted=1):
- """Return the list of channels."""
- channels = []
- for channel in self._channels:
- if hidden or not channel.has_key("hidden"):
- channels.append((channel.name, channel))
-
- if sorted:
- channels.sort()
-
- return [ c[-1] for c in channels ]
-
- def find_by_basename(self, basename):
- for channel in self._channels:
- if basename == channel.cache_basename(): return channel
-
- def subscribe(self, channel):
- """Subscribe the planet to the channel."""
- self._channels.append(channel)
-
- def unsubscribe(self, channel):
- """Unsubscribe the planet from the channel."""
- self._channels.remove(channel)
-
- def items(self, hidden=0, sorted=1, max_items=0, max_days=0, channels=None):
- """Return an optionally filtered list of items in the channel.
-
- The filters are applied in the following order:
-
- If hidden is true then items in hidden channels and hidden items
- will be returned.
-
- If sorted is true then the item list will be sorted with the newest
- first.
-
- If max_items is non-zero then this number of items, at most, will
- be returned.
-
- If max_days is non-zero then any items older than the newest by
- this number of days won't be returned. Requires sorted=1 to work.
-
-
- The sharp-eyed will note that this looks a little strange code-wise,
- it turns out that Python gets *really* slow if we try to sort the
- actual items themselves. Also we use mktime here, but it's ok
- because we discard the numbers and just need them to be relatively
- consistent between each other.
- """
- planet_filter_re = None
- if self.filter:
- planet_filter_re = re.compile(self.filter, re.I)
- planet_exclude_re = None
- if self.exclude:
- planet_exclude_re = re.compile(self.exclude, re.I)
-
- items = []
- seen_guids = {}
- if not channels: channels=self.channels(hidden=hidden, sorted=0)
- for channel in channels:
- for item in channel._items.values():
- if hidden or not item.has_key("hidden"):
-
- channel_filter_re = None
- if channel.filter:
- channel_filter_re = re.compile(channel.filter,
- re.I)
- channel_exclude_re = None
- if channel.exclude:
- channel_exclude_re = re.compile(channel.exclude,
- re.I)
- if (planet_filter_re or planet_exclude_re \
- or channel_filter_re or channel_exclude_re):
- title = ""
- if item.has_key("title"):
- title = item.title
- content = item.get_content("content")
-
- if planet_filter_re:
- if not (planet_filter_re.search(title) \
- or planet_filter_re.search(content)):
- continue
-
- if planet_exclude_re:
- if (planet_exclude_re.search(title) \
- or planet_exclude_re.search(content)):
- continue
-
- if channel_filter_re:
- if not (channel_filter_re.search(title) \
- or channel_filter_re.search(content)):
- continue
-
- if channel_exclude_re:
- if (channel_exclude_re.search(title) \
- or channel_exclude_re.search(content)):
- continue
-
- if not seen_guids.has_key(item.id):
- seen_guids[item.id] = 1;
- items.append((time.mktime(item.date), item.order, item))
-
- # Sort the list
- if sorted:
- items.sort()
- items.reverse()
-
- # Apply max_items filter
- if len(items) and max_items:
- items = items[:max_items]
-
- # Apply max_days filter
- if len(items) and max_days:
- max_count = 0
- max_time = items[0][0] - max_days * 84600
- for item in items:
- if item[0] > max_time:
- max_count += 1
- else:
- items = items[:max_count]
- break
-
- return [ i[-1] for i in items ]
-
-class Channel(cache.CachedInfo):
- """A list of news items.
-
- This class represents a list of news items taken from the feed of
- a website or other source.
-
- Properties:
- url URL of the feed.
- url_etag E-Tag of the feed URL.
- url_modified Last modified time of the feed URL.
- url_status Last HTTP status of the feed URL.
- hidden Channel should be hidden (True if exists).
- name Name of the feed owner, or feed title.
- next_order Next order number to be assigned to NewsItem
-
- updated Correct UTC-Normalised update time of the feed.
- last_updated Correct UTC-Normalised time the feed was last updated.
-
- id An identifier the feed claims is unique (*).
- title One-line title (*).
- link Link to the original format feed (*).
- tagline Short description of the feed (*).
- info Longer description of the feed (*).
-
- modified Date the feed claims to have been modified (*).
-
- author Name of the author (*).
- publisher Name of the publisher (*).
- generator Name of the feed generator (*).
- category Category name (*).
- copyright Copyright information for humans to read (*).
- license Link to the licence for the content (*).
- docs Link to the specification of the feed format (*).
- language Primary language (*).
- errorreportsto E-Mail address to send error reports to (*).
-
- image_url URL of an associated image (*).
- image_link Link to go with the associated image (*).
- image_title Alternative text of the associated image (*).
- image_width Width of the associated image (*).
- image_height Height of the associated image (*).
-
- filter A regular expression that articles must match.
- exclude A regular expression that articles must not match.
-
- Properties marked (*) will only be present if the original feed
- contained them. Note that the optional 'modified' date field is simply
- a claim made by the item and parsed from the information given, 'updated'
- (and 'last_updated') are far more reliable sources of information.
-
- Some feeds may define additional properties to those above.
- """
- IGNORE_KEYS = ("links", "contributors", "textinput", "cloud", "categories",
- "url", "href", "url_etag", "url_modified", "tags", "itunes_explicit")
-
- def __init__(self, planet, url):
- if not os.path.isdir(planet.cache_directory):
- os.makedirs(planet.cache_directory)
- cache_filename = cache.filename(planet.cache_directory, url)
- cache_file = dbhash.open(cache_filename, "c", 0666)
-
- cache.CachedInfo.__init__(self, cache_file, url, root=1)
-
- self._items = {}
- self._planet = planet
- self._expired = []
- self.url = url
- # retain the original URL for error reporting
- self.configured_url = url
- self.url_etag = None
- self.url_status = None
- self.url_modified = None
- self.name = None
- self.updated = None
- self.last_updated = None
- self.filter = None
- self.exclude = None
- self.next_order = "0"
- self.cache_read()
- self.cache_read_entries()
-
- if planet.config.has_section(url):
- for option in planet.config.options(url):
- value = planet.config.get(url, option)
- self.set_as_string(option, value, cached=0)
-
- def has_item(self, id_):
- """Check whether the item exists in the channel."""
- return self._items.has_key(id_)
-
- def get_item(self, id_):
- """Return the item from the channel."""
- return self._items[id_]
-
- # Special methods
- __contains__ = has_item
-
- def items(self, hidden=0, sorted=0):
- """Return the item list."""
- items = []
- for item in self._items.values():
- if hidden or not item.has_key("hidden"):
- items.append((time.mktime(item.date), item.order, item))
-
- if sorted:
- items.sort()
- items.reverse()
-
- return [ i[-1] for i in items ]
-
- def __iter__(self):
- """Iterate the sorted item list."""
- return iter(self.items(sorted=1))
-
- def cache_read_entries(self):
- """Read entry information from the cache."""
- keys = self._cache.keys()
- for key in keys:
- if key.find(" ") != -1: continue
- if self.has_key(key): continue
-
- item = NewsItem(self, key)
- self._items[key] = item
-
- def cache_basename(self):
- return cache.filename('',self._id)
-
- def cache_write(self, sync=1):
- """Write channel and item information to the cache."""
- for item in self._items.values():
- item.cache_write(sync=0)
- for item in self._expired:
- item.cache_clear(sync=0)
- cache.CachedInfo.cache_write(self, sync)
-
- self._expired = []
-
- def feed_information(self):
- """
- Returns a description string for the feed embedded in this channel.
-
- This will usually simply be the feed url embedded in <>, but in the
- case where the current self.url has changed from the original
- self.configured_url the string will contain both pieces of information.
- This is so that the URL in question is easier to find in logging
- output: getting an error about a URL that doesn't appear in your config
- file is annoying.
- """
- if self.url == self.configured_url:
- return "<%s>" % self.url
- else:
- return "<%s> (formerly <%s>)" % (self.url, self.configured_url)
-
- def update(self):
- """Download the feed to refresh the information.
-
- This does the actual work of pulling down the feed and if it changes
- updates the cached information about the feed and entries within it.
- """
- info = feedparser.parse(self.url,
- etag=self.url_etag, modified=self.url_modified,
- agent=self._planet.user_agent)
- if info.has_key("status"):
- self.url_status = str(info.status)
- elif info.has_key("entries") and len(info.entries)>0:
- self.url_status = str(200)
- elif info.bozo and info.bozo_exception.__class__.__name__=='Timeout':
- self.url_status = str(408)
- else:
- self.url_status = str(500)
-
- if self.url_status == '301' and \
- (info.has_key("entries") and len(info.entries)>0):
- log.warning("Feed has moved from <%s> to <%s>", self.url, info.url)
- try:
- os.link(cache.filename(self._planet.cache_directory, self.url),
- cache.filename(self._planet.cache_directory, info.url))
- except:
- pass
- self.url = info.url
- elif self.url_status == '304':
- log.info("Feed %s unchanged", self.feed_information())
- return
- elif self.url_status == '410':
- log.info("Feed %s gone", self.feed_information())
- self.cache_write()
- return
- elif self.url_status == '408':
- log.warning("Feed %s timed out", self.feed_information())
- return
- elif int(self.url_status) >= 400:
- log.error("Error %s while updating feed %s",
- self.url_status, self.feed_information())
- return
- else:
- log.info("Updating feed %s", self.feed_information())
-
- self.url_etag = info.has_key("etag") and info.etag or None
- self.url_modified = info.has_key("modified") and info.modified or None
- if self.url_etag is not None:
- log.debug("E-Tag: %s", self.url_etag)
- if self.url_modified is not None:
- log.debug("Last Modified: %s",
- time.strftime(TIMEFMT_ISO, self.url_modified))
-
- self.update_info(info.feed)
- self.update_entries(info.entries)
- self.cache_write()
-
- def update_info(self, feed):
- """Update information from the feed.
-
- This reads the feed information supplied by feedparser and updates
- the cached information about the feed. These are the various
- potentially interesting properties that you might care about.
- """
- for key in feed.keys():
- if key in self.IGNORE_KEYS or key + "_parsed" in self.IGNORE_KEYS:
- # Ignored fields
- pass
- elif feed.has_key(key + "_parsed"):
- # Ignore unparsed date fields
- pass
- elif key.endswith("_detail"):
- # retain name and email sub-fields
- if feed[key].has_key('name') and feed[key].name:
- self.set_as_string(key.replace("_detail","_name"), \
- feed[key].name)
- if feed[key].has_key('email') and feed[key].email:
- self.set_as_string(key.replace("_detail","_email"), \
- feed[key].email)
- elif key == "items":
- # Ignore items field
- pass
- elif key.endswith("_parsed"):
- # Date fields
- if feed[key] is not None:
- self.set_as_date(key[:-len("_parsed")], feed[key])
- elif key == "image":
- # Image field: save all the information
- if feed[key].has_key("url"):
- self.set_as_string(key + "_url", feed[key].url)
- if feed[key].has_key("link"):
- self.set_as_string(key + "_link", feed[key].link)
- if feed[key].has_key("title"):
- self.set_as_string(key + "_title", feed[key].title)
- if feed[key].has_key("width"):
- self.set_as_string(key + "_width", str(feed[key].width))
- if feed[key].has_key("height"):
- self.set_as_string(key + "_height", str(feed[key].height))
- elif isinstance(feed[key], (str, unicode)):
- # String fields
- try:
- detail = key + '_detail'
- if feed.has_key(detail) and feed[detail].has_key('type'):
- if feed[detail].type == 'text/html':
- feed[key] = sanitize.HTML(feed[key])
- elif feed[detail].type == 'text/plain':
- feed[key] = escape(feed[key])
- self.set_as_string(key, feed[key])
- except KeyboardInterrupt:
- raise
- except:
- log.exception("Ignored '%s' of <%s>, unknown format",
- key, self.url)
-
- def update_entries(self, entries):
- """Update entries from the feed.
-
- This reads the entries supplied by feedparser and updates the
- cached information about them. It's at this point we update
- the 'updated' timestamp and keep the old one in 'last_updated',
- these provide boundaries for acceptable entry times.
-
- If this is the first time a feed has been updated then most of the
- items will be marked as hidden, according to Planet.new_feed_items.
-
- If the feed does not contain items which, according to the sort order,
- should be there; those items are assumed to have been expired from
- the feed or replaced and are removed from the cache.
- """
- if not len(entries):
- return
-
- self.last_updated = self.updated
- self.updated = time.gmtime()
-
- new_items = []
- feed_items = []
- for entry in entries:
- # Try really hard to find some kind of unique identifier
- if entry.has_key("id"):
- entry_id = cache.utf8(entry.id)
- elif entry.has_key("link"):
- entry_id = cache.utf8(entry.link)
- elif entry.has_key("title"):
- entry_id = (self.url + "/"
- + md5.new(cache.utf8(entry.title)).hexdigest())
- elif entry.has_key("summary"):
- entry_id = (self.url + "/"
- + md5.new(cache.utf8(entry.summary)).hexdigest())
- else:
- log.error("Unable to find or generate id, entry ignored")
- continue
-
- # Create the item if necessary and update
- if self.has_item(entry_id):
- item = self._items[entry_id]
- else:
- item = NewsItem(self, entry_id)
- self._items[entry_id] = item
- new_items.append(item)
- item.update(entry)
- feed_items.append(entry_id)
-
- # Hide excess items the first time through
- if self.last_updated is None and self._planet.new_feed_items \
- and len(feed_items) > self._planet.new_feed_items:
- item.hidden = "yes"
- log.debug("Marked <%s> as hidden (new feed)", entry_id)
-
- # Assign order numbers in reverse
- new_items.reverse()
- for item in new_items:
- item.order = self.next_order = str(int(self.next_order) + 1)
-
- # Check for expired or replaced items
- feed_count = len(feed_items)
- log.debug("Items in Feed: %d", feed_count)
- for item in self.items(sorted=1):
- if feed_count < 1:
- break
- elif item.id in feed_items:
- feed_count -= 1
- elif item._channel.url_status != '226':
- del(self._items[item.id])
- self._expired.append(item)
- log.debug("Removed expired or replaced item <%s>", item.id)
-
- def get_name(self, key):
- """Return the key containing the name."""
- for key in ("name", "title"):
- if self.has_key(key) and self.key_type(key) != self.NULL:
- return self.get_as_string(key)
-
- return ""
-
-class NewsItem(cache.CachedInfo):
- """An item of news.
-
- This class represents a single item of news on a channel. They're
- created by members of the Channel class and accessible through it.
-
- Properties:
- id Channel-unique identifier for this item.
- id_hash Relatively short, printable cryptographic hash of id
- date Corrected UTC-Normalised update time, for sorting.
- order Order in which items on the same date can be sorted.
- hidden Item should be hidden (True if exists).
-
- title One-line title (*).
- link Link to the original format text (*).
- summary Short first-page summary (*).
- content Full HTML content.
-
- modified Date the item claims to have been modified (*).
- issued Date the item claims to have been issued (*).
- created Date the item claims to have been created (*).
- expired Date the item claims to expire (*).
-
- author Name of the author (*).
- publisher Name of the publisher (*).
- category Category name (*).
- comments Link to a page to enter comments (*).
- license Link to the licence for the content (*).
- source_name Name of the original source of this item (*).
- source_link Link to the original source of this item (*).
-
- Properties marked (*) will only be present if the original feed
- contained them. Note that the various optional date fields are
- simply claims made by the item and parsed from the information
- given, 'date' is a far more reliable source of information.
-
- Some feeds may define additional properties to those above.
- """
- IGNORE_KEYS = ("categories", "contributors", "enclosures", "links",
- "guidislink", "date", "tags")
-
- def __init__(self, channel, id_):
- cache.CachedInfo.__init__(self, channel._cache, id_)
-
- self._channel = channel
- self.id = id_
- self.id_hash = md5.new(id_).hexdigest()
- self.date = None
- self.order = None
- self.content = None
- self.cache_read()
-
- def update(self, entry):
- """Update the item from the feedparser entry given."""
- for key in entry.keys():
- if key in self.IGNORE_KEYS or key + "_parsed" in self.IGNORE_KEYS:
- # Ignored fields
- pass
- elif entry.has_key(key + "_parsed"):
- # Ignore unparsed date fields
- pass
- elif key.endswith("_detail"):
- # retain name, email, and language sub-fields
- if entry[key].has_key('name') and entry[key].name:
- self.set_as_string(key.replace("_detail","_name"), \
- entry[key].name)
- if entry[key].has_key('email') and entry[key].email:
- self.set_as_string(key.replace("_detail","_email"), \
- entry[key].email)
- if entry[key].has_key('language') and entry[key].language and \
- (not self._channel.has_key('language') or \
- entry[key].language != self._channel.language):
- self.set_as_string(key.replace("_detail","_language"), \
- entry[key].language)
- elif key.endswith("_parsed"):
- # Date fields
- if entry[key] is not None:
- self.set_as_date(key[:-len("_parsed")], entry[key])
- elif key == "source":
- # Source field: save both url and value
- if entry[key].has_key("value"):
- self.set_as_string(key + "_name", entry[key].value)
- if entry[key].has_key("url"):
- self.set_as_string(key + "_link", entry[key].url)
- elif key == "content":
- # Content field: concatenate the values
- value = ""
- for item in entry[key]:
- if item.type == 'text/html':
- item.value = sanitize.HTML(item.value)
- elif item.type == 'text/plain':
- item.value = escape(item.value)
- if item.has_key('language') and item.language and \
- (not self._channel.has_key('language') or
- item.language != self._channel.language) :
- self.set_as_string(key + "_language", item.language)
- value += cache.utf8(item.value)
- self.set_as_string(key, value)
- elif isinstance(entry[key], (str, unicode)):
- # String fields
- try:
- detail = key + '_detail'
- if entry.has_key(detail):
- if entry[detail].has_key('type'):
- if entry[detail].type == 'text/html':
- entry[key] = sanitize.HTML(entry[key])
- elif entry[detail].type == 'text/plain':
- entry[key] = escape(entry[key])
- self.set_as_string(key, entry[key])
- except KeyboardInterrupt:
- raise
- except:
- log.exception("Ignored '%s' of <%s>, unknown format",
- key, self.id)
-
- # Generate the date field if we need to
- self.get_date("date")
-
- def get_date(self, key):
- """Get (or update) the date key.
-
- We check whether the date the entry claims to have been changed is
- since we last updated this feed and when we pulled the feed off the
- site.
-
- If it is then it's probably not bogus, and we'll sort accordingly.
-
- If it isn't then we bound it appropriately, this ensures that
- entries appear in posting sequence but don't overlap entries
- added in previous updates and don't creep into the next one.
- """
-
- for other_key in ("updated", "modified", "published", "issued", "created"):
- if self.has_key(other_key):
- date = self.get_as_date(other_key)
- break
- else:
- date = None
-
- if date is not None:
- if date > self._channel.updated:
- date = self._channel.updated
-# elif date < self._channel.last_updated:
-# date = self._channel.updated
- elif self.has_key(key) and self.key_type(key) != self.NULL:
- return self.get_as_date(key)
- else:
- date = self._channel.updated
-
- self.set_as_date(key, date)
- return date
-
- def get_content(self, key):
- """Return the key containing the content."""
- for key in ("content", "tagline", "summary"):
- if self.has_key(key) and self.key_type(key) != self.NULL:
- return self.get_as_string(key)
-
- return ""
diff --git a/code/planet/atomstyler.py b/code/planet/atomstyler.py
deleted file mode 100644
index 9220702..0000000
--- a/code/planet/atomstyler.py
+++ /dev/null
@@ -1,124 +0,0 @@
-from xml.dom import minidom, Node
-from urlparse import urlparse, urlunparse
-from xml.parsers.expat import ExpatError
-from htmlentitydefs import name2codepoint
-import re
-
-# select and apply an xml:base for this entry
-class relativize:
- def __init__(self, parent):
- self.score = {}
- self.links = []
- self.collect_and_tally(parent)
- self.base = self.select_optimal_base()
- if self.base:
- if not parent.hasAttribute('xml:base'):
- self.rebase(parent)
- parent.setAttribute('xml:base', self.base)
-
- # collect and tally cite, href and src attributes
- def collect_and_tally(self,parent):
- uri = None
- if parent.hasAttribute('cite'): uri=parent.getAttribute('cite')
- if parent.hasAttribute('href'): uri=parent.getAttribute('href')
- if parent.hasAttribute('src'): uri=parent.getAttribute('src')
-
- if uri:
- parts=urlparse(uri)
- if parts[0].lower() == 'http':
- parts = (parts[1]+parts[2]).split('/')
- base = None
- for i in range(1,len(parts)):
- base = tuple(parts[0:i])
- self.score[base] = self.score.get(base,0) + len(base)
- if base and base not in self.links: self.links.append(base)
-
- for node in parent.childNodes:
- if node.nodeType == Node.ELEMENT_NODE:
- self.collect_and_tally(node)
-
- # select the xml:base with the highest score
- def select_optimal_base(self):
- if not self.score: return None
- for link in self.links:
- self.score[link] = 0
- winner = max(self.score.values())
- if not winner: return None
- for key in self.score.keys():
- if self.score[key] == winner:
- if winner == len(key): return None
- return urlunparse(('http', key[0], '/'.join(key[1:]), '', '', '')) + '/'
-
- # rewrite cite, href and src attributes using this base
- def rebase(self,parent):
- uri = None
- if parent.hasAttribute('cite'): uri=parent.getAttribute('cite')
- if parent.hasAttribute('href'): uri=parent.getAttribute('href')
- if parent.hasAttribute('src'): uri=parent.getAttribute('src')
- if uri and uri.startswith(self.base):
- uri = uri[len(self.base):] or '.'
- if parent.hasAttribute('href'): uri=parent.setAttribute('href', uri)
- if parent.hasAttribute('src'): uri=parent.setAttribute('src', uri)
-
- for node in parent.childNodes:
- if node.nodeType == Node.ELEMENT_NODE:
- self.rebase(node)
-
-# convert type="html" to type="plain" or type="xhtml" as appropriate
-def retype(parent):
- for node in parent.childNodes:
- if node.nodeType == Node.ELEMENT_NODE:
-
- if node.hasAttribute('type') and node.getAttribute('type') == 'html':
- if len(node.childNodes)==0:
- node.removeAttribute('type')
- elif len(node.childNodes)==1:
-
- # replace html entity defs with utf-8
- chunks=re.split('&(\w+);', node.childNodes[0].nodeValue)
- for i in range(1,len(chunks),2):
- if chunks[i] in ['amp', 'lt', 'gt', 'apos', 'quot']:
- chunks[i] ='&' + chunks[i] +';'
- elif chunks[i] in name2codepoint:
- chunks[i]=unichr(name2codepoint[chunks[i]])
- else:
- chunks[i]='&' + chunks[i] + ';'
- text = u"".join(chunks)
-
- try:
- # see if the resulting text is a well-formed XML fragment
- div = '
and
- # only if all the remaining content is nested underneath it.
- # This means that the divs would be retained in the following:
- #
foo
bar
- if pieces and (pieces[0] == '
' or pieces[0].startswith('
':
- depth = 0
- for piece in pieces[:-1]:
- if piece.startswith(''):
- depth -= 1
- if depth == 0: break
- elif piece.startswith('<') and not piece.endswith('/>'):
- depth += 1
- else:
- pieces = pieces[1:-1]
-
- output = ''.join(pieces)
- if stripWhitespace:
- output = output.strip()
- if not expectingText: return output
-
- # decode base64 content
- if base64 and self.contentparams.get('base64', 0):
- try:
- output = base64.decodestring(output)
- except binascii.Error:
- pass
- except binascii.Incomplete:
- pass
-
- # resolve relative URIs
- if (element in self.can_be_relative_uri) and output:
- output = self.resolveURI(output)
-
- # decode entities within embedded markup
- if not self.contentparams.get('base64', 0):
- output = self.decodeEntities(element, output)
-
- # remove temporary cruft from contentparams
- try:
- del self.contentparams['mode']
- except KeyError:
- pass
- try:
- del self.contentparams['base64']
- except KeyError:
- pass
-
- # resolve relative URIs within embedded markup
- if self.mapContentType(self.contentparams.get('type', 'text/html')) in self.html_types:
- if element in self.can_contain_relative_uris:
- output = _resolveRelativeURIs(output, self.baseuri, self.encoding)
-
- # sanitize embedded markup
- if self.mapContentType(self.contentparams.get('type', 'text/html')) in self.html_types:
- if element in self.can_contain_dangerous_markup:
- output = _sanitizeHTML(output, self.encoding)
-
- if self.encoding and type(output) != type(u''):
- try:
- output = unicode(output, self.encoding)
- except:
- pass
-
- # address common error where people take data that is already
- # utf-8, presume that it is iso-8859-1, and re-encode it.
- if self.encoding=='utf-8' and type(output) == type(u''):
- try:
- output = unicode(output.encode('iso-8859-1'), 'utf-8')
- except:
- pass
-
- # map win-1252 extensions to the proper code points
- if type(output) == type(u''):
- output = u''.join([c in cp1252 and cp1252[c] or c for c in output])
-
- # categories/tags/keywords/whatever are handled in _end_category
- if element == 'category':
- return output
-
- # store output in appropriate place(s)
- if self.inentry and not self.insource:
- if element == 'content':
- self.entries[-1].setdefault(element, [])
- contentparams = copy.deepcopy(self.contentparams)
- contentparams['value'] = output
- self.entries[-1][element].append(contentparams)
- elif element == 'link':
- self.entries[-1][element] = output
- if output:
- self.entries[-1]['links'][-1]['href'] = output
- else:
- if element == 'description':
- element = 'summary'
- self.entries[-1][element] = output
- if self.incontent:
- contentparams = copy.deepcopy(self.contentparams)
- contentparams['value'] = output
- self.entries[-1][element + '_detail'] = contentparams
- elif (self.infeed or self.insource) and (not self.intextinput) and (not self.inimage):
- context = self._getContext()
- if element == 'description':
- element = 'subtitle'
- context[element] = output
- if element == 'link':
- context['links'][-1]['href'] = output
- elif self.incontent:
- contentparams = copy.deepcopy(self.contentparams)
- contentparams['value'] = output
- context[element + '_detail'] = contentparams
- return output
-
- def pushContent(self, tag, attrsD, defaultContentType, expectingText):
- self.incontent += 1
- self.contentparams = FeedParserDict({
- 'type': self.mapContentType(attrsD.get('type', defaultContentType)),
- 'language': self.lang,
- 'base': self.baseuri})
- self.contentparams['base64'] = self._isBase64(attrsD, self.contentparams)
- self.push(tag, expectingText)
-
- def popContent(self, tag):
- value = self.pop(tag)
- self.incontent -= 1
- self.contentparams.clear()
- return value
-
- def _mapToStandardPrefix(self, name):
- colonpos = name.find(':')
- if colonpos <> -1:
- prefix = name[:colonpos]
- suffix = name[colonpos+1:]
- prefix = self.namespacemap.get(prefix, prefix)
- name = prefix + ':' + suffix
- return name
-
- def _getAttribute(self, attrsD, name):
- return attrsD.get(self._mapToStandardPrefix(name))
-
- def _isBase64(self, attrsD, contentparams):
- if attrsD.get('mode', '') == 'base64':
- return 1
- if self.contentparams['type'].startswith('text/'):
- return 0
- if self.contentparams['type'].endswith('+xml'):
- return 0
- if self.contentparams['type'].endswith('/xml'):
- return 0
- return 1
-
- def _itsAnHrefDamnIt(self, attrsD):
- href = attrsD.get('url', attrsD.get('uri', attrsD.get('href', None)))
- if href:
- try:
- del attrsD['url']
- except KeyError:
- pass
- try:
- del attrsD['uri']
- except KeyError:
- pass
- attrsD['href'] = href
- return attrsD
-
- def _save(self, key, value):
- context = self._getContext()
- context.setdefault(key, value)
-
- def _start_rss(self, attrsD):
- versionmap = {'0.91': 'rss091u',
- '0.92': 'rss092',
- '0.93': 'rss093',
- '0.94': 'rss094'}
- if not self.version:
- attr_version = attrsD.get('version', '')
- version = versionmap.get(attr_version)
- if version:
- self.version = version
- elif attr_version.startswith('2.'):
- self.version = 'rss20'
- else:
- self.version = 'rss'
-
- def _start_dlhottitles(self, attrsD):
- self.version = 'hotrss'
-
- def _start_channel(self, attrsD):
- self.infeed = 1
- self._cdf_common(attrsD)
- _start_feedinfo = _start_channel
-
- def _cdf_common(self, attrsD):
- if attrsD.has_key('lastmod'):
- self._start_modified({})
- self.elementstack[-1][-1] = attrsD['lastmod']
- self._end_modified()
- if attrsD.has_key('href'):
- self._start_link({})
- self.elementstack[-1][-1] = attrsD['href']
- self._end_link()
-
- def _start_feed(self, attrsD):
- self.infeed = 1
- versionmap = {'0.1': 'atom01',
- '0.2': 'atom02',
- '0.3': 'atom03'}
- if not self.version:
- attr_version = attrsD.get('version')
- version = versionmap.get(attr_version)
- if version:
- self.version = version
- else:
- self.version = 'atom'
-
- def _end_channel(self):
- self.infeed = 0
- _end_feed = _end_channel
-
- def _start_image(self, attrsD):
- self.inimage = 1
- self.push('image', 0)
- context = self._getContext()
- context.setdefault('image', FeedParserDict())
-
- def _end_image(self):
- self.pop('image')
- self.inimage = 0
-
- def _start_textinput(self, attrsD):
- self.intextinput = 1
- self.push('textinput', 0)
- context = self._getContext()
- context.setdefault('textinput', FeedParserDict())
- _start_textInput = _start_textinput
-
- def _end_textinput(self):
- self.pop('textinput')
- self.intextinput = 0
- _end_textInput = _end_textinput
-
- def _start_author(self, attrsD):
- self.inauthor = 1
- self.push('author', 1)
- _start_managingeditor = _start_author
- _start_dc_author = _start_author
- _start_dc_creator = _start_author
- _start_itunes_author = _start_author
-
- def _end_author(self):
- self.pop('author')
- self.inauthor = 0
- self._sync_author_detail()
- _end_managingeditor = _end_author
- _end_dc_author = _end_author
- _end_dc_creator = _end_author
- _end_itunes_author = _end_author
-
- def _start_itunes_owner(self, attrsD):
- self.inpublisher = 1
- self.push('publisher', 0)
-
- def _end_itunes_owner(self):
- self.pop('publisher')
- self.inpublisher = 0
- self._sync_author_detail('publisher')
-
- def _start_contributor(self, attrsD):
- self.incontributor = 1
- context = self._getContext()
- context.setdefault('contributors', [])
- context['contributors'].append(FeedParserDict())
- self.push('contributor', 0)
-
- def _end_contributor(self):
- self.pop('contributor')
- self.incontributor = 0
-
- def _start_dc_contributor(self, attrsD):
- self.incontributor = 1
- context = self._getContext()
- context.setdefault('contributors', [])
- context['contributors'].append(FeedParserDict())
- self.push('name', 0)
-
- def _end_dc_contributor(self):
- self._end_name()
- self.incontributor = 0
-
- def _start_name(self, attrsD):
- self.push('name', 0)
- _start_itunes_name = _start_name
-
- def _end_name(self):
- value = self.pop('name')
- if self.inpublisher:
- self._save_author('name', value, 'publisher')
- elif self.inauthor:
- self._save_author('name', value)
- elif self.incontributor:
- self._save_contributor('name', value)
- elif self.intextinput:
- context = self._getContext()
- context['textinput']['name'] = value
- _end_itunes_name = _end_name
-
- def _start_width(self, attrsD):
- self.push('width', 0)
-
- def _end_width(self):
- value = self.pop('width')
- try:
- value = int(value)
- except:
- value = 0
- if self.inimage:
- context = self._getContext()
- context['image']['width'] = value
-
- def _start_height(self, attrsD):
- self.push('height', 0)
-
- def _end_height(self):
- value = self.pop('height')
- try:
- value = int(value)
- except:
- value = 0
- if self.inimage:
- context = self._getContext()
- context['image']['height'] = value
-
- def _start_url(self, attrsD):
- self.push('href', 1)
- _start_homepage = _start_url
- _start_uri = _start_url
-
- def _end_url(self):
- value = self.pop('href')
- if self.inauthor:
- self._save_author('href', value)
- elif self.incontributor:
- self._save_contributor('href', value)
- elif self.inimage:
- context = self._getContext()
- context['image']['href'] = value
- elif self.intextinput:
- context = self._getContext()
- context['textinput']['link'] = value
- _end_homepage = _end_url
- _end_uri = _end_url
-
- def _start_email(self, attrsD):
- self.push('email', 0)
- _start_itunes_email = _start_email
-
- def _end_email(self):
- value = self.pop('email')
- if self.inpublisher:
- self._save_author('email', value, 'publisher')
- elif self.inauthor:
- self._save_author('email', value)
- elif self.incontributor:
- self._save_contributor('email', value)
- _end_itunes_email = _end_email
-
- def _getContext(self):
- if self.insource:
- context = self.sourcedata
- elif self.inentry:
- context = self.entries[-1]
- else:
- context = self.feeddata
- return context
-
- def _save_author(self, key, value, prefix='author'):
- context = self._getContext()
- context.setdefault(prefix + '_detail', FeedParserDict())
- context[prefix + '_detail'][key] = value
- self._sync_author_detail()
-
- def _save_contributor(self, key, value):
- context = self._getContext()
- context.setdefault('contributors', [FeedParserDict()])
- context['contributors'][-1][key] = value
-
- def _sync_author_detail(self, key='author'):
- context = self._getContext()
- detail = context.get('%s_detail' % key)
- if detail:
- name = detail.get('name')
- email = detail.get('email')
- if name and email:
- context[key] = '%s (%s)' % (name, email)
- elif name:
- context[key] = name
- elif email:
- context[key] = email
- else:
- author = context.get(key)
- if not author: return
- emailmatch = re.search(r'''(([a-zA-Z0-9\_\-\.\+]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?))''', author)
- if not emailmatch: return
- email = emailmatch.group(0)
- # probably a better way to do the following, but it passes all the tests
- author = author.replace(email, '')
- author = author.replace('()', '')
- author = author.strip()
- if author and (author[0] == '('):
- author = author[1:]
- if author and (author[-1] == ')'):
- author = author[:-1]
- author = author.strip()
- context.setdefault('%s_detail' % key, FeedParserDict())
- context['%s_detail' % key]['name'] = author
- context['%s_detail' % key]['email'] = email
-
- def _start_subtitle(self, attrsD):
- self.pushContent('subtitle', attrsD, 'text/plain', 1)
- _start_tagline = _start_subtitle
- _start_itunes_subtitle = _start_subtitle
-
- def _end_subtitle(self):
- self.popContent('subtitle')
- _end_tagline = _end_subtitle
- _end_itunes_subtitle = _end_subtitle
-
- def _start_rights(self, attrsD):
- self.pushContent('rights', attrsD, 'text/plain', 1)
- _start_dc_rights = _start_rights
- _start_copyright = _start_rights
-
- def _end_rights(self):
- self.popContent('rights')
- _end_dc_rights = _end_rights
- _end_copyright = _end_rights
-
- def _start_item(self, attrsD):
- self.entries.append(FeedParserDict())
- self.push('item', 0)
- self.inentry = 1
- self.guidislink = 0
- id = self._getAttribute(attrsD, 'rdf:about')
- if id:
- context = self._getContext()
- context['id'] = id
- self._cdf_common(attrsD)
- _start_entry = _start_item
- _start_product = _start_item
-
- def _end_item(self):
- self.pop('item')
- self.inentry = 0
- _end_entry = _end_item
-
- def _start_dc_language(self, attrsD):
- self.push('language', 1)
- _start_language = _start_dc_language
-
- def _end_dc_language(self):
- self.lang = self.pop('language')
- _end_language = _end_dc_language
-
- def _start_dc_publisher(self, attrsD):
- self.push('publisher', 1)
- _start_webmaster = _start_dc_publisher
-
- def _end_dc_publisher(self):
- self.pop('publisher')
- self._sync_author_detail('publisher')
- _end_webmaster = _end_dc_publisher
-
- def _start_published(self, attrsD):
- self.push('published', 1)
- _start_dcterms_issued = _start_published
- _start_issued = _start_published
-
- def _end_published(self):
- value = self.pop('published')
- self._save('published_parsed', _parse_date(value))
- _end_dcterms_issued = _end_published
- _end_issued = _end_published
-
- def _start_updated(self, attrsD):
- self.push('updated', 1)
- _start_modified = _start_updated
- _start_dcterms_modified = _start_updated
- _start_pubdate = _start_updated
- _start_dc_date = _start_updated
-
- def _end_updated(self):
- value = self.pop('updated')
- parsed_value = _parse_date(value)
- self._save('updated_parsed', parsed_value)
- _end_modified = _end_updated
- _end_dcterms_modified = _end_updated
- _end_pubdate = _end_updated
- _end_dc_date = _end_updated
-
- def _start_created(self, attrsD):
- self.push('created', 1)
- _start_dcterms_created = _start_created
-
- def _end_created(self):
- value = self.pop('created')
- self._save('created_parsed', _parse_date(value))
- _end_dcterms_created = _end_created
-
- def _start_expirationdate(self, attrsD):
- self.push('expired', 1)
-
- def _end_expirationdate(self):
- self._save('expired_parsed', _parse_date(self.pop('expired')))
-
- def _start_cc_license(self, attrsD):
- self.push('license', 1)
- value = self._getAttribute(attrsD, 'rdf:resource')
- if value:
- self.elementstack[-1][2].append(value)
- self.pop('license')
-
- def _start_creativecommons_license(self, attrsD):
- self.push('license', 1)
-
- def _end_creativecommons_license(self):
- self.pop('license')
-
- def _addTag(self, term, scheme, label):
- context = self._getContext()
- tags = context.setdefault('tags', [])
- if (not term) and (not scheme) and (not label): return
- value = FeedParserDict({'term': term, 'scheme': scheme, 'label': label})
- if value not in tags:
- tags.append(FeedParserDict({'term': term, 'scheme': scheme, 'label': label}))
-
- def _start_category(self, attrsD):
- if _debug: sys.stderr.write('entering _start_category with %s\n' % repr(attrsD))
- term = attrsD.get('term')
- scheme = attrsD.get('scheme', attrsD.get('domain'))
- label = attrsD.get('label')
- self._addTag(term, scheme, label)
- self.push('category', 1)
- _start_dc_subject = _start_category
- _start_keywords = _start_category
-
- def _end_itunes_keywords(self):
- for term in self.pop('itunes_keywords').split():
- self._addTag(term, 'http://www.itunes.com/', None)
-
- def _start_itunes_category(self, attrsD):
- self._addTag(attrsD.get('text'), 'http://www.itunes.com/', None)
- self.push('category', 1)
-
- def _end_category(self):
- value = self.pop('category')
- if not value: return
- context = self._getContext()
- tags = context['tags']
- if value and len(tags) and not tags[-1]['term']:
- tags[-1]['term'] = value
- else:
- self._addTag(value, None, None)
- _end_dc_subject = _end_category
- _end_keywords = _end_category
- _end_itunes_category = _end_category
-
- def _start_cloud(self, attrsD):
- self._getContext()['cloud'] = FeedParserDict(attrsD)
-
- def _start_link(self, attrsD):
- attrsD.setdefault('rel', 'alternate')
- attrsD.setdefault('type', 'text/html')
- attrsD = self._itsAnHrefDamnIt(attrsD)
- if attrsD.has_key('href'):
- attrsD['href'] = self.resolveURI(attrsD['href'])
- expectingText = self.infeed or self.inentry or self.insource
- context = self._getContext()
- context.setdefault('links', [])
- context['links'].append(FeedParserDict(attrsD))
- if attrsD['rel'] == 'enclosure':
- self._start_enclosure(attrsD)
- if attrsD.has_key('href'):
- expectingText = 0
- if (attrsD.get('rel') == 'alternate') and (self.mapContentType(attrsD.get('type')) in self.html_types):
- context['link'] = attrsD['href']
- else:
- self.push('link', expectingText)
- _start_producturl = _start_link
-
- def _end_link(self):
- value = self.pop('link')
- context = self._getContext()
- if self.intextinput:
- context['textinput']['link'] = value
- if self.inimage:
- context['image']['link'] = value
- _end_producturl = _end_link
-
- def _start_guid(self, attrsD):
- self.guidislink = (attrsD.get('ispermalink', 'true') == 'true')
- self.push('id', 1)
-
- def _end_guid(self):
- value = self.pop('id')
- self._save('guidislink', self.guidislink and not self._getContext().has_key('link'))
- if self.guidislink:
- # guid acts as link, but only if 'ispermalink' is not present or is 'true',
- # and only if the item doesn't already have a link element
- self._save('link', value)
-
- def _start_title(self, attrsD):
- self.pushContent('title', attrsD, 'text/plain', self.infeed or self.inentry or self.insource)
- def _start_title_low_pri(self, attrsD):
- if not self._getContext().has_key('title'):
- self._start_title(attrsD)
- _start_dc_title = _start_title_low_pri
- _start_media_title = _start_title_low_pri
-
- def _end_title(self):
- value = self.popContent('title')
- context = self._getContext()
- if self.intextinput:
- context['textinput']['title'] = value
- elif self.inimage:
- context['image']['title'] = value
- def _end_title_low_pri(self):
- if not self._getContext().has_key('title'):
- self._end_title()
- _end_dc_title = _end_title_low_pri
- _end_media_title = _end_title_low_pri
-
- def _start_description(self, attrsD):
- context = self._getContext()
- if context.has_key('summary'):
- self._summaryKey = 'content'
- self._start_content(attrsD)
- else:
- self.pushContent('description', attrsD, 'text/html', self.infeed or self.inentry or self.insource)
-
- def _start_abstract(self, attrsD):
- self.pushContent('description', attrsD, 'text/plain', self.infeed or self.inentry or self.insource)
-
- def _end_description(self):
- if self._summaryKey == 'content':
- self._end_content()
- else:
- value = self.popContent('description')
- context = self._getContext()
- if self.intextinput:
- context['textinput']['description'] = value
- elif self.inimage:
- context['image']['description'] = value
- self._summaryKey = None
- _end_abstract = _end_description
-
- def _start_info(self, attrsD):
- self.pushContent('info', attrsD, 'text/plain', 1)
- _start_feedburner_browserfriendly = _start_info
-
- def _end_info(self):
- self.popContent('info')
- _end_feedburner_browserfriendly = _end_info
-
- def _start_generator(self, attrsD):
- if attrsD:
- attrsD = self._itsAnHrefDamnIt(attrsD)
- if attrsD.has_key('href'):
- attrsD['href'] = self.resolveURI(attrsD['href'])
- self._getContext()['generator_detail'] = FeedParserDict(attrsD)
- self.push('generator', 1)
-
- def _end_generator(self):
- value = self.pop('generator')
- context = self._getContext()
- if context.has_key('generator_detail'):
- context['generator_detail']['name'] = value
-
- def _start_admin_generatoragent(self, attrsD):
- self.push('generator', 1)
- value = self._getAttribute(attrsD, 'rdf:resource')
- if value:
- self.elementstack[-1][2].append(value)
- self.pop('generator')
- self._getContext()['generator_detail'] = FeedParserDict({'href': value})
-
- def _start_admin_errorreportsto(self, attrsD):
- self.push('errorreportsto', 1)
- value = self._getAttribute(attrsD, 'rdf:resource')
- if value:
- self.elementstack[-1][2].append(value)
- self.pop('errorreportsto')
-
- def _start_summary(self, attrsD):
- context = self._getContext()
- if context.has_key('summary'):
- self._summaryKey = 'content'
- self._start_content(attrsD)
- else:
- self._summaryKey = 'summary'
- self.pushContent(self._summaryKey, attrsD, 'text/plain', 1)
- _start_itunes_summary = _start_summary
-
- def _end_summary(self):
- if self._summaryKey == 'content':
- self._end_content()
- else:
- self.popContent(self._summaryKey or 'summary')
- self._summaryKey = None
- _end_itunes_summary = _end_summary
-
- def _start_enclosure(self, attrsD):
- attrsD = self._itsAnHrefDamnIt(attrsD)
- self._getContext().setdefault('enclosures', []).append(FeedParserDict(attrsD))
- href = attrsD.get('href')
- if href:
- context = self._getContext()
- if not context.get('id'):
- context['id'] = href
-
- def _start_source(self, attrsD):
- self.insource = 1
-
- def _end_source(self):
- self.insource = 0
- self._getContext()['source'] = copy.deepcopy(self.sourcedata)
- self.sourcedata.clear()
-
- def _start_content(self, attrsD):
- self.pushContent('content', attrsD, 'text/plain', 1)
- src = attrsD.get('src')
- if src:
- self.contentparams['src'] = src
- self.push('content', 1)
-
- def _start_prodlink(self, attrsD):
- self.pushContent('content', attrsD, 'text/html', 1)
-
- def _start_body(self, attrsD):
- self.pushContent('content', attrsD, 'application/xhtml+xml', 1)
- _start_xhtml_body = _start_body
-
- def _start_content_encoded(self, attrsD):
- self.pushContent('content', attrsD, 'text/html', 1)
- _start_fullitem = _start_content_encoded
-
- def _end_content(self):
- copyToDescription = self.mapContentType(self.contentparams.get('type')) in (['text/plain'] + self.html_types)
- value = self.popContent('content')
- if copyToDescription:
- self._save('description', value)
- _end_body = _end_content
- _end_xhtml_body = _end_content
- _end_content_encoded = _end_content
- _end_fullitem = _end_content
- _end_prodlink = _end_content
-
- def _start_itunes_image(self, attrsD):
- self.push('itunes_image', 0)
- self._getContext()['image'] = FeedParserDict({'href': attrsD.get('href')})
- _start_itunes_link = _start_itunes_image
-
- def _end_itunes_block(self):
- value = self.pop('itunes_block', 0)
- self._getContext()['itunes_block'] = (value == 'yes') and 1 or 0
-
- def _end_itunes_explicit(self):
- value = self.pop('itunes_explicit', 0)
- self._getContext()['itunes_explicit'] = (value == 'yes') and 1 or 0
-
-if _XML_AVAILABLE:
- class _StrictFeedParser(_FeedParserMixin, xml.sax.handler.ContentHandler):
- def __init__(self, baseuri, baselang, encoding):
- if _debug: sys.stderr.write('trying StrictFeedParser\n')
- xml.sax.handler.ContentHandler.__init__(self)
- _FeedParserMixin.__init__(self, baseuri, baselang, encoding)
- self.bozo = 0
- self.exc = None
-
- def startPrefixMapping(self, prefix, uri):
- self.trackNamespace(prefix, uri)
-
- def startElementNS(self, name, qname, attrs):
- namespace, localname = name
- lowernamespace = str(namespace or '').lower()
- if lowernamespace.find('backend.userland.com/rss') <> -1:
- # match any backend.userland.com namespace
- namespace = 'http://backend.userland.com/rss'
- lowernamespace = namespace
- if qname and qname.find(':') > 0:
- givenprefix = qname.split(':')[0]
- else:
- givenprefix = None
- prefix = self._matchnamespaces.get(lowernamespace, givenprefix)
- if givenprefix and (prefix == None or (prefix == '' and lowernamespace == '')) and not self.namespacesInUse.has_key(givenprefix):
- raise UndeclaredNamespace, "'%s' is not associated with a namespace" % givenprefix
- if prefix:
- localname = prefix + ':' + localname
- localname = str(localname).lower()
- if _debug: sys.stderr.write('startElementNS: qname = %s, namespace = %s, givenprefix = %s, prefix = %s, attrs = %s, localname = %s\n' % (qname, namespace, givenprefix, prefix, attrs.items(), localname))
-
- # qname implementation is horribly broken in Python 2.1 (it
- # doesn't report any), and slightly broken in Python 2.2 (it
- # doesn't report the xml: namespace). So we match up namespaces
- # with a known list first, and then possibly override them with
- # the qnames the SAX parser gives us (if indeed it gives us any
- # at all). Thanks to MatejC for helping me test this and
- # tirelessly telling me that it didn't work yet.
- attrsD = {}
- for (namespace, attrlocalname), attrvalue in attrs._attrs.items():
- lowernamespace = (namespace or '').lower()
- prefix = self._matchnamespaces.get(lowernamespace, '')
- if prefix:
- attrlocalname = prefix + ':' + attrlocalname
- attrsD[str(attrlocalname).lower()] = attrvalue
- for qname in attrs.getQNames():
- attrsD[str(qname).lower()] = attrs.getValueByQName(qname)
- self.unknown_starttag(localname, attrsD.items())
-
- def characters(self, text):
- self.handle_data(text)
-
- def endElementNS(self, name, qname):
- namespace, localname = name
- lowernamespace = str(namespace or '').lower()
- if qname and qname.find(':') > 0:
- givenprefix = qname.split(':')[0]
- else:
- givenprefix = ''
- prefix = self._matchnamespaces.get(lowernamespace, givenprefix)
- if prefix:
- localname = prefix + ':' + localname
- localname = str(localname).lower()
- self.unknown_endtag(localname)
-
- def error(self, exc):
- self.bozo = 1
- self.exc = exc
-
- def fatalError(self, exc):
- self.error(exc)
- raise exc
-
-class _BaseHTMLProcessor(sgmllib.SGMLParser):
- elements_no_end_tag = ['area', 'base', 'basefont', 'br', 'col', 'frame', 'hr',
- 'img', 'input', 'isindex', 'link', 'meta', 'param']
-
- def __init__(self, encoding):
- self.encoding = encoding
- if _debug: sys.stderr.write('entering BaseHTMLProcessor, encoding=%s\n' % self.encoding)
- sgmllib.SGMLParser.__init__(self)
-
- def reset(self):
- self.pieces = []
- sgmllib.SGMLParser.reset(self)
-
- def _shorttag_replace(self, match):
- tag = match.group(1)
- if tag in self.elements_no_end_tag:
- return '<' + tag + ' />'
- else:
- return '<' + tag + '>' + tag + '>'
-
- def feed(self, data):
- data = re.compile(r'', self._shorttag_replace, data) # bug [ 1399464 ] Bad regexp for _shorttag_replace
- data = re.sub(r'<([^<\s]+?)\s*/>', self._shorttag_replace, data)
- data = data.replace(''', "'")
- data = data.replace('"', '"')
- if self.encoding and type(data) == type(u''):
- data = data.encode(self.encoding)
- sgmllib.SGMLParser.feed(self, data)
- sgmllib.SGMLParser.close(self)
-
- def normalize_attrs(self, attrs):
- # utility method to be called by descendants
- attrs = [(k.lower(), v) for k, v in attrs]
- attrs = [(k, k in ('rel', 'type') and v.lower() or v) for k, v in attrs]
- return attrs
-
- def unknown_starttag(self, tag, attrs):
- # called for each start tag
- # attrs is a list of (attr, value) tuples
- # e.g. for
, tag='pre', attrs=[('class', 'screen')]
- if _debug: sys.stderr.write('_BaseHTMLProcessor, unknown_starttag, tag=%s\n' % tag)
- uattrs = []
- # thanks to Kevin Marks for this breathtaking hack to deal with (valid) high-bit attribute values in UTF-8 feeds
- for key, value in attrs:
- if type(value) != type(u''):
- value = unicode(value, self.encoding)
- uattrs.append((unicode(key, self.encoding), value))
- strattrs = u''.join([u' %s="%s"' % (key, value) for key, value in uattrs]).encode(self.encoding)
- if tag in self.elements_no_end_tag:
- self.pieces.append('<%(tag)s%(strattrs)s />' % locals())
- else:
- self.pieces.append('<%(tag)s%(strattrs)s>' % locals())
-
- def unknown_endtag(self, tag):
- # called for each end tag, e.g. for
- HTMLTMPL WARNING:
- Cannot include template: %s
-
-
- """ % filename
- self.DEB("CANNOT INCLUDE WARNING")
-
- elif token == "." % token
-
- elif DISABLE_OUTPUT not in output_control:
- # Raw textual template data.
- # If output of current block is not disabled, then
- # append template data to the output buffer.
- out += token
-
- i += 1
- # end of the big while loop
-
- # Check whether all opening statements were closed.
- if loop_name: raise TemplateError, "Missing ."
- if output_control: raise TemplateError, "Missing or "
- return out
-
- ##############################################
- # PRIVATE METHODS #
- ##############################################
-
- def DEB(self, str):
- """ Print debugging message to stderr if debugging is enabled.
- @hidden
- """
- if self._debug: print >> sys.stderr, str
-
- def find_value(self, var, loop_name, loop_pass, loop_total,
- global_override=None):
- """ Search the self._vars data structure to find variable var
- located in currently processed pass of a loop which
- is currently being processed. If the variable is an ordinary
- variable, then return it.
-
- If the variable is an identificator of a loop, then
- return the total number of times this loop will
- be executed.
-
- Return an empty string, if the variable is not
- found at all.
-
- @hidden
- """
- # Search for the requested variable in magic vars if the name
- # of the variable starts with "__" and if we are inside a loop.
- if self._magic_vars and var.startswith("__") and loop_name:
- return self.magic_var(var, loop_pass[-1], loop_total[-1])
-
- # Search for an ordinary variable or for a loop.
- # Recursively search in self._vars for the requested variable.
- scope = self._vars
- globals = []
- for i in range(len(loop_name)):
- # If global lookup is on then push the value on the stack.
- if ((self._global_vars and global_override != "0") or \
- global_override == "1") and scope.has_key(var) and \
- self.is_ordinary_var(scope[var]):
- globals.append(scope[var])
-
- # Descent deeper into the hierarchy.
- if scope.has_key(loop_name[i]) and scope[loop_name[i]]:
- scope = scope[loop_name[i]][loop_pass[i]]
- else:
- return ""
-
- if scope.has_key(var):
- # Value exists in current loop.
- if type(scope[var]) == ListType:
- # The requested value is a loop.
- # Return total number of its passes.
- return len(scope[var])
- else:
- return scope[var]
- elif globals and \
- ((self._global_vars and global_override != "0") or \
- global_override == "1"):
- # Return globally looked up value.
- return globals.pop()
- else:
- # No value found.
- if var[0].isupper():
- # This is a loop name.
- # Return zero, because the user wants to know number
- # of its passes.
- return 0
- else:
- return ""
-
- def magic_var(self, var, loop_pass, loop_total):
- """ Resolve and return value of a magic variable.
- Raise an exception if the magic variable is not recognized.
-
- @hidden
- """
- self.DEB("MAGIC: '%s', PASS: %d, TOTAL: %d"\
- % (var, loop_pass, loop_total))
- if var == "__FIRST__":
- if loop_pass == 0:
- return 1
- else:
- return 0
- elif var == "__LAST__":
- if loop_pass == loop_total - 1:
- return 1
- else:
- return 0
- elif var == "__INNER__":
- # If this is neither the first nor the last pass.
- if loop_pass != 0 and loop_pass != loop_total - 1:
- return 1
- else:
- return 0
- elif var == "__PASS__":
- # Magic variable __PASS__ counts passes from one.
- return loop_pass + 1
- elif var == "__PASSTOTAL__":
- return loop_total
- elif var == "__ODD__":
- # Internally pass numbers stored in loop_pass are counted from
- # zero. But the template language presents them counted from one.
- # Therefore we must add one to the actual loop_pass value to get
- # the value we present to the user.
- if (loop_pass + 1) % 2 != 0:
- return 1
- else:
- return 0
- elif var.startswith("__EVERY__"):
- # Magic variable __EVERY__x is never true in first or last pass.
- if loop_pass != 0 and loop_pass != loop_total - 1:
- # Check if an integer follows the variable name.
- try:
- every = int(var[9:]) # nine is length of "__EVERY__"
- except ValueError:
- raise TemplateError, "Magic variable __EVERY__x: "\
- "Invalid pass number."
- else:
- if not every:
- raise TemplateError, "Magic variable __EVERY__x: "\
- "Pass number cannot be zero."
- elif (loop_pass + 1) % every == 0:
- self.DEB("MAGIC: EVERY: " + str(every))
- return 1
- else:
- return 0
- else:
- return 0
- else:
- raise TemplateError, "Invalid magic variable '%s'." % var
-
- def escape(self, str, override=""):
- """ Escape a string either by HTML escaping or by URL escaping.
- @hidden
- """
- ESCAPE_QUOTES = 1
- if (self._html_escape and override != "NONE" and override != "0" and \
- override != "URL") or override == "HTML" or override == "1":
- return cgi.escape(str, ESCAPE_QUOTES)
- elif override == "URL":
- return urllib.quote_plus(str)
- else:
- return str
-
- def is_ordinary_var(self, var):
- """ Return true if var is a scalar. (not a reference to loop)
- @hidden
- """
- if type(var) == StringType or type(var) == IntType or \
- type(var) == LongType or type(var) == FloatType:
- return 1
- else:
- return 0
-
-
-##############################################
-# CLASS: TemplateCompiler #
-##############################################
-
-class TemplateCompiler:
- """ Preprocess, parse, tokenize and compile the template.
-
- This class parses the template and produces a 'compiled' form
- of it. This compiled form is an instance of the Template
- class. The compiled form is used as input for the TemplateProcessor
- which uses it to actually process the template.
-
- This class should be used direcly only when you need to compile
- a template from a string. If your template is in a file, then you
- should use the TemplateManager class which provides
- a higher level interface to this class and also can save the
- compiled template to disk in a precompiled form.
- """
-
- def __init__(self, include=1, max_include=5, comments=1, gettext=0,
- debug=0):
- """ Constructor.
-
- @header __init__(include=1, max_include=5, comments=1, gettext=0,
- debug=0)
-
- @param include Enable or disable included templates.
- @param max_include Maximum depth of nested inclusions.
- @param comments Enable or disable template comments.
- @param gettext Enable or disable gettext support.
- @param debug Enable or disable debugging messages.
- """
-
- self._include = include
- self._max_include = max_include
- self._comments = comments
- self._gettext = gettext
- self._debug = debug
-
- # This is a list of filenames of all included templates.
- # It's modified by the include_templates() method.
- self._include_files = []
-
- # This is a counter of current inclusion depth. It's used to prevent
- # infinite recursive includes.
- self._include_level = 0
-
- def compile(self, file):
- """ Compile template from a file.
-
- @header compile(file)
- @return Compiled template.
- The return value is an instance of the Template
- class.
-
- @param file Filename of the template.
- See the prepare() method of the TemplateManager
- class for exaplanation of this parameter.
- """
-
- self.DEB("COMPILING FROM FILE: " + file)
- self._include_path = os.path.join(os.path.dirname(file), INCLUDE_DIR)
- tokens = self.parse(self.read(file))
- compile_params = (self._include, self._max_include, self._comments,
- self._gettext)
- return Template(__version__, file, self._include_files,
- tokens, compile_params, self._debug)
-
- def compile_string(self, data):
- """ Compile template from a string.
-
- This method compiles a template from a string. The
- template cannot include any templates.
- TMPL_INCLUDE statements are turned into warnings.
-
- @header compile_string(data)
- @return Compiled template.
- The return value is an instance of the Template
- class.
-
- @param data String containing the template data.
- """
- self.DEB("COMPILING FROM STRING")
- self._include = 0
- tokens = self.parse(data)
- compile_params = (self._include, self._max_include, self._comments,
- self._gettext)
- return Template(__version__, None, None, tokens, compile_params,
- self._debug)
-
- ##############################################
- # PRIVATE METHODS #
- ##############################################
-
- def DEB(self, str):
- """ Print debugging message to stderr if debugging is enabled.
- @hidden
- """
- if self._debug: print >> sys.stderr, str
-
- def read(self, filename):
- """ Read content of file and return it. Raise an error if a problem
- occurs.
- @hidden
- """
- self.DEB("READING: " + filename)
- try:
- f = None
- try:
- f = open(filename, "r")
- data = f.read()
- except IOError, (errno, errstr):
- raise TemplateError, "IO error while reading template '%s': "\
- "(%d) %s" % (filename, errno, errstr)
- else:
- return data
- finally:
- if f: f.close()
-
- def parse(self, template_data):
- """ Parse the template. This method is recursively called from
- within the include_templates() method.
-
- @return List of processing tokens.
- @hidden
- """
- if self._comments:
- self.DEB("PREPROCESS: COMMENTS")
- template_data = self.remove_comments(template_data)
- tokens = self.tokenize(template_data)
- if self._include:
- self.DEB("PREPROCESS: INCLUDES")
- self.include_templates(tokens)
- return tokens
-
- def remove_comments(self, template_data):
- """ Remove comments from the template data.
- @hidden
- """
- pattern = r"### .*"
- return re.sub(pattern, "", template_data)
-
- def include_templates(self, tokens):
- """ Process TMPL_INCLUDE statements. Use the include_level counter
- to prevent infinite recursion. Record paths to all included
- templates to self._include_files.
- @hidden
- """
- i = 0
- out = "" # buffer for output
- skip_params = 0
-
- # Process the list of tokens.
- while 1:
- if i == len(tokens): break
- if skip_params:
- skip_params = 0
- i += PARAMS_NUMBER
- continue
-
- token = tokens[i]
- if token == "."
- self._include_level += 1
- if self._include_level > self._max_include:
- # Do not include the template.
- # Protection against infinite recursive includes.
- skip_params = 1
- self.DEB("INCLUDE: LIMIT REACHED: " + filename)
- else:
- # Include the template.
- skip_params = 0
- include_file = os.path.join(self._include_path, filename)
- self._include_files.append(include_file)
- include_data = self.read(include_file)
- include_tokens = self.parse(include_data)
-
- # Append the tokens from the included template to actual
- # position in the tokens list, replacing the TMPL_INCLUDE
- # token and its parameters.
- tokens[i:i+PARAMS_NUMBER+1] = include_tokens
- i = i + len(include_tokens)
- self.DEB("INCLUDED: " + filename)
- continue # Do not increment 'i' below.
- i += 1
- # end of the main while loop
-
- if self._include_level > 0: self._include_level -= 1
- return out
-
- def tokenize(self, template_data):
- """ Split the template into tokens separated by template statements.
- The statements itself and associated parameters are also
- separately included in the resulting list of tokens.
- Return list of the tokens.
-
- @hidden
- """
- self.DEB("TOKENIZING TEMPLATE")
- # NOTE: The TWO double quotes in character class in the regexp below
- # are there only to prevent confusion of syntax highlighter in Emacs.
- pattern = r"""
- (?:^[ \t]+)? # eat spaces, tabs (opt.)
- (<
- (?:!--[ ])? # comment start + space (opt.)
- /?TMPL_[A-Z]+ # closing slash / (opt.) + statement
- [ a-zA-Z0-9""/.=:_\\-]* # this spans also comments ending (--)
- >)
- [%s]? # eat trailing newline (opt.)
- """ % os.linesep
- rc = re.compile(pattern, re.VERBOSE | re.MULTILINE)
- split = rc.split(template_data)
- tokens = []
- for statement in split:
- if statement.startswith(" 0 and '=' not in params[0]:
- # implicit identifier
- name = params[0]
- del params[0]
- else:
- # explicit identifier as a 'NAME' parameter
- name = self.find_param("NAME", params)
- self.DEB("TOKENIZER: NAME: " + str(name))
- return name
-
- def find_param(self, param, params):
- """ Extract value of parameter from a statement.
- @hidden
- """
- for pair in params:
- name, value = pair.split("=")
- if not name or not value:
- raise TemplateError, "Syntax error in template."
- if name == param:
- if value[0] == '"':
- # The value is in double quotes.
- ret_value = value[1:-1]
- else:
- # The value is without double quotes.
- ret_value = value
- self.DEB("TOKENIZER: PARAM: '%s' => '%s'" % (param, ret_value))
- return ret_value
- else:
- self.DEB("TOKENIZER: PARAM: '%s' => NOT DEFINED" % param)
- return None
-
-
-##############################################
-# CLASS: Template #
-##############################################
-
-class Template:
- """ This class represents a compiled template.
-
- This class provides storage and methods for the compiled template
- and associated metadata. It's serialized by pickle if we need to
- save the compiled template to disk in a precompiled form.
-
- You should never instantiate this class directly. Always use the
- TemplateManager or TemplateCompiler classes to
- create the instances of this class.
-
- The only method which you can directly use is the is_uptodate
- method.
- """
-
- def __init__(self, version, file, include_files, tokens, compile_params,
- debug=0):
- """ Constructor.
- @hidden
- """
- self._version = version
- self._file = file
- self._tokens = tokens
- self._compile_params = compile_params
- self._debug = debug
- self._mtime = None
- self._include_mtimes = {}
-
- if not file:
- self.DEB("TEMPLATE WAS COMPILED FROM A STRING")
- return
-
- # Save modifitcation time of the main template file.
- if os.path.isfile(file):
- self._mtime = os.path.getmtime(file)
- else:
- raise TemplateError, "Template: file does not exist: '%s'" % file
-
- # Save modificaton times of all included template files.
- for inc_file in include_files:
- if os.path.isfile(inc_file):
- self._include_mtimes[inc_file] = os.path.getmtime(inc_file)
- else:
- raise TemplateError, "Template: file does not exist: '%s'"\
- % inc_file
-
- self.DEB("NEW TEMPLATE CREATED")
-
- def is_uptodate(self, compile_params=None):
- """ Check whether the compiled template is uptodate.
-
- Return true if this compiled template is uptodate.
- Return false, if the template source file was changed on the
- disk since it was compiled.
- Works by comparison of modification times.
- Also takes modification times of all included templates
- into account.
-
- @header is_uptodate(compile_params=None)
- @return True if the template is uptodate, false otherwise.
-
- @param compile_params Only for internal use.
- Do not use this optional parameter. It's intended only for
- internal use by the TemplateManager.
- """
- if not self._file:
- self.DEB("TEMPLATE COMPILED FROM A STRING")
- return 0
-
- if self._version != __version__:
- self.DEB("TEMPLATE: VERSION NOT UPTODATE")
- return 0
-
- if compile_params != None and compile_params != self._compile_params:
- self.DEB("TEMPLATE: DIFFERENT COMPILATION PARAMS")
- return 0
-
- # Check modification times of the main template and all included
- # templates. If the included template no longer exists, then
- # the problem will be resolved when the template is recompiled.
-
- # Main template file.
- if not (os.path.isfile(self._file) and \
- self._mtime == os.path.getmtime(self._file)):
- self.DEB("TEMPLATE: NOT UPTODATE: " + self._file)
- return 0
-
- # Included templates.
- for inc_file in self._include_mtimes.keys():
- if not (os.path.isfile(inc_file) and \
- self._include_mtimes[inc_file] == \
- os.path.getmtime(inc_file)):
- self.DEB("TEMPLATE: NOT UPTODATE: " + inc_file)
- return 0
- else:
- self.DEB("TEMPLATE: UPTODATE")
- return 1
-
- def tokens(self):
- """ Get tokens of this template.
- @hidden
- """
- return self._tokens
-
- def file(self):
- """ Get filename of the main file of this template.
- @hidden
- """
- return self._file
-
- def debug(self, debug):
- """ Get debugging state.
- @hidden
- """
- self._debug = debug
-
- ##############################################
- # PRIVATE METHODS #
- ##############################################
-
- def __getstate__(self):
- """ Used by pickle when the class is serialized.
- Remove the 'debug' attribute before serialization.
- @hidden
- """
- dict = copy.copy(self.__dict__)
- del dict["_debug"]
- return dict
-
- def __setstate__(self, dict):
- """ Used by pickle when the class is unserialized.
- Add the 'debug' attribute.
- @hidden
- """
- dict["_debug"] = 0
- self.__dict__ = dict
-
-
- def DEB(self, str):
- """ Print debugging message to stderr.
- @hidden
- """
- if self._debug: print >> sys.stderr, str
-
-
-##############################################
-# EXCEPTIONS #
-##############################################
-
-class TemplateError(Exception):
- """ Fatal exception. Raised on runtime or template syntax errors.
-
- This exception is raised when a runtime error occurs or when a syntax
- error in the template is found. It has one parameter which always
- is a string containing a description of the error.
-
- All potential IOError exceptions are handled by the module and are
- converted to TemplateError exceptions. That means you should catch the
- TemplateError exception if there is a possibility that for example
- the template file will not be accesssible.
-
- The exception can be raised by constructors or by any method of any
- class.
-
- The instance is no longer usable when this exception is raised.
- """
-
- def __init__(self, error):
- """ Constructor.
- @hidden
- """
- Exception.__init__(self, "Htmltmpl error: " + error)
-
-
-class PrecompiledError(Exception):
- """ This exception is _PRIVATE_ and non fatal.
- @hidden
- """
-
- def __init__(self, template):
- """ Constructor.
- @hidden
- """
- Exception.__init__(self, template)
-
diff --git a/code/planet/sanitize.py b/code/planet/sanitize.py
deleted file mode 100644
index c98b14d..0000000
--- a/code/planet/sanitize.py
+++ /dev/null
@@ -1,354 +0,0 @@
-"""
-sanitize: bringing sanitiy to world of messed-up data
-"""
-
-__author__ = ["Mark Pilgrim ",
- "Aaron Swartz "]
-__contributors__ = ["Sam Ruby "]
-__license__ = "BSD"
-__version__ = "0.25"
-
-_debug = 0
-
-# If you want sanitize to automatically run HTML markup through HTML Tidy, set
-# this to 1. Requires mxTidy
-# or utidylib .
-TIDY_MARKUP = 0
-
-# List of Python interfaces for HTML Tidy, in order of preference. Only useful
-# if TIDY_MARKUP = 1
-PREFERRED_TIDY_INTERFACES = ["uTidy", "mxTidy"]
-
-import sgmllib, re
-
-# chardet library auto-detects character encodings
-# Download from http://chardet.feedparser.org/
-try:
- import chardet
- if _debug:
- import chardet.constants
- chardet.constants._debug = 1
-
- _chardet = lambda data: chardet.detect(data)['encoding']
-except:
- chardet = None
- _chardet = lambda data: None
-
-class _BaseHTMLProcessor(sgmllib.SGMLParser):
- elements_no_end_tag = ['area', 'base', 'basefont', 'br', 'col', 'frame', 'hr',
- 'img', 'input', 'isindex', 'link', 'meta', 'param']
-
- _r_barebang = re.compile(r'')
-
- def __init__(self, encoding):
- self.encoding = encoding
- if _debug: sys.stderr.write('entering BaseHTMLProcessor, encoding=%s\n' % self.encoding)
- sgmllib.SGMLParser.__init__(self)
-
- def reset(self):
- self.pieces = []
- sgmllib.SGMLParser.reset(self)
-
- def _shorttag_replace(self, match):
- tag = match.group(1)
- if tag in self.elements_no_end_tag:
- return '<' + tag + ' />'
- else:
- return '<' + tag + '>' + tag + '>'
-
- def feed(self, data):
- data = self._r_barebang.sub(r'<!\1', data)
- data = self._r_bareamp.sub("&", data)
- data = self._r_shorttag.sub(self._shorttag_replace, data)
- if self.encoding and type(data) == type(u''):
- data = data.encode(self.encoding)
- sgmllib.SGMLParser.feed(self, data)
-
- def normalize_attrs(self, attrs):
- # utility method to be called by descendants
- attrs = [(k.lower(), v) for k, v in attrs]
- attrs = [(k, k in ('rel', 'type') and v.lower() or v) for k, v in attrs]
- return attrs
-
- def unknown_starttag(self, tag, attrs):
- # called for each start tag
- # attrs is a list of (attr, value) tuples
- # e.g. for
, tag='pre', attrs=[('class', 'screen')]
- if _debug: sys.stderr.write('_BaseHTMLProcessor, unknown_starttag, tag=%s\n' % tag)
- uattrs = []
- # thanks to Kevin Marks for this breathtaking hack to deal with (valid) high-bit attribute values in UTF-8 feeds
- for key, value in attrs:
- if type(value) != type(u''):
- value = unicode(value, self.encoding)
- uattrs.append((unicode(key, self.encoding), value))
- strattrs = u''.join([u' %s="%s"' % (key, value) for key, value in uattrs]).encode(self.encoding)
- if tag in self.elements_no_end_tag:
- self.pieces.append('<%(tag)s%(strattrs)s />' % locals())
- else:
- self.pieces.append('<%(tag)s%(strattrs)s>' % locals())
-
- def unknown_endtag(self, tag):
- # called for each end tag, e.g. for
-
-### One of the two loops available is the Channels loop. This allows you
-### to easily create a list of subscriptions, which is exactly what we'll do
-### here.
-
-### Note that we can also expand variables inside HTML tags, but we need
-### to be cautious and HTML-escape any illegal characters using the form
-###
-
-
-
-### The other loop is the Items loop, which will get iterated for each
-### news item.
-
-
-
-### Visually distinguish articles from different days by checking for
-### the new_date flag. This demonstrates the ...
-### check.
-
-
-
-
-
-### Group consecutive articles by the same author together by checking
-### for the new_channel flag.
-
-
-
-
-
-
diff --git a/code/python/opml.xml.tmpl b/code/python/opml.xml.tmpl
deleted file mode 100644
index 50bbabe..0000000
--- a/code/python/opml.xml.tmpl
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- " xmlUrl=""/>
-
-
-
diff --git a/code/python/rss10.xml.tmpl b/code/python/rss10.xml.tmpl
deleted file mode 100644
index ea0c2e9..0000000
--- a/code/python/rss10.xml.tmpl
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-">
-
-
- -
-
-
-
-
- " />
-
-
-
-
-
-
-">
- :
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/code/python/rss20.xml.tmpl b/code/python/rss20.xml.tmpl
deleted file mode 100644
index 379e03f..0000000
--- a/code/python/rss20.xml.tmpl
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
- en
- -
-
-
-
- :
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/config/atom.xml.tmpl b/config/atom.xml.tmpl
deleted file mode 100644
index c444d01..0000000
--- a/config/atom.xml.tmpl
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
- "/>
- "/>
-
-
-
-
-
- xml:lang="">
- xml:lang="">
- "/>
-
-
- xml:lang="">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- "/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/config/basic.html.tmpl b/config/basic.html.tmpl
deleted file mode 100644
index 42d53d3..0000000
--- a/config/basic.html.tmpl
+++ /dev/null
@@ -1,88 +0,0 @@
-
-
-
-### Planet HTML template.
-###
-### This is intended to demonstrate and document Planet's templating
-### facilities, and at the same time provide a good base for you to
-### modify into your own design.
-###
-### The output's a bit boring though, if you're after less documentation
-### and more instant gratification, there's an example with a much
-### prettier output in the fancy-examples/ directory of the Planet source.
-
-### Lines like this are comments, and are automatically removed by the
-### templating engine before processing.
-
-
-### Planet makes a large number of variables available for your templates.
-### See INSTALL for the complete list. The raw value can be placed in your
-### output file using . We'll put the name of our
-### Planet in the page title and again in an h1.
-
-
-
-
-">
-
-
-
-
-
-### One of the two loops available is the Channels loop. This allows you
-### to easily create a list of subscriptions, which is exactly what we'll do
-### here.
-
-### Note that we can also expand variables inside HTML tags, but we need
-### to be cautious and HTML-escape any illegal characters using the form
-###
-
-
-
-### The other loop is the Items loop, which will get iterated for each
-### news item.
-
-
-
-### Visually distinguish articles from different days by checking for
-### the new_date flag. This demonstrates the ...
-### check.
-
-
-
-
-
-### Group consecutive articles by the same author together by checking
-### for the new_channel flag.
-
-
-
-
-
-
diff --git a/config/config.ini b/config/config.ini
deleted file mode 100644
index 1f2f606..0000000
--- a/config/config.ini
+++ /dev/null
@@ -1,182 +0,0 @@
-[Planet]
-date_format = %d de %B de %Y às %H:%M
-owner_name = Python Brasil
-log_level = DEBUG
-name = Planeta PythonBrasil
-encoding = utf-8
-days_per_page = 0
-owner_email = contato@python.org.br
-new_feed_items = 2
-items_per_page = 60
-fetch_interval = 5
-output_dir = /planet/dist
-new_date_format = %B %d, %Y
-link = http://planet.python.org.br/
-template_files = config/index.html.tmpl config/atom.xml.tmpl config/rss20.xml.tmpl config/rss10.xml.tmpl config/opml.xml.tmpl config/foafroll.xml.tmpl
-cache_directory = /planet/cache
-
-[http://allissonazevedo.com/tag/python/feed/]
-name = Allison Azevedo
-
-[http://aprenda-python.blogspot.com/atom.xml]
-name = Aprenda Python
-
-[http://aimotion.blogspot.com/feeds/posts/default]
-name = Artificial Intelligence in Motion
-
-[https://paulohrpinheiro.xyz/texts/python/feed.xml]
-name = Blog do PauloHRPinheiro
-
-[http://brunorocha.org/tag/pyplanet.atom]
-name = Bruno Cezar Rocha
-
-[https://christiano.me/category/python/feed/]
-name = Christiano Anderson
-
-[http://feeds.feedburner.com/dmmartins]
-name = Diego Manenti Martins
-
-[http://feeds.feedburner.com/dirceuinfopython]
-name = Dirceu Tiegs
-
-[https://dev.to/feed/ellisonleao]
-name = Ellison Leão
-
-[https://ericstk.wordpress.com/feed/]
-name = Eric Hideki
-
-[http://www.erico.com.br/blog/categories/python/RSS]
-name = Erico Andrei
-
-[http://livingintheshell.com/feeds/all.atom.xml]
-name = Fernando Rocha
-
-[http://blog.filipesaraiva.info/?feed=rss2&tag=planeta-python-brasil]
-name = Filipe Saraiva
-
-[http://blog.flavioribeiro.com/rss/]
-name = Flavio Ribeiro
-
-[http://pyinsci.blogspot.com/feeds/posts/default]
-name = Flávio Coelho
-
-[http://f.souza.cc/feeds/posts/default]
-name = Francisco Souza
-
-[https://blog.luizirber.org/atom.xml?tag=python]
-name = Gabbleblotchits
-
-[http://gilenofilho.com.br/tag/python/rss/]
-name = Gileno Filho
-
-[http://df.python.org.br/blog/feeds.atom]
-name = Grupy-DF
-
-[https://blog.labix.org/feed]
-name = Gustavo Niemeyer
-
-[https://humberto.io/pt-br/tags/python/index.xml]
-name = Humberto Rocha
-
-[http://techberto.wordpress.com/tag/python/]
-name = Inno::Blog
-
-[https://juracy.wordpress.com/tag/python/feed/atom/]
-name = Juracy Filho
-
-[http://kodumaro.blogspot.com/feeds/posts/default/-/Python]
-name = Kodumaro
-
-[https://lauro.wordpress.com/feed/]
-name = Lauro Moura
-
-[https://lameiro.wordpress.com/tag/python/feed/atom/]
-name = Leandro Lameiro
-
-[https://linil.wordpress.com/category/python/feed/atom/]
-name = Linil
-
-[http://blog.ramgarlic.com/feeds/posts/default]
-name = Luciano Ramalho
-
-[http://feeds.feedburner.com/MindBending-pt]
-name = Magnun Leno
-
-[http://setanta.wordpress.com/tag/python/feed/atom]
-name = Marcelo Lira
-
-[http://marrcandre.blogspot.com/feeds/posts/default/-/python]
-name = Marco André Lopes Mendes
-
-[https://medeubranco.wordpress.com/tag/python/feed/atom/]
-name = Marcos Daniel
-
-[http://metapython.blogspot.com/feeds/posts/default]
-name = MetaPython
-
-[http://claudiotorcato.wordpress.com/tag/python/feed/]
-name = Mosaico Livre
-
-[https://blog.nilo.pro.br/tags/python/index.xml]
-name = Nilo Ney Coutinho Menezes
-
-[http://nosleepforyou.blogspot.com/feeds/posts/default]
-name = No Sleep For You
-
-[http://pythonologia.org/feed/]
-name = Osvaldo Santana Neto
-
-[https://pugpe.wordpress.com/feed/]
-name = PUG - PE
-
-[https://powerpython.wordpress.com/feed/]
-name = PowerPython
-
-[https://programandociencia.com/tag/python/feed/]
-name = Programando Ciência
-
-[https://pythonhelp.wordpress.com/tag/python/feed/]
-name = Python Help
-
-[http://pythonnotes.blogspot.com/atom.xml]
-name = Python Notes
-
-[http://pythonclub.com.br/feeds/all.atom.xml]
-name = PythonClub
-
-[http://pythonidae.blogspot.com/atom.xml]
-name = Pythonidae
-
-[http://www.dieblinkenlights.com/search_rss?Subject%3Alist=python]
-name = Ricardo Banffy
-
-[https://rodo.lpho.de/tags/python/index.xml]
-name = Rodolpho Eckhardt
-
-[https://ramaral.wordpress.com/tag/python/feed/]
-name = Rodrigo Amaral
-
-[http://feeds.feedburner.com/NullOnError?format=xml]
-name = Rodrigo Delduca
-
-[http://python-blog.blogspot.com/atom.xml]
-name = Ruda Filgueiras
-
-[http://themindcaster.blogspot.com/feeds/posts/default]
-name = The Mind Caster
-
-[http://feeds.feedburner.com/pyavelino]
-name = Thiago Avelino
-
-[http://victorpoluceno.blogspot.com/feeds/posts/default/-/python]
-name = Victor Poluceno
-
-[https://www.vinta.com.br/python/feed/]
-name = Vinta Software
-
-[http://www.sergiofasilva.com/feed/python]
-name = sergiofasilva.com
-
-[http://blog.justen.eng.br/rss.xml]
-name = Álvaro Jüsten
-
diff --git a/config/fancy.html.tmpl b/config/fancy.html.tmpl
deleted file mode 100644
index 88f3c79..0000000
--- a/config/fancy.html.tmpl
+++ /dev/null
@@ -1,126 +0,0 @@
-
-
-
-### Fancy Planet HTML template.
-###
-### When combined with the stylesheet and images in the output/ directory
-### of the Planet source, this gives you a much prettier result than the
-### default examples template and demonstrates how to use the config file
-### to support things like faces
-###
-### For documentation on the more boring template elements, see
-### examples/config.ini and examples/index.html.tmpl in the Planet source.
-
-
-
-
-">
-
-
-
-" title="" type="application/+xml">
-
-
-
-
-
-
-
-
-
-### End
-
-### End
-
-
-
-
-
-
-
-
-### End
-
-
-
-
-### Planet provides template variables for *all* configuration options for
-### the channel (and defaults), even if it doesn't know about them. We
-### exploit this here to add hackergotchi faces to our channels. Planet
-### doesn't know about the "face", "facewidth" and "faceheight" configuration
-### variables, but makes them available to us anyway.
-
-
-
-### Planet also makes available all of the information from the feed
-### that it can. Use the 'planet-cache' tool on the cache file for
-### a particular feed to find out what additional keys it supports.
-### Comment extra fields are 'author' and 'category' which we
-### demonstrate below.
-
-
In my free time, I’m working on a 2D hide-and-seek game, and as you might expect, I’ve automated the entire release pipeline for publishing on Steam. After a few attempts, when it finally worked, it felt like magic: all I had to do was create a new tag, and within minutes, the Steam client was downloading the update.
+
+
As I mentioned earlier, I have a 2D engine that, while simple, is quite comprehensive. With each new tag, I compile it in parallel for Windows, macOS, Linux, and WebAssembly. Once compilation is complete, I create a release and publish it on GitHub. Releases · willtobyte/carimbo
Note: You must have MFA enabled. After logging in, run the command below and copy the output into a GitHub Action variable named STEAM_CONFIG_VDF.
+
+
Also, create the variables STEAM_USERNAME with your username and STEAM_APP_ID with your game’s ID.
+
+
Additionally, the Action downloads the latest Carimbo release for Windows only (sorry Linux and macOS users, my time is limited). Ideally, I should pin the runtime version (the Carimbo version) using something like a runtime.txt file. Maybe I’ll implement this in the future, but for now, everything runs on the bleeding edge. :-)
After a while, I decided to resume work on my game engine.
+
+
I made a game for my son. I could have used an existing engine, but I chose to write everything from scratch because code is like pasta—it’s much better and more enjoyable when homemade.
+
+
This actually reminds me of when my father used to build my toys, from kites to wooden slides. Good memories. I have decided to do the same using what I know: programming.
+
+
You can watch it here or play it here, (runs in the browser thanks to WebAssembly), use A and D for moving around and space to shoot.
+
+
The engine was written in C++17, and the games are in Lua. The engine exposes some primitives to the Lua VM, which in turn coordinates the entire game.
We created the postal service, which is something I borrowed from languages like Erlang, where an entity can send messages to any other. As we’ll see below, the bullet sends a hit message when it collides with the octopus, and the octopus, upon receiving the hit, decrements its own health.
+
+
We also obtain the SoundManager to play sounds and spawn the main entities.
It’s not a triple-A title, but I’m very careful with resource management. A widely known technique is to create an object pool that you can reuse. In the code below, we limit it to a maximum of 3 bullets present on the screen.
+
+
The on_update method is a callback called each loop in the engine. In the case of the bullet, I check if it has approached the green octopus. If so, I send a message to it indicating that it received a hit.
On the octopus’s side, when it receives a “hit” message, the entity triggers an explosion animation, switches to attack mode, and decreases its health by 1. If it reaches 0, it changes the animation to “dead”.
And finally, the player’s on_update, where I apply the velocity if any movement keys are pressed, or set the animation to idle if none are pressed.
+
+
We also have the projectile firing, where the fire function is called, taking an instance of a bullet from the pool, placing it in a semi-random position, and applying velocity.
In the end, he requested some minor tweaks, such as the projectile being the princess sprite, the target being the player, and the entity that shoots being the green octopus. 🤷♂️
Since the first URL shorteners emerged, that’s always drawn me in for some reason, perhaps because I figured out on my own that they encoded the primary key in base36 (maybe).
+
+
So around ~2010, I decided to make my own. It was called “encurta.ae” (make it short there in Portuguese), and it was based on AppEngine. Even back then, I was quite fond of serverless and such.
+
+
It worked great locally, but when I deployed it, the datastore IDs were too large, causing the URLs to be too long.
+
+
Fast-forward to nowadays, I’ve decided to bring this project to life again. This time with a twist: when shortening the URL, the shortener would take a screenshot of the website and embed Open Graph tags in the redirect URL. This way, when shared on a social network, the link would display a title, description, and thumbnail that accurately represent what the user is about to open.
+
+
Stack
+
+
Typically, I use serverless for my personal projects because they scale to zero, thus eliminating costs when not in use. However, after working with serverless for many years, I’ve been wanting to experiment with a more grounded and down-to-earth approach to development and deployment.
+
+
+
I opted to use Go, with several goroutines performing tasks in parallel, and a purely written work queue mechanism.
+
Playwright & Chromium for automation and screenshot.
Deployment is done using Docker Compose, via SSH using the DOCKER_HOST environment variable pointing directly to a Raspberry Pi that I had bought and never used before. Now it saves me $5 per month, and I can keep a limited number of projects running on it.
+
+
And then you might ask: How do you expose it to the internet? I use Cloudflare Tunnel; the setup is simple and creates a direct connection between the Raspberry Pi and the nearest Point of Presence.
+
+
This type of hosting is extremely advantageous because the server’s IP and/or ports are never revealed; it stays behind my firewall. Everything goes through Cloudflare.
+
+
I have more than one Docker Compose file, and that’s what’s coolest. Locally, I run one, for deployment I instruct the Compose to read two others for logging and tunneling.
It’s noticeable that it took a bit more effort than if I had used a task queue. However, it turned out to be quite robust and easy to debug, and for that reason alone, it was worth it.
OrbStack as Docker desktop replacement, plus virtual machines
+
Apple ecosystem for mail, calendar, reminders, music, messaging, and more.
+
+
+
Degoogling
+
+
As can be seen, I am using Kagi, which delivers excellent results without any garbage or ads, instead of Google. I also use email with my own domain through Apple’s iCloud email system, which allows for custom domains, as well as calendar and other features, all outside Google’s domain. Unfortunately, I have to maintain my Google account to use YouTube and occasionally for sites that do not allow creating accounts with a username and password, but only through Single Sign-On (SSO).
I have been using Redis for its most basic operations such as caching with expiration and counters, as well as slightly more complex operations like zsets, zrangebyscore, and scan, or as a queue for probably more than 6 years on a daily basis.
+
+
I won’t discuss Postgres today, as we can achieve similar results. Today, I will introduce something different: the possibility of scripting Redis with Lua.
+
+
Months ago, I found myself facing the following problem: I needed to retrieve a JSON string from Redis using a specific key, merge it with the local JSON, and then submit it back to Redis as a string.
+
+
This would have been a trivial task if not for the following scenario: On the “other” side, there was a process that consumed the same JSON and then, in an atomic pipeline operation, deleted it.
+
+
Given this context, I could not perform the JSON merge on the client side, as it would not be atomic. This is where my native language, Lua (incidentally, I have a dog with the same name, and another dog named Python, yes, I’m a nerd, how did you guess?), comes into play.
+
+
With Lua, operations are atomic and accelerated with JIT, making it possible to do a myriad of things, one very classic example being a rate limiter by IP address.
+
+
Let’s see how my solution turned out:
+
+
constscript=`
+ local key = KEYS[1]
+ local input = ARGV[1]
+ local existing = redis.call('GET', key)
+
+ if existing then
+ local existingJson = cjson.decode(existing)
+ local inputJson = cjson.decode(input)
+ for _, v in ipairs(inputJson) do
+ table.insert(existingJson, v)
+ end
+ input = cjson.encode(existingJson)
+ end
+
+ redis.call('SET', key, input)
+ return input
+ `;
+
+constkey="...";
+
+awaitredis.eval(script,{
+ keys:[key],
+ arguments:[JSON.stringify(schema.parse(data))],
+});
+
+
+
And on the other side, the consumer can retrieve the JSON and delete it, atomically within a pipeline:
The year started and I joined Fueled, an agency specializing in mobile apps and web systems, as a lead engineer.
+
+
At Fueled, so far I’ve had the opportunity to work with three of their biggest clients to date. One of them is the world’s most famous lingerie brand, for which I developed a ‘social network’ that currently has about 5 million users. Then, I worked on a credit platform for elders in the United States that garnered 500,000 users at launch. Currently, I am working on a personal time management project for one of the top coaches in the field.
+
+
In parallel, I wrote five articles for Fueled’s technical blog. They are:
As if that wasn’t enough, I also developed and maintained personal projects, one of which, Carimbo, was the one I devoted most of my time to. They are:
+
+
+
Carimbo - A 2D engine written in C++ using SDL and Lua as a scripting language.
+
Carimbo Play - A web server to serve a combination of Carimbo releases and games.
+
Grammateus - An assistant that summarizes Twitch streams using OpenAI, written in Go.
+
Neo Compiler Bot - A Telegram bot capable of compiling and executing unsafe code in the cloud.
+
Freudian Slip Bot - A Telegram bot designed to detect slip-ups between the lines.
+
+
+
And many other small projects.
+
+
As a result.
+
+
+
+
Let’s see if 2024 will surpass it. Happy new year everyone!
I enjoy experimenting and writing Telegram bots for programming groups I participate in. In two groups, people frequently ask about C or C++ code, seeking help, examples, and more. Instead of using online tools like Godbolt (Compiler Explorer), they prefer sending their code directly in messages.
+
+
I had previously created such a bot using a Flask webserver, which communicated with another container through JSON-RPC. It worked well but occasionally had issues.
+
+
With the rise of LLM, I switched to using OpenAI, but many users complained about the unconventional results, which was amusing.
+
+
Recently, while working on a project named Carimbo, I started exploring WebAssembly. I realized it could be ideal for running untrusted code. Initially, I considered using isolated-vm with WebAssembly, but I was quite satisfied with Wasmtime. It offered options to limit CPU time and RAM usage, among other features.
+
+
Cgroups
+
+
Any experienced developer would likely suggest using cgroups and namespaces, which are indeed superior options. However, I prefer not to incur the costs of VMs or keep a machine running 24/7 at my home. This is primarily because Cloud Run, based on Docker, already utilizes cgroups, and to my knowledge, nested cgroups aren’t possible.
+
+
Cloud Run offers me several advantages. Without delving into too much detail, it’s a serverless platform built on top of Kubernetes, employing gVisor for an added security layer. You don’t need to handle Kubernetes directly, but the option for fine-tuning is available, which I will discuss in this article.
+
+
The Bot
+
+
Unlike in my previous work Hosting Telegram bots on Cloud Run for free, this time I will not use Flask, but instead, I will directly employ Starlette. Starlette is an asynchronous framework for Python. One of the main reasons for this migration is to utilize asyncio, which will enable handling more requests. Additionally, the python-telegram-robot library has shifted to this asynchronous model, aligning with this change.
+
+
Let’s start with the Dockerfile.
+
+
FROMpython:3.12-slim-bookwormASbase
+
+ENV PIP_DISABLE_PIP_VERSION_CHECK 1
+ENV PYTHONUNBUFFERED 1
+ENV PYTHONDONTWRITEBYTECODE 1
+ENV EMSDK=/emsdk
+ENV PATH=/emsdk:/emsdk/upstream/emscripten:/opt/venv/bin:$PATH
+
+FROMbaseASbuilder
+RUN python -m venv /opt/venv
+COPY requirements.txt .
+RUN pip install--no-cache-dir--requirement requirements.txt
+
+FROM base
+WORKDIR /opt/app
+
+# Let's steal this entire directory from the official Emscripten image.
+COPY --from=emscripten/emsdk:3.1.49 /emsdk /emsdk
+COPY --from=builder /opt/venv /opt/venv
+COPY . .
+
+RUN useradd -r user
+USER user
+
+# Instead of Gunicorn, we will use Uvicorn, which is an ASGI web server implementation for Python.
+CMD exec uvicorn main:app --host 0.0.0.0 --port $PORT --workers 8 --timeout-keep-alive 600 --timeout-graceful-shutdown 600
+
+
+
The main differences are that we steal an entire directory from the Emscripten Docker image, which saves us from having to build in the image, which is excellent. We also use Uvicorn, an ASGI web server that allows direct use of asyncio.
+
+
Now let’s see how it goes with handling the incoming requests.
+
+
defequals(left:str|None,right:str|None)->bool:
+ """
+ Compare two strings using a consistent amount of time to avoid timing attacks.
+ """
+ ifnotleftornotright:
+ returnFalse
+
+ iflen(left)!=len(right):
+ returnFalse
+
+ forc1,c2inzip(left,right):
+ ifc1!=c2:
+ returnFalse
+
+ returnTrue
+
+
+asyncdefwebhook(request:Request):
+ """
+ Entry point for requests coming from Telegram.
+ """
+ ifnotequals(
+ request.headers.get("X-Telegram-Bot-Api-Secret-Token"),
+ os.environ["SECRET"],
+ ):
+ # This section prevents false calls, only this application and Telegram know the secret.
+returnResponse(status_code=401)
+
+ payload=awaitrequest.json()
+
+ # Where the bot becomes operational, the JSON is passed to the application, which in turn processes the request.
+asyncwithapplication:
+ awaitapplication.process_update(Update.de_json(payload,application.bot))
+
+ returnResponse(status_code=200)
+
+
+app=Starlette(
+ routes=[
+ Route("/",webhook,methods=["POST"]),
+ ],
+)
+
+
+
Finally, we have the handler for messages that start with /run.
+
+
asyncdefon_run(update:Update,context:ContextTypes.DEFAULT_TYPE)->None:
+ message=update.message.reply_to_messageorupdate.message
+ ifnotmessage:
+ return
+
+ text=message.text
+ ifnottext:
+ return
+
+ text=text.lstrip("/run")
+
+ ifnottext:
+ awaitmessage.reply_text("Luke, I need the code for the Death Star's system.")
+ return
+
+ try:
+ # All the code is asynchronous, while the 'run' function is not. Therefore, we execute it in a thread.
+coro=asyncio.to_thread(run,text)
+
+ # We execute the thread as a coroutine and limit its execution to 30 seconds.
+result=awaitasyncio.wait_for(coro,timeout=30)
+
+ # Below, we prevent flooding in groups by placing very long messages into a bucket and returning the public URL.
+iflen(result)>64:
+ blob=bucket.blob(hashlib.sha256(str(text).encode()).hexdigest())
+ blob.upload_from_string(result)
+ blob.make_public()
+
+ result=blob.public_url
+
+ # Respond to the message with the result, which can be either an error or a success.
+awaitmessage.reply_text(result)
+ exceptasyncio.TimeoutError:
+ # If the code exceeds the time limit or takes too long to compile, we return some emojis.
+awaitmessage.reply_text("⏰😮💨")
+
+
+
Running Untrusted Code
+
+
Each request to execute code is compiled using em++, an ‘alias’ for clang++, targeting WebAssembly, and then executed with the WASI runtime. Each execution runs separately and in a thread-safe manner in its own directory. While I could limit CPU usage (fuel) and memory usage, as indicated by the commented lines, in my case I opted for a container with 4GB of RAM and 4 vCPUs, which is more than sufficient given that I configured Run to accept only 8 connections per instance.
+
+
defrun(source:str)->str:
+ withTemporaryDirectory()aspath:
+ os.chdir(path)
+
+ withopen("main.cpp","w+t")asmain:
+ main.write(source)
+ main.flush()
+
+ try:
+ # Compile it.
+result=subprocess.run(
+ [
+ "em++",
+ "-s",
+ "ENVIRONMENT=node",
+ "-s",
+ "WASM=1",
+ "-s",
+ "PURE_WASI=1",
+ "main.cpp",
+ ],
+ capture_output=True,
+ text=True,
+ check=True,
+ )
+
+ ifresult.returncode!=0:
+ returnresult.stderr
+
+ # Run it.
+withopen("a.out.wasm","rb")asbinary:
+ wasi=WasiConfig()
+ # Store the output in a file.
+wasi.stdout_file="a.out.stdout"
+ # Store the errors in a file.
+wasi.stderr_file="a.out.stderr"
+
+ config=Config()
+ # config.consume_fuel = True
+engine=Engine(config)
+ store=Store(engine)
+ store.set_wasi(wasi)
+ # Limits the RAM.
+# store.set_limits(16 * 1024 * 1024)
+# Limits the CPU.
+# store.set_fuel(10_000_000_000)
+
+ linker=Linker(engine)
+ linker.define_wasi()
+ module=Module(store.engine,binary.read())
+ instance=linker.instantiate(store,module)
+
+ # `_start` is the binary entrypoint, also known as main.
+start=instance.exports(store)["_start"]
+ assertisinstance(start,Func)
+
+ try:
+ start(store)
+ exceptExitTrapase:
+ # If exit code is not 0, we return the errors.
+ife.code!=0:
+ withopen("a.out.stderr","rt")asstderr:
+ returnstderr.read()
+
+ # If no errors, we return the output.
+withopen("a.out.stdout","rt")asstdout:
+ returnstdout.read()
+ exceptsubprocess.CalledProcessErrorase:
+ returne.stderr
+ exceptExceptionase:# noqa
+returnstr(e)
+
+
+
Deploy
+
+
In the past, I always used Google’s tools for deployment, but this time I tried building the Docker image in GitHub Action, which gave me two huge advantages.
+
+
+
Cache: I don’t know why, but I never got the cache to work in Cloud Build. With GitHub, it’s just a matter of using a flag.
+
Modern Docker syntax usage: In Cloud Build, it’s not possible to use heredoc, for example.
+
Speed: I know it’s possible to upgrade the Cloud Build machine, but that costs money, and on GitHub, I have a quite generous free quota.
I have less than 16 milliseconds to do more than thousands of operations.
+
+
+
Roughly 15 years ago, during an extended summer holiday, in a shared room of a student residence, I found myself endeavoring to port my 2D game engine built on top of SDL to the Google Native Client (NaCl). NaCl served as a sandboxing mechanism for Chrome, enabling the execution of native code within the browser, specifically within the Chrome browser. It’s safe to assert that NaCl can be considered the progenitor of WebAssembly.
+
+
A considerable amount of time has elapsed, and many changes have transpired. I transitioned from game development to web development, yet low-level programming has always coursed through my veins. Consequently, I resolved to revive the dream of crafting my own game engine running on the web. Today, with the advent of WebAssembly, achieving this goal is significantly more feasible and portable.
+
+
Therefore, I created Carimbo 🇧🇷, meaning “stamp” in English. The name encapsulates the notion that 2D engines are continually stamping sprites onto the screen.
This engine shares the foundational principles of Wintermoon; it is built upon the SDL library, employs Lua for scripting, and consolidates all assets within a compressed LZMA file, which is mounted when the engine initializes.
+
+
In essence, it operates as follows: video, audio, joystick, network, and file system components are initialized. Then, the bundle.zip file is mounted. When I say ‘mounted,’ I employ a library that makes read and write operations within the compressed file entirely transparent, eliminating the need for decompression, which is excellent. Subsequently, the ‘main.lua’ file is executed. This file should utilize the factory to construct the engine, which is the cornerstone. Following this, the script must spawn entities and other objects to be used within the game. Finally, with the game defined, the script should invoke the ‘run’ method of the engine, which will block and initiate the event loop.
+
+
However, this time around, the tooling for C++ has significantly improved compared to that era. Numerous package managers now exist, and compilers, along with the standard library, have matured considerably. Notably, there’s no longer a necessity to employ Boost for advanced features; many functionalities, including smart pointers and others formerly associated with Boost, are now integral parts of the standard library.
+
+
Speaking of package managers, in Carimbo, I opted for Conan, which, in my opinion, is an excellent package manager.
+
+
It was during this exploration that I discovered Conan’s support for various toolings, including emsdk—the Software Development Kit (SDK) and compiler for the Emscripten project. Emscripten is an LLVM/Clang-based compiler designed to translate C and C++ source code into WebAssembly.
+
+
With the emsdk, I could finally fulfill my long-held aspiration.
+
+
+
+
Yes, a black screen and an overwhelming sense of victory. I had successfully ported my code to run in the browser. Now, all that remained was to figure out how to load the assets (at that time). However, the event loop was running just a tad below 60 frames per second due to a bug in the counter.
+
+
And you might be wondering, ‘How do you debug this?’ Firstly, for various reasons that can burden the final binary, a lot is stripped away. Therefore, we need to recompile everything and all dependencies with sanitizers and debugging symbols. Secondly, WebAssembly should be linked with -sASSERTIONS=2, -sRUNTIME_DEBUG, and –profiling. This way, it’s possible to see the stack trace in the browser console as if by magic. Additionally, Chrome has a debugger that allows you to insert breakpoints within your source code and inspect step by step.
+
+
By the way, a binary and all its dependencies compiled with all sanitizers and debugging symbols can easily surpass 300 megabytes! So, I recommend compiling with -Os or -O1.
Programmers, like no other beings on the planet, are completely obsessed with automating things, from the simplest to the most complex, and I’m no different.
+
+
I have automated several things using GitHub Actions, and today I will show some of the things I’ve done.
+
+
README.md
+
+
In my GitHub README, I periodically fetch the RSS feed from my blog (the one you are currently reading) and populate it with the latest articles, like this:
+
+
Ah, you can use any source of RSS, like your YouTube channel!
+
+
name:Latest blog post workflow
+on:
+ schedule:
+ -cron:"0*/6***"
+ workflow_dispatch:# Run workflow manually
+
+jobs:
+ update-readme-with-blog:
+ name:Update this repo's README with latest blog posts
+ runs-on:ubuntu-latest
+ steps:
+ -name:Checkout
+ uses:actions/checkout@v2
+ -name:Pull NULL on Error posts
+ uses:gautamkrishnar/blog-post-workflow@v1
+ with:
+ comment_tag_name:BLOG
+ commit_message:Update with the latest blog posts
+ committer_username:Rodrigo Delduca
+ committer_email:46259+skhaz@users.noreply.github.com
+ max_post_count:6
+ feed_list:"https://nullonerror.org/feed"
+
+
+
Resume
+
+
My resume is public. I have an action in the repository that compiles and uploads it to a Google Cloud bucket and sets the object as public, like this:
I believe everyone enjoys starring repositories, but GitHub’s interface doesn’t help much when it comes to finding or organizing them.
+
+
To do this, I use starred, which generates a README file categorizing by language. You can check it out at the following address: https://github.com/skhaz/stars
I imagine that, just like me, you also have several websites. In my case, all of them are simple and do not generate any income. However, I want to make sure that everything is in order, so I use https://healthchecks.io in conjunction with GitHub Actions. Healthchecks.io operates passively; it does not make requests to your site. On the contrary, you must make a request to it, and then it marks it as healthy. If there are no pings for a certain amount of time (configurable), it will notify you through various means that the site or application is not functioning as it should. Think of it as a Kubernetes probe.
This one is a bit more complex, as it goes beyond just an action. I have a repository called ‘salary’, where there’s an action that runs every hour. What this action does is essentially run a Go code, and the result updates the README file. This way, I can simply access the URL and get an estimate of how much I’ll receive.
+
+
In the program, I have two goroutines running in parallel. In one of them, I query the number of hours on Toggl, multiply it by my rate, and return it through a channel. The other does the same, but it uses an API to convert dollars to Brazilian reais, and in the end, the values are summed up.
Today we will see how to send compressed CBOR with ZSTD from an ESP32 microcontroller through an MQTT topic, passing through AWS IoT Core, and finally being decoded in a TypeScript-written Lambda function.
+
+
Intro
+
+
Firstly, what is a microcontroller (MCU)? In this article, we will be using the ESP32 microcontroller from Espressif. It is a highly affordable and inexpensive microcontroller that comes with built-in WiFi and Bluetooth, making it ideal for IoT projects as we will see today. It also boasts a generous amount of flash memory and RAM, as well as a powerful dual-core 32-bit CPU.
+
+
Another tool we will be using is PlatformIO, a development framework that functions as a series of plugins and command-line tools for VSCode. With PlatformIO, we have everything we need, from project setup, board selection, serial port speed, compilation flags (yes, we’ll be compiling code in C and C++), and more. Installing it is quite simple; just visit https://platformio.org/ and follow the instructions.
+
+
Lastly, to streamline our project and infrastructure, we will be using the Serverless Framework, a framework designed for developing serverless solutions. In our case, we will be using a Lambda function to receive messages sent to the topic. The Serverless Framework is a perfect fit for this scenario and works seamlessly with AWS Amazon.
+
+
Compressed Binary Data
+
+
We could certainly use JSON. In fact, with JSON, it is possible to perform queries using the WHERE clause (Yes, IoT Core supports SQL, and we will delve into that later). However, our objective here is to save as many bytes as possible. Imagine that our application will send and receive data via a costly and unreliable telephone connection. Therefore, we need to compress the data as much as possible.
+
+
Firstly, let’s construct the raw payload in CBOR. CBOR is an interchangeable binary format specified in an RFC and supported by various programming languages (it is derived from MsgPack, in case you’ve heard of it before).
+
+
Since this part will be done on the microcontroller side, we will be using the C++ language with the ssilverman/libCBOR library. To do this, we open the platformio.ini file and add the dependency under lib_deps.
Since we are developing for a microcontroller, memory allocation is a critical concern as it can lead to fragmentation and other issues. Therefore, we will define a buffer of 256 bytes for reading and writing CBOR messages, which is more than sufficient for our current application.
Finally, and not least importantly, let’s prepare to send messages to a topic on IoT Core. The setup is a bit complex and requires attention. Firstly, we need to add the knolleary/PubSubClient library.
+
+
We will need three things from AWS IoT Core: the Certificate Authority (CA) certificate, the Thing certificate, and the private key certificate of the Thing. We will see how to create rules in IoT Core to allow things to subscribe and publish only to specific topics for security reasons. One more thing, in our project, we will hardcode these values in the flash memory of the ESP32 using the PROGMEM directive.
Now let’s proceed with a typical Arduino program structure, with the setup and loop functions. In the setup function, we will connect to the WiFi network, and then we will configure the keys in the WiFiClientSecure object so that the PubSubClient can use them to connect to IoT Core. Without these keys, the connection will be rejected by the AWS Amazon servers.
+
+
voidsetup()
+{
+ // For debugging.
+ Serial.begin(115200);
+
+ // Connect to WiFi.
+ WiFi.mode(WIFI_STA);
+ WiFi.begin(WIFI_SSID,WIFI_PASSWORD);
+
+ // Wait for the connection.
+ while(WiFi.status()!=WL_CONNECTED)
+ {
+ delay(500);
+ Serial.print(".");
+ }
+
+ // Show WiFi information.
+ Serial.println();
+ Serial.print("Connected to ");
+ Serial.println(WIFI_SSID);
+ Serial.print("IP address: ");
+ Serial.println(WiFi.localIP());
+
+ // Setup the certificates.
+ net.setCACert(AWS_CERT_CA);
+ net.setCertificate(AWS_CERT_CRT);
+ net.setPrivateKey(AWS_CERT_PRIVATE);
+
+ // Connect to IoT Core.
+ pubsub.connect(THINGNAME);
+}
+
+
+
If everything goes well, the connect method of PubSubClient will return true. We won’t perform that check; instead, we’ll attempt to publish directly and monitor the results on the IoT Core dashboard.
+
+
Now comes the interesting part. We will assemble our payload in CBOR format, compress it using ZSTD, and publish it to a topic.
+
+
voidon_sensor(constuint64_t*sensor_data,size_tsensor_data_size){
+ // Create a new instance of the CBOR writer.
+ cbor::Writercbor{bp};
+
+ // Reset BytesPrint instance.
+ bp.reset();
+
+ // Indicates that what follows is an array of a certain size.
+ cbor.beginArray(sensor_data_size);
+ for(size_ti=0;i<sensor_data_size;i++)
+ {
+ // For each item in the array, write it to CBOR.
+ cbor.writeUnsignedInt(sensor_data[i]);
+ }
+
+ // Get the final CBOR size.
+ constsize_tlenght=cbor.getWriteSize();
+
+ // Compress the CBOR buffer using ZSTD.
+ size_tcompressedSize=ZSTD_compress(zbytes,kZBytesSize,bytes,lenght,ZSTD_CLEVEL_DEFAULT);
+
+ // Publish the binary compressed data onto the topic.
+ chartopic[128];
+ sprintf(topic,"sensors/%s/v1",THINGNAME);
+ pubsub.publish(topic,zbytes,compressedSize,false);
+}
+
+
+
On the cloud side
+
+
As I mentioned before, we will be using the Serverless Framework, which follows the Infrastructure as Code (IaC) approach. So what we’ll do is create the rules for the Things, create a lambda function, and define the trigger for that lambda as IoT Core. Since the data is in binary format, we will encode it in Base64 in the IoT Core SQL.
The YAML file for serverless may seem a bit intimidating at first, but it’s simple. It essentially does two things. Firstly, it creates an IoTPolicy for the things. A Thing can only publish or subscribe to its own topic with IoTPolicies, allowing for a granular level of security.
+
+
Also, it’s worth noting that we are using ARM64 architecture. Lambdas running on the ARM architecture are not only more cost-effective but also more efficient compared to x86_64.
+
+
The second part is the definition of the lambda function and its trigger. It uses a query in IoT Core, which is the key to working with binary data in IoT Core. You need to encode the data in base64 before sending it to the lambda function; otherwise, it won’t work. This lambda function is “listening” to the sensors topic from any Thing, hence the plus symbol in the topic. By default, I prefer to version APIs, and a topic shouldn’t be an exception. Therefore, this is version 1 of my project.
+
+
Now let’s take a look at the lambda function itself. For this project, I chose to use TypeScript, but AWS Lambda and the Serverless Framework support various programming languages.
+
+
The code is quite straightforward. It receives the binary payload in the data parameter (as defined in the SQL statement above). First, it decodes the payload from base64 to binary. Then, it decompresses it using ZSTD and, finally, utilizes the CBOR library to parse it into a JavaScript object, ready to be used.
+
+
import{decompressSync}from"@skhaz/zstd";
+import{decodeFirstSync}from"cbor";
+
+exportasyncfunctionhandler(event:{timestamp:number;topic:string;data:string}){
+ // Extract from event some variables.
+ const{timestamp,topic,data}=event;
+
+ // Decode from base64 to binary.
+ constbuffer=Buffer.from(data,"base64");
+ // Decompress using ZSTD algorithm.
+ constcbor=decompressSync(buffer);
+ // Parse the binary CBOR to JavaScript object.
+ constpayload=decodeFirstSync(cbor);
+
+ // Print the sensor array data.
+ console.log(payload);
+}
+
+
+
Conclusion
+
+
In conclusion, it is possible to save a few bytes or even more with these techniques while utilizing cloud solutions like IoT Core in your projects. It allows for efficient data handling and enables the integration of cloud services into your applications.
+
+
The compression ratio can vary dramatically, depending solely on the input. In my experiments with numerical data, it ranged from around 30% to 40%.
+<p>Confused about the distinctions between Strip Clubs and Gentlemen's Clubs in Las Vegas? In this episode, we're going to dissect what sets these venues apart and what you can anticipate from the very best of both. Be sure to listen in as we demystify the Las Vegas club scene.</p>
+<p><br></p>
+<p><a href="https://www.stripclubconcierge.com/" target="_blank" rel="noopener noreferer">https://www.stripclubconcierge.com/</a></p>
+<p><br></p>
+<p><a href="https://www.stripclubconcierge.com/gentlemens-club-vs-strip-club/" target="_blank" rel="noopener noreferer">https://www.stripclubconcierge.com/gentlemens-club-vs-strip-club/</a></p>
+<p><br></p>
+<p>Tags: Gentlemens Clubs Las Vegas, Best Gentlemens Clubs Las Vegas, Treasures Gentlemen’s Club, Crazy Horse III Gentlemen’s Club</p>
+<p>Are you mulling over hiring Las Vegas strippers for a bachelor party? In this episode, we help you weigh up the pros and cons between opting for private in-room entertainers and taking the gang to a strip club. Make sure you tune in to get all the information you need for planning the ultimate bachelor party.</p>
+<p><br></p>
+<p><a href="https://www.stripclubconcierge.com/" target="_blank" rel="noopener noreferer">https://www.stripclubconcierge.com/</a></p>
+<p><br></p>
+<p><a href="https://www.stripclubconcierge.com/las-vegas-strippers/" target="_blank" rel="noopener noreferer">https://www.stripclubconcierge.com/las-vegas-strippers/</a></p>
+<p><br></p>
+<p>Tags: Las Vegas Bachelor Party, Las Vegas Guides, Las Vegas Strippers, Outcall Strippers, Private Strippers</p>
Escrever documentação nem sempre é uma das tarefas mais prazerosas, mas ter uma boa documentação nos nossos projetos é o que realmente faz diferença. Ainda mais quando usamos ferramentas que nos auxiliam nessa escrita.
+
+
Nesse post vamos ensinar como automatizar a geração da documentação específica para plugins Neovim utilizando Github Actions
+
+
+
+
+ Entendendo o fluxo
+
+
+
Assumindo que entendemos o fluxo básico das Github Actions, temos o seguinte fluxo para a geração da doc:
+
+
+
Fazemos um push na branch principal (geralmente chamada de main)
+
Um job chamado docs é iniciado com os seguintes passos:
+
+
+
Conversão do README.md do seu projeto em um arquivo .txt no formato vimdoc
+
+
Criamos um commit e fazemos o push na mesma branch com o resultado da conversão
+
+
+
+
+
Convertendo isso para o Github Actions, temos:
+
+
+
Crie um arquivo .github/workflows/docs.yml com o seguinte conteúdo:
+
+
+
+
+
on:
+ push:
+ branches:
+ -main# aqui nossa branch principal se chama `main`
+
+jobs:
+ docs:
+ runs-on:ubuntu-latest
+ needs:test
+ steps:
+ -uses:actions/checkout@v3
+ -name:panvimdoc
+ uses:kdheepak/panvimdoc@main
+ with:
+ vimdoc:nome-do-seu-projeto# sem extensão .txt
+ version:"Neovim>=0.8.0"
+ demojify:true# coloque false caso queira manter os emojis
+ treesitter:true
+ -name:Push changes
+ uses:stefanzweifel/git-auto-commit-action@v4
+ with:
+ commit_message:"auto-generatevimdoc"
+ commit_user_name:"github-actions[bot]"
+ commit_user_email:"github-actions[bot]@users.noreply.github.com"
+ commit_author:"github-actions[bot]<github-actions[bot]@users.noreply.github.com>"
+
+
+
+
+
+
+
+
+
+ Preparando seu README
+
+
+
Para um bom resultado de uma documentação vimdoc, recomendamos que seu README tenha pelo menos:
+
+
+
Uma seção Sobre
+
+
Uma seção Instalação
+
+
Uma seção Como usar
+
+
+
+
O uso de tabelas, bullet points também é suportado e recomendado. Quanto mais detalhes conseguir colocar na documentação, melhor ficará o resultado (até emojis são suportados!)
+
+
+
+
+ Colocando tudo para funcionar
+
+
+
Antes de commitar e rodar a action pela primeira vez, crie o arquivo vazio da doc:
+
Recentemente li o texto do Maycon Alves, "3 algoritmos para você sua lógica", onde são apresentados 3 problemas para treinar a lógica e escrita de algoritmos: cálculo de fatorial, identificar se um número é primo, e calcular os valores da sequência de Fibonacci. São problemas interessantes, e após resolvê-los, pode-se fazer outras perguntas que levam a um entendimento mais profundo desses algoritmos. Eu recomendo que leiam o texto do Maycon primeiro e tentem implementar uma solução para esses problemas propostos, e com isso feito, vamos discutir um pouco sobre eles.
+
Analisando as soluções
+
No texto do Maycon, tem uma dica sobre o problema da sequência de Fibonacci, onde é dito que ele pode ser resolvido usando recursividade ou loops. Vamos analisar essas opções.
+
Uma solução recursiva pode ser implementada como a baixo. O código dessa solução é simples e se aproxima bastante da descrição matemática do problema.
Essas mesmas técnicas podem ser utilizadas para resolver o cálculo do fatorial. Onde uma implementação recursiva e outra iterativa podem ser vistas a baixo:
Com essas soluções implementadas, vem uma pergunta: Existe alguma diferença entre elas, além da diferença de como isso está expresso no código? Um primeiro teste que pode ser feito é de desempenho visando observar quanto tempo cada implementação leva para calcular a resposta. Os testes foram executados em um notebook com processador Intel Core i7-6500U CPU @ 2.50GHz, memória RAM DDR4 2133MHz, no Python 3.9.2 do Debian 11, desativamente o garbage collector do Python durante a execução das funções para ter um resultado com menos variação, apresentados como uma média de 10 execuções (código utilizado).
+
O gráfico a baixo mostra o tempo de execução das implementações que calculam os valores da sequência de Fibonacci, onde é possível observar que a implementação iterativa mantém uma linearidade do tempo conforme vai se pedindo números maiores da sequência, diferente da implementação recursiva, que a cada valor quase dobra o tempo de execução.
+
+
E a baixo pode-se observar o gráfico para as implementações que calculam o fatorial, que devido aos tempos serem mais baixos, possui uma variação um pouco maior, e é possível observar uma tendência de reta para as duas implementações, com a implementação recursiva tendo um ângulo um pouco mais íngreme, implicando em um algoritmo mais lento.
+
+
A partir desses dois gráficos algumas perguntas podem ser feitas: Por que a implementação recursiva do Fibonacci apresentou uma curva que se aproxima de uma exponencial e não de uma reta como as demais? Qual a diferença para a implementação recursiva do fatorial que explicar isso? Implementações recursivas são sempre piores que as implementações iterativas, ou existem casos em elas superem ou se equivalem as iterativas?
+
Saindo um pouco desses gráficos, outras questões podem ser levantadas, como: Existe mais algum aspecto ou característica além do tempo de execução (e facilidade de leitura do código) para a escolha entre uma implementação ou outra? Considerando que essas funções vão rodar em um servidor, existe alguma coisa que possa ser feita para melhorar a performance dessas funções, como reaproveitar resultados já calculados para se calcular novos resultados? Como isso poderia ser feito? Quais são as desvantagens dessa abordagem?
+
Olhando para o problema de verificar se um número é primo, existe o crivo de Eratóstenes, ele é uma implementação eficiente? Existem casos em que ele pode ser uma boa solução ou não? O exemplo a baixo (retirado da Wikipédia) mostra o processo para encontrar todos os números primos até 120, existe alguma forma de adaptá-lo para executar diversas vezes reaproveitando o que já foi calculado, como sempre retornar o próximo número primo?
+
+
Considerações
+
Se você nunca se deparou com perguntas desse tipo, seja bem-vindo a área de análise de algoritmos, onde após se ter uma solução, busca-se analisar e descrever o comportamento do algoritmo, e até a busca de algoritmos mais eficientes. E trazendo para o dia a dia de um desenvolvedor, essas questões podem ser a resposta do motivo do código funcionar muito bem no computador de quem desenvolveu, mas demorar muito ou apresentar problemas para rodar no servidor de produção, ou com o tempo (e crescimento do volume de dados) começar a dar problemas.
+
Nesse artigo eu apenas levantei as perguntas, deixo que cada um busque as respostas, que existem. Sintam-se livres para me procurar para discutir alguma questão ou orientações para encontrar as respostas, seja nos comentários do texto no dev.to ou no Twitter.
+
+
Esse artigo foi publicado originalmente no meu blog, passe por lá, ou siga-me no DEV para ver mais artigos que eu escrevi.
Uma breve lista dos melhores plugins pra usar agora e aumentar sua produtividade
+
+
+
Quando comecei a usar o Neovim, lá por 2017, depois de passar 6 anos usando Vim, não tinha sentido uma diferença tão grande porque primeiro a API lua era praticamente inexistente e minha configuração ainda era puramente vimscript e meus plugins também.
+
+
Mas tudo mudou quando fiz o upgrade pras versões 0.4.x e gradualmente comecei a me aventurar no mundo do Lua, no final de 2019. De lá pra ca já consegui migrar minhas configurações 100% para Lua e 99% dos plugins que uso hoje também são feitos na linguagem. Aqui vai minha lista dos 10 melhores plugins que acho indispensáveis hoje:
+ A use-package inspired plugin manager for Neovim. Uses native packages, supports Luarocks dependencies, written in Lua, allows for expressive config
+
+
+
+
+
+
NOTICE:
+
This repository is currently unmaintained. For the time being (as of August, 2023), it is recommended to use one of the following plugin managers instead:
+
+
+lazy.nvim: Most stable and maintained plugin manager for Nvim.
+
+pckr.nvim: Spiritual successor of packer.nvim. Functional but not as stable as lazy.nvim.
+
+
+
packer.nvim
+
+
+
use-package inspired plugin/package management for
+Neovim.
Começando por esse incrível gerenciador de plugins, que vai deixar sua vida bem mais fácil na hora de instalar, atualizar e configurar seus plugins, principalmente se você utiliza lua nas suas configurações. Tem suporte a instalação de pacotes do luarocks, lazy loading e mais.
-- add this to your lua/plugins.lua, lua/plugins/init.lua, or the file you keep your other plugins:
+{
+ 'numToStr/Comment.nvim',
+ opts= {
+ -- add any options here
+ }
+}
+
Plugin obrigatório para quem quer utilizar o maravilhoso cliente nativo LSP. Com esse plugin a configuração dos LSP servers das linguagens fica extremamente fácil.
mason.nvim is the next generation version of nvim-lsp-installer. It
+builds on top of the very same foundation as nvim-lsp-installer (which means it's easy to migrate), but with a
+majority of internals refactored to improve extensibility and testability.
+
More importantly, the scope of mason.nvim has also been widened to target more than just LSP servers. mason.nvim
+supports DAP servers, linters, formatters, and more. As of writing, mason.nvim provides 150+ packages for 100+
+languages. It can be thought of as a general-purpose package manager, native to Neovim, that runs everywhere Neovim runs
+(Windows, macOS, Linux, etc.).
+
Another big change with mason.nvim is that executables are now linked to a single, shared, location, allowing seamless
+access from Neovim builtins (shell, terminal, etc.) as well as other 3rd party plugins.
Plugin que fornece todo suporte para uma rápida instalação dos LSP servers com uma excelente UX. É um plugin que funciona em conjunto com o nvim-lspconfig
+ Use Neovim as a language server to inject LSP diagnostics, code actions, and more via Lua.
+
+
+
+
+
+
+
ARCHIVED
+
+
null-ls is now archived and will no longer receive updates. Please see
+this issue for
+details.
+
+
null-ls.nvim
+
+
Use Neovim as a language server to inject LSP diagnostics, code actions, and
+more via Lua.
+
+
Motivation
+
+
Neovim's LSP ecosystem is growing, and plugins like
+telescope.nvim and
+trouble.nvim make it a joy to work with
+LSP features like code actions and diagnostics.
+
Unlike the VS Code and coc.nvim ecosystems, Neovim doesn't provide a way for
+non-LSP sources to hook into its LSP client. null-ls is an attempt to bridge
+that gap and simplify the process of creating, sharing, and setting up LSP
+sources using pure Lua.
+
null-ls is also an attempt to reduce the boilerplate required to set up
+general-purpose language servers and improve performance by removing the need
+for external processes.
+
+
Status
+
+
+
null-ls is in beta status. Please see below for steps to follow if something
+doesn't work the way…
Gere diagnósticos, formate, realize ações de código e mais, utilizando esse plugin que utiliza o neovim como um servidor LSP, e faz a ponte entre ferramentas que não fazem uma conexão direta com o cliente nativo LSP. Entre os exemplos podemos citar:
+
+
+
Formatar código ao salvar o buffer com o prettier
+
Gerar relatório de erros e warnings com golangci-lint para Go
The goal of nvim-treesitter is both to provide a simple and easy way to use the interface for tree-sitter in Neovim and to provide some basic functionality such as highlighting based on it:
+
+
Traditional highlighting (left) vs Treesitter-based highlighting (right)
+More examples can be found in our gallery.
+
Warning: Treesitter and nvim-treesitter highlighting are an experimental feature of Neovim.
+Please consider the experience with this plug-in as experimental until Tree-Sitter support in Neovim is stable!
+We recommend using the nightly builds of Neovim if possible.
+You can find the current roadmap here.
+The roadmap and all features of this plugin are open to change, and any suggestion will be highly appreciated!
+
Nvim-treesitter is based on three interlocking features: language parsers, queries, and modules, where modules provide features – e.g., highlighting – based on…
Um plugin que faz interface com o excelente projeto tree-sitter e traz funcionalides como _syntax highlighting _, indentação, seleção incremental, movimentação, folding, entre outros.
O melhor script para criação de snippets do Neovim. Simples assim. Tem uma curva de aprendizado um pouco alta, mas quando se pega o jeito, você não vai querer largar nunca. Aqui um vídeo explicando mais sobre as funcionalidades dele (Em inglês)
A completion engine plugin for neovim written in Lua
+Completion sources are installed from external repositories and "sourced".
+
+
+
+
+ demo.mp4
+
+
+
+
+
+
+
+
+
+
Readme!
+
+
+
+
There is a GitHub issue that documents breaking changes for nvim-cmp. Subscribe to the issue to be notified of upcoming breaking changes.
+
This is my hobby project. You can support me via GitHub sponsors.
+
Bug reports are welcome, but don't expect a fix unless you provide minimal configuration and steps to reproduce your issue.
+
The cmp.mapping.preset.* is pre-defined configuration that aims to mimic neovim's native like behavior. It can be changed without announcement. Please manage key-mapping by yourself.
+
+
+
Concept
+
+
+
+
Full support for LSP completion related capabilities
+
Powerful customizability via Lua functions
+
Smart handling of key mappings
+
No flicker
+
+
+
Setup
+
+
+
+
Recommended Configuration
+
+
+
This example configuration uses vim-plug as the plugin manager and vim-vsnip as a snippet plugin.
Seguindo o melhor plugin para snippets, esse com certeza vai ser o melhor plugin para autocomplete que você irá ter no mercado até o momento. Seu design flexível permite incluir vários tipos diferentes de sources, desde sugestões de LSP, snippets, etc. A lista é gigantesca e você também conta com uma excelente documentação caso queira criar os seus próprios sources também.
+ A blazing fast and easy to configure neovim statusline plugin written in pure lua.
+
+
+
+
+
+
+
lualine.nvim
+
+
+
+
+
A blazing fast and easy to configure Neovim statusline written in Lua.
+
lualine.nvim requires Neovim >= 0.7.
+
For previous versions of neovim please use compatability tags for example
+compat-nvim-0.5
+
+
Contributing
+
+
+
Feel free to create an issue/PR if you want to see anything else implemented.
+If you have some question or need help with configuration, start a discussion.
+
Please read CONTRIBUTING.md before opening a PR.
+You can also help with documentation in the wiki.
+
+
+
Screenshots
+
+
+
Here is a preview of what lualine can look like.
+
+
+
+
+
+
+
+
+
Screenshots of all available themes are listed in THEMES.md
+
+
For those who want to break the norms, you can create custom looks for lualine.
+ Find, Filter, Preview, Pick. All lua, all the time.
+
+
+
+
+
+
+
telescope.nvim
+
+
+
+
Gaze deeply into unknown regions using the power of the moon.
+
+
What Is Telescope?
+
+
telescope.nvim is a highly extendable fuzzy finder over lists. Built on the
+latest awesome features from neovim core. Telescope is centered around
+modularity, allowing for easy customization.
This section should guide you to run your first builtin pickers.
+
Neovim (v0.9.0) or the
+latest neovim nightly commit is required for telescope.nvim to work.
+The neovim version also needs to be compiled with LuaJIT, we currently do not
+support Lua5.1 because of some ongoing issues.
Talvez o plugin mais essencial de todos hoje. Faça buscas por arquivo, texto e muito mais, numa velocidade absurda, com esse plugin fantástico.
+
+
+
+
+ Conclusão
+
+
+
Com o ecossistema Lua se desenvolvendo cada dia mais, muitos plugins estão surgindo todos os dias e com certeza essa lista provavelmente precisará ser atualizada em um futuro próximo.
+
+
Faltou algum plugin nessa lista? Me conta ae nos comentários!
Python Brasil 2021, a maior conferência de Python da América Latina, acontecerá entre os dias 11 e 17 de outubro, reunindo pessoas desenvolvedoras, cientistas de dados e entusiastas da tecnologia.
+
+
A Python Brasil 2021, evento promovido pela comunidade brasileira de Python, traz nesta edição uma agenda imperdível para quem quer mergulhar no universo de uma das linguagens de programação mais utilizadas na atualidade.
+
Uma das principais novidades deste ano é a trilha de atividades em espanhol, que vai trazer ao evento palestras de toda a América Latina, criando um ambiente de muita diversidade onde as comunidades Python de língua espanhola vão poder se conectar e trocar experiências.
+
Em sua 17 edição, o evento será realizado de forma online, gratuito e aberto para qualquer pessoa de 11 a 17 de outubro. É esperado um público de mais de 10 mil participantes de todas as partes do Brasil e do mundo.
+Nos sete dias de imersão, os participantes poderão contribuir para projetos de software livre, participar de treinamentos e adquirir novos conhecimentos com outras pessoas da comunidade.
+
E que tal falar sobre aquele projeto inovador, dar dicas sobre carreira ou mostrar formas de usar a tecnologia para causar impacto social, tudo isso em um ambiente inclusivo, seguro e acolhedor?! Até o dia 12 de agosto você poderá submeter sua proposta para apresentação de palestras e tutoriais na maior conferência Python da América Latina.
+
Você poderá acompanhar a Python Brasil pelo YouTube em tempo real e também participar de atividades e interagir com toda a comunidade pelo servidor do Discord criado especialmente para o evento.
A Faculdade de Computação e o Programa de Pós-Graduação em Ciência da Computação da UFPA estão desenvolvendo um projeto que pretende atingir dois objetivos: o primeiro, fazer uma melhor divulgação para o público externo à universidade do que produzimos em nossas pesquisas; o segundo, uma melhor divulgação INTERNA da mesma coisa – o que desenvolvemos em nossas pesquisas.
+
Sim, INTERNA – não bastasse de fato a comunicação deficitária e pulverizada do que fazemos para os nossos próprios alunos, a pandemia só veio a piorar esse quadro. Após mais de um ano sem contato próximo com as turmas, com aulas completamente à distância e sem maiores interações extra-classe, os alunos em geral estão perdidos sobre as possibilidades de temas para TCCs e pesquisas que eles podem realizar conosco.
+
Dessa forma as duas subunidades estão entrevistando todos os professores para que falem um pouco sobre os temas que trabalham, o histórico de pesquisa, o que vem sendo feito e, mais interessante, o que pode vir a ser feito. As entrevistas ocorrem no canal do YouTube Computação UFPA e depois são retrabalhadas para aparecerem no FacompCast.
+
Feitas as devidas introduções, nesta terça dia 22/06 às 11h eu e o amigo prof. Jefferson Morais iremos falar um pouco sobre as pesquisas em Inteligência Computacional (ou seria Artificial?) desenvolvidas por nós. Será um bom apanhado sobre os trabalhos em 4 áreas que atuamos – aprendizado de máquina, metaheurísticas, sistemas fuzzy e sistemas multiagentes -, expondo projetos atuais e novos para os interessados.
Seguindo com a série, chegou a hora de discutir sobre encapsulamento, ou seja, ocultar detalhes de implementação de uma classe do resto do código. Em algumas linguagens de programação isso é feito utilizando protected ou private, e às vezes o acesso aos atributos é feito através de funções getters e setters. Nesse texto vamos ver como o Python lida com essas questões.
+
Métodos protegidos e privados
+
Diferente de linguagens como Java e PHP que possuem palavras-chave como protected e private para impedir que outras classes acessem determinados métodos ou atributos, Python deixa tudo como público. Porém isso não significa que todas as funções de uma classe podem ser chamadas por outras, ou todos os atributos podem ser lidos e alterados sem cuidados.
+
Para que quem estiver escrevendo um código saiba quais as funções ou atributos que não deveriam ser acessados diretamente, segue-se o padrão de começá-los com _, de forma similar aos arquivos ocultos em sistemas UNIX, que começam com .. Esse padrão já foi seguido na classe AutenticavelComRegistro da postagem sobre mixins, onde a função que pega a data do sistema foi nomeada _get_data. Entretanto isso é apenas uma sugestão, nada impede dela ser chamada, como no exemplo a baixo:
Porém algumas bibliotecas também utilizam o _ para indicar outras informações como metadados do objeto, e que podem ser acessados sem muitos problemas. Assim é possível utilizar esse símbolo duas vezes (__) para indicar que realmente essa variável ou função não deveria ser acessada de fora da classe, apresentando erro de que o atributo não foi encontrado ao tentar executar a função, porém ela ainda pode ser acessada:
Os getters e setters muitas vezes são usados para impedir que determinadas variáveis sejam alteradas, ou validar o valor antes de atribuir a variável, ou ainda processar um valor a partir de outras variáveis. Porém como o Python incentiva o acesso direto as variáveis, existe a property, que ao tentar acessar uma variável ou alterar um valor, uma função é chamada. Exemplo:
Nesse código algumas variáveis são acessíveis através de properties, de forma geral, as variáveis foram definidas começando com _ e com uma property de mesmo nome (sem o _). O primeiro caso é o nome, que possui apenas o getter, sendo possível o seu acesso como obj.nome, porém ao tentar atribuir um valor, será lançado um erro (AttributeError: can't set attribute). Em relação ao sobrenome, como não é necessário nenhum tratamento especial, não foi utilizado um property, porém futuramente pode ser facilmente substituído por um sem precisar alterar os demais códigos. Porém a função nome_completo foi substituída por um property, permitindo tanto o acesso ao nome completo da pessoa, como se fosse uma variável, quanto trocar nome e sobrenome ao atribuir um novo valor para essa property. Quanto a idade utiliza o setter do property para validar o valor recebido, retornando erro para idades inválidas (negativas).
+
Vale observar também que todas as funções de getter não recebem nenhum argumento (além do self), enquanto as funções de setter recebem o valor atribuído à variável.
+
Utilizando a ABC, ainda é possível informar que alguma classe filha deverá implementar alguma property. Exemplo:
Diferente de algumas linguagens que ocultam as variáveis dos objetos, permitindo o seu acesso apenas através de funções, Python seguem no sentido contrário, acessando as funções de getter e setter como se fossem variáveis, isso permite começar com uma classe simples e ir adicionando funcionalidades conforme elas forem necessárias, sem precisar mudar o código das demais partes da aplicação, além de deixar transparente para quem desenvolve, não sendo necessário lembrar se precisa usar getteres e setteres ou não.
+
De forma geral, programação orientada a objetos consiste em seguir determinados padrões de código, e as linguagens que implementam esse paradigma oferecem facilidades para escrever código seguindo esses padrões, e às vezes até ocultando detalhes complexos de suas implementações. Nesse contexto, eu recomendo a palestra do autor do htop feita no FISL 16, onde ele comenta como usou orientação a objetos em C. E para quem ainda quiser se aprofundar no assunto de orientação a objetos no Python, recomendo os vídeos do Eduardo Mendes (também conhecido como dunossauro).
+
+
Esse artigo foi publicado originalmente no meu blog, passe por lá, ou siga-me no DEV para ver mais artigos que eu escrevi.
+Seguindo o trilha com o ORM Pony, neste terceiro texto, agora começaremos a gerar situações de erro e ver o que os objetos retornam ou que exceções são geradas.
Na discussão sobre herança e mixins foram criadas várias classes, como Autenticavel e AutenticavelComRegistro que adicionam funcionalidades a outras classes e implementavam tudo o que precisavam para seu funcionamento. Entretanto podem existir casos em que não seja possível implementar todas as funções na própria classe, deixando com que as classes que a estende implemente essas funções. Uma forma de fazer isso é través das ABC (abstract base classes, ou classes base abstratas).
+
Sem uso de classes base abstratas
+
Um exemplo de classe que não é possível implementar todas as funcionalidades foi dada no texto Encapsulamento da lógica do algoritmo, que discutia a leitura de valores do teclado até que um valor válido fosse lido (ou que repete a leitura caso um valor inválido tivesse sido informado). Nesse caso a classe ValidaInput implementava a lógica base de funcionamento, porém eram suas classes filhas (ValidaNomeInput e ValidaNotaInput) que implementavam as funções para tratar o que foi lido do teclado e verificar se é um valor válido ou não.
Entretanto, esse código permite a criação de objetos da classe ValidaInput mesmo sem ter uma implementação das funções transformar_entrada e validar_valor. E a única mensagem de erro ocorreria ao tentar executar essas funções, o que poderia estar longe do problema real, que é a criação de um objeto a partir de uma classe que não prove todas as implementações das suas funções, o que seria semelhante a uma classe abstrata em outras linguagens.
+
obj=ValidaInput()
+
+# Diversas linhas de código
+
+obj('Entrada: ')# Exceção NotImplementedError lançada
+
+
+
+
Com uso de classes base abstratas
+
Seguindo a documentação da ABC, para utilizá-las é necessário informar a metaclasse ABCMeta na criação da classe, ou simplesmente estender a classe ABC, e decorar com abstractmethod as funções que as classes que a estenderem deverão implementar. Exemplo:
Desta forma, ocorrerá um erro já ao tentar criar um objeto do tipo ValidaInput, dizendo quais são as funções que precisam ser implementadas. Porém funcionará normalmente ao criar objetos a partir das classes ValidaNomeInput e ValidaNotaInput visto que elas implementam essas funções.
+
obj=ValidaInput()# Exceção TypeError lançada
+
+nome_input=ValidaNomeInput()# Objeto criado
+nota_input=ValidaNotaInput()# Objeto criado
+
+
+
+
Como essas funções não utilizam a referência ao objeto (self), ainda é possível decorar as funções com staticmethod, como:
Isso também seria válido para funções decoradas com classmethod, que receberiam a referência a classe (cls).
+
Considerações
+
Não é necessário utilizar ABC para fazer o exemplo discutido, porém ao utilizar essa biblioteca ficou mais explícito quais as funções que precisavam ser implementados nas classes filhas, ainda mais que sem utilizar ABC a classe base poderia nem ter as funções, com:
Como Python possui duck-typing, não é necessário uma grande preocupação com os tipos, como definir e utilizar interfaces presentes em outras implementações de orientação a objetos, porém devido à herança múltipla, ABC pode ser utilizada como interface que não existe em Python, fazendo com que as classes implementem determinadas funções. Para mais a respeito desse assunto, recomendo as duas lives do dunossauro sobre ABC (1 e 2), e a apresentação do Luciano Ramalho sobre type hints.
+
Uma classe filha também não é obrigada a implementar todas as funções decoradas com abstractmethod, mas assim como a classe pai, não será possível criar objetos a partir dessa classe, apenas de uma classe filha dela que implemente as demais funções. Como se ao aplicar um abstractmethod tornasse a classe abstrata, e qualquer classe filha só deixasse de ser abstrata quando a última função decorada com abstractmethod for sobrescrita. Exemplo:
+
fromabcimportABC,abstractmethod
+
+
+classA(ABC):
+ @abstractmethod
+ deffunc1(self):
+ ...
+
+ @abstractmethod
+ deffunc2(self):
+ ...
+
+
+classB(A):
+ deffunc1(self):
+ print('1')
+
+
+classC(B):
+ deffunc2(self):
+ print('2')
+
+
+a=A()# Erro por não implementar func1 e func2
+b=B()# Erro por não implementar func2
+c=C()# Objeto criado
+
+
+
+
+
Esse artigo foi publicado originalmente no meu blog, passe por lá, ou siga-me no DEV para ver mais artigos que eu escrevi.
No texto anterior foi apresentando o conceito de herança, que herda toda a estrutura e comportamento de uma classe, podendo estendê-la com outros atributos e comportamentos. Esse texto apresentará a ideia de herança múltipla, e uma forma para se aproveitar esse recurso, através de mixins.
+
Herança múltiplas
+
Voltando ao sistema para lidar com dados das pessoas, onde algumas dessas pessoas possuem a possibilidade de acessar o sistema através de usuário e senha, também deseja-se permitir que outros sistemas autentiquem e tenham acesso os dados através de uma API. Isso pode ser feito criando uma classe para representar os sistemas que terão permissão para acessar os dados. Exemplo:
Aproveitando que Python, diferente de outras linguagens, possui herança múltipla, é possível extrair essa lógica das classes, centralizando a implementação em uma outra classe e simplesmente herdá-la. Exemplo:
A primeira coisa a ser observada são os argumentos *args e **kwargs no __init__ da classe Autenticavel, eles são usados uma vez que não se sabe todos os argumentos que o __init__ da classe que estenderá o Autenticavel espera receber, funcionando de forma dinâmica (mais sobre esse recurso pode ser visto na documentação do Python).
+
A segunda coisa a ser verificada é que para a classe PessoaAutenticavel, agora cria em seus objetos, a estrutura tanto da classe Pessoa, quanto Autenticavel. Algo similar a versão sem orientação a objetos a baixo:
Também vale observar que as classes PessoaAutenticavel e Sistema não precisam definir nenhuma função, uma vez que elas cumprem seus papéis apenas herdando outras classes, porém seria possível implementar funções específicas dessas classes, assim como sobrescrever as funções definidas por outras classes.
+
Ordem de resolução de métodos
+
Embora herança múltiplas sejam interessantes, existe um problema, se ambas as classes pai possuírem uma função com um mesmo nome, a classe filha deveria chamar qual das funções? A do primeiro pai? A do último? Para lidar com esse problema o Python usa o MRO (method resolution order, ordem de resolução do método), que consiste em uma tupla com a ordem de qual classe o Python usará para encontrar o método a ser chamado. Exemplo:
Por esse motivo que também foi possível chamar o super().__init__ dentro de Autenticavel, que devido ao MRO, o Python chama o __init__ da outra classe pai da classe que estendeu Autenticavel, em vez de precisar fazer um método __init__ em PessoaAutenticavel chamando o __init__ de todas as suas classes pais, como foi feito na versão sem orientação a objetos. E por isso a ordem Autenticavel e Pessoa na herança de PessoaAutenticavel, para fazer o MRO procurar os métodos primeiro em Autenticavel e depois em Pessoa.
+
Para tentar fugir da complexidade que pode ser herança múltipla, é possível escrever classes que tem por objetivo unicamente incluir alguma funcionalidade em outra, como o caso da classe Autenticavel, que pode ser herdada por qualquer outra classe do sistema para permitir o acesso ao sistema. Essas classes recebem o nome de mixins, e adiciona uma funcionalidade bem definida.
+
Estendendo mixins
+
Imagine se além de permitir o acesso ao sistema, também gostaríamos de registrar algumas tentativas de acesso, informando quando houve a tentativa e se o acesso foi concedido ou não. Como Autenticavel é uma classe, é possível extendê-la para implementar essa funcionalidade na função autenticar. Exemplo:
Essa implementação utiliza-se do super() para acessar a função autenticar da classe Autenticavel para não precisar reimplementar a autenticação. Porém, antes de chamá-la, manipula seus argumentos para registrar quem tentou acessar o sistema, assim como também manipula o seu retorno para registrar se o acesso foi permitido ou não.
+
Essa classe também permite analisar melhor a ordem em que as classes são consultadas quando uma função é chamada:
Que também pode ser visto na forma de um digrama de classes:
+
+
Onde é feito uma busca em profundidade, como se a função fosse chamada no primeiro pai, e só se ela não for encontrada, busca-se no segundo pai e assim por diante. Também é possível observar a classe object, que sempre será a última classe, e é a classe pai de todas as outras classes do Python quando elas não possuirem um pai declarado explicitamente.
+
Considerações
+
Herança múltipla pode dificultar bastante o entendimento do código, principalmente para encontrar onde determinada função está definida, porém pode facilitar bastante o código. Um exemplo que usa bastante herança e mixins são as views baseadas em classe do django (class-based views), porém para facilitar a visualização existe o site Classy Class-Based Views que lista todas as classes, e os mixins utilizados em cada uma, como pode ser visto em "Ancestors" como na UpdateView, que é usado para criar uma página com formulário para editar um registro já existente no banco, assim ela usa mixins para pegar um objeto do banco (SingleObjectMixin), processar formulário baseado em uma tabela do banco (ModelFormMixin) e algumas outras funcionalidades necessárias para implementar essa página.
+
+
Esse artigo foi publicado originalmente no meu blog, passe por lá, ou siga-me no DEV para ver mais artigos que eu escrevi.
Algo que ajuda no desenvolvimento é a reutilização de código. Em orientação a objetos, essa reutilização pode ocorrer através de herança, onde um objeto pode se comportar como um objeto da sua própria classe, como também da classe que herdou.
+
Adicionando funcionalidades
+
Uma das utilidades da herança é estender uma classe para adicionar funcionalidades. Pensando no contexto das postagens anteriores, poderíamos querer criar um usuário e senha para algumas pessoas poderem acessar o sistema. Isso poderia ser feito adicionando atributos usuário e senha para as pessoas, além de uma função para validar se os dados estão corretos, e assim permitir o acesso ao sistema. Porém isso não pode ser feito para todas as pessoas, e sim apenas para aqueles que possuem permissão de acesso.
+
Sem orientação a objetos
+
Voltando a solução com dicionários (sem utilizar orientação a objetos), isso consistiria em criar um dicionário com a estrutura de uma pessoa, e em seguida estender essa estrutura com os novos campos de usuário e senha nesse mesmo dicionário, algo como:
Porém nessa solução é possível que o programador esqueça de chamar as duas funções init diferentes, e como queremos que todo dicionário com a estrutura de pessoa_autenticavel contenha também a estrutura de pessoa, podemos chamar o init de pessoa dentro do init de pessoa_autenticavel:
Nesse caso foi necessário alterar o nome do argumento pessoa da função pessoa_autenticavel.init para não conflitar com o outro módulo importado com esse mesmo nome. Porém ao chamar um init dentro de outro, temos a garantia de que o dicionário será compatível tanto com a estrutura pedida para ser criada pelo programador, quanto pelas estruturas pais dela.
A principal novidade desse exemplo é que ao declarar a classe PessoaAutenticavel (filha), foi declarado a classe Pessoa (pai) entre parênteses, isso faz o interpretador Python criar uma cópia dessa classe estendendo-a com as novas funções que estamos criando. Porém pode ser um pouco redundante chamar Pessoa.__init__ dentro da função __init__ sendo que já foi declarado que ela estende Pessoa, podendo ser trocado por super(), que aponta para a classe que foi estendida. Exemplo:
Assim se evita repetir o nome da classe, e já passa automaticamente a referência para self, assim como quando usamos o açúcar sintático apresentado na primeira postagem dessa série. E esse açúcar sintática também pode ser usado para chamar tanto as funções declaradas em Pessoa quanto em PessoaAutenticavel. Exemplo:
Esse método também facilita a utilização das funções, uma vez que não é necessário lembrar em qual classe que cada função foi declarada. Na verdade, como PessoaAutenticavel estende Pessoa, seria possível executar também PessoaAutenticavel.nome_completo, porém eles apontam para a mesma função.
+
Sobrescrevendo uma função
+
A classe Pessoa possui a função nome_completo que retorna uma str contento nome e sobrenome. Porém no Japão, assim como em outros países asiáticos, o sobrenome vem primeiro, e até estão pedindo para seguir a tradição deles ao falarem os nomes de japoneses, como o caso do primeiro-ministro, mudando de Shinzo Abe para Abe Shinzo.
+
Com orientação a objetos
+
Isso também pode ser feito no sistema usando herança, porém em vez de criar uma nova função com outro nome, é possível criar uma função com o mesmo nome, sobrescrevendo a anterior, porém apenas para os objetos da classe filha. Algo semelhante ao que já foi feito com a função __init__. Exemplo:
+
classJapones(Pessoa):
+ defnome_completo(self):
+ returnf'{self.sobrenome}{self.nome}'
+
+
+p1=Pessoa('João','da Silva',20)
+p2=Japones('Shinzo','Abe',66)
+
+print(p1.nome_completo())# João da Silva
+print(p2.nome_completo())# Abe Shinzo
+
+
+
+
Essa relação de herança traz algo interessante, todo objeto da classe Japones se comporta como um objeto da classe Pessoa, porém a relação inversa não é verdade. Assim como podemos dizer que todo japonês é uma pessoa, mas nem todas as pessoas são japonesas. Ser japonês é um caso mais específico de pessoa, assim como as demais nacionalidades.
+
Sem orientação a objetos
+
Esse comportamento de sobrescrever a função nome_completo não é tão simples de replicar em uma estrutura de dicionário, porém é possível fazer. Porém como uma pessoa pode ser tanto japonês quanto não ser, não é possível saber de antemão para escrever no código pessoa.nome_completo ou japones.nome_completo, que diferente do exemplo da autenticação, agora são duas funções diferentes, isso precisa ser descoberto dinamicamente quando se precisar chamar a função.
+
Uma forma de fazer isso é guardar uma referência para a função que deve ser chamada dentro da própria estrutura. Exemplo:
importpessoa
+importjapones
+
+p1={}
+pessoa.init(p1,'João','da Silva',20)
+p2={}
+japones.init(p2,'Shinzo','Abe',66)
+
+print(p1['nome_completo'](p1))# João da Silva
+print(p2['nome_completo'](p2))# Abe Shinzo
+
+
+
+
Perceba que a forma de chamar a função foi alterada. O que acontece na prática é que toda função que pode ser sobrescrita não é chamada diretamente, e sim a partir de uma referência, e isso gera um custo computacional adicional. Como esse custo não é tão alto (muitas vezes sendo quase irrelevante), esse é o comportamento adotado em várias linguagens, porém em C++, por exemplo, existe a palavra-chave virtual para descrever quando uma função pode ser sobrescrita ou não.
+
Considerações
+
Herança é um mecanismo interessante para ser explorado com o objetivo de reaproveitar código e evitar repeti-lo. Porém isso pode vir com alguns custos, seja computacional durante sua execução, seja durante a leitura do código, sendo necessário verificar diversas classes para saber o que de fato está sendo executado, porém isso também pode ser usado para ocultar e abstrair lógicas mais complicadas, como eu já comentei em outra postagem.
+
Herança também permite trabalhar com generalização e especialização, podendo descrever o comportamento mais geral, ou mais específico. Ou simplesmente só adicionar mais funcionalidades a uma classe já existente.
+
Assim como foi utilizado o super() para chamar a função __init__ da classe pai, é possível utilizá-lo para chamar qualquer outra função. Isso permite, por exemplo, tratar os argumentos da função, aplicando modificações antes de chamar a função original, ou seu retorno, executando algum processamento em cima do retorno dela, não precisando rescrever toda a função.
+
+
Esse artigo foi publicado originalmente no meu blog, passe por lá, ou siga-me no DEV para ver mais artigos que eu escrevi.
Na postagem anterior foi apresentado o self, nessa postagem será discutido mais a respeito desse argumento, considerando opções para ele e suas aplicações.
+
Métodos estáticos
+
Nem todas as funções de uma classe precisam receber uma referência de um objeto para lê-lo ou alterá-lo, muitas vezes uma função pode fazer o seu papel apenas com os dados passados como argumento, por exemplo, receber um nome e validar se ele possui pelo menos três caracteres sem espaço. Dessa forma, essa função poderia ser colocada fora do escopo da classe, porém para facilitar sua chamada, e possíveis alterações (que será discutido em outra postagem), é possível colocar essa função dentro da classe e informar que ela não receberá o argumento self com o decorador @staticmethod:
Dessa forma, essa função pode ser chamada diretamente de um objeto pessoa, ou até mesmo diretamente da classe, sem precisar criar um objeto primeiro:
+
# Chamando diretamente da classe
+print(Pessoa.valida_nome('João'))
+
+# Chamando através de um objeto do tipo Pessoa
+p1=Pessoa('João','da Silva',20)
+print(p1.valida_nome(p1.nome))
+
+
+
+
E essa função também pode ser utilizada dendro de outras funções, como validar o nome na criação de uma pessoa, de forma que caso o nome informado seja válido, será criado um objeto do tipo Pessoa, e caso o nome seja inválido, será lançado uma exceção:
Entretanto algumas funções podem precisar de um meio termo, necessitar acessar o contexto da classe, porém sem necessitar de um objeto. Isso é feito através do decorador @classmethod, onde a função decorada com ele, em vez de receber um objeto como primeiro argumento, recebe a própria classe.
+
Para demonstrar essa funcionalidade será implementado um id auto incremental para os objetos da classe Pessoa:
Nesse código é criado uma variável total_de_pessoas dentro do escopo da classe Pessoas, e que é compartilhado tanto pela classe, como pelos objetos dessa classe, diferente de declará-la com self. dentro do __init__, onde esse valor pertenceria apenas ao objeto, e não é compartilhado com os demais objetos. Declarar variáveis dentro do contexto da classe é similar ao se declarar variáveis com static em outras linguagens, assim como o @classmethod é semelhante a declaração de funções com static.
+
As funções declaradas com @classmethod também podem ser chamadas sem a necessidade de se criar um objeto, como Pessoa.novo_id(), embora que para essa função específica isso não faça muito sentido, ou receber outros argumentos, tudo depende do que essa função fará.
+
Considerações
+
Embora possa parecer confuso identificar a diferença de uma função de um objeto (função sem decorador), função de uma classe (com decorador @classmethod) e função sem acesso a nenhum outro contexto (com decorador @staticmethod), essa diferença fica mais clara ao se analisar o primeiro argumento recebido por cada tipo de função. Podendo ser a referência a um objeto (self) e assim necessitando que um objeto seja criado anteriormente, ser uma classe (cls) e não necessitando receber um objeto, ou simplesmente não recebendo nenhum argumento especial, apenas os demais argumentos necessários para a função. Sendo diferenciados pelo uso dos decoradores.
+
Na orientação a objetos implementada pelo Python, algumas coisas podem ficar confusas quando se mistura com nomenclaturas de outras linguagens que possuem implementações diferentes. A linguagem Java, por exemplo, utiliza a palavra-chave static para definir os atributos e métodos de classe, enquanto no Python um método estático é aquele que não acessa nem um objeto, nem uma classe, devendo ser utilizado o escopo da classe e o decorador @classmethod para se criar atributos e métodos da classe.
+
+
Esse artigo foi publicado originalmente no meu blog, passe por lá, ou siga-me no DEV para ver mais artigos que eu escrevi.
Nas poucas e raríssimas lives que eu fiz na Twitch, surgiu a ideia de escrever sobre programação orientada a objetos em Python, principalmente por algumas diferenças de como ela foi implementada nessa linguagem. Aproveitando o tema, vou fazer uma série de postagens dando uma visão diferente sobre orientação a objetos. E nessa primeira postagem falarei sobre classes e objetos.
+
Usando um dicionário
+
Entretanto, antes de começar com orientação a objetos, gostaria de apresentar e discutir alguns exemplos sem utilizar esse paradigma de programação.
+
Pensando em um sistema que precise manipular dados de pessoas, é possível utilizar os dicionários do Python para agrupar os dados de uma pessoa em uma única variável, como no exemplo a baixo:
Onde os dados poderiam ser acessados através da variável e do nome do dado desejado, como:
+
print(pessoa['nome'])# Imprimindo João
+
+
+
+
Assim, todos os dados de uma pessoa ficam agrupados em uma variável, o que facilita bastante a programação, visto que não é necessário criar uma variável para cada dado, e quando se manipula os dados de diferentes pessoas fica muito mais fácil identificar de qual pessoa aquele dado se refere, bastando utilizar variáveis diferentes.
+
Função para criar o dicionário
+
Apesar de prático, é necessário replicar essa estrutura de dicionário toda vez que se desejar utilizar os dados de uma nova pessoa. Para evitar a repetição de código, a criação desse dicionário pode ser feita dentro de uma função que pode ser colocada em um módulo pessoa (arquivo, nesse caso com o nome de pessoa.py):
Desta forma, garante-se que todos os dicionários representando pessoas terão os campos desejados e devidamente preenchidos.
+
Função com o dicionário
+
Também é possível criar algumas funções para executar operações com os dados desses dicionários, como pegar o nome completo da pessoa, trocar o seu sobrenome, ou fazer aniversário (o que aumentaria a idade da pessoa em um ano):
Nesse caso, pode-se observar que todas as funções aqui implementadas seguem o padrão de receber o dicionário que representa a pessoa como primeiro argumento, podendo ter outros argumentos ou não conforme a necessidade, acessando e alterando os valores desse dicionário.
+
Versão com orientação a objetos
+
Antes de entrar na versão orientada a objetos propriamente dita dos exemplos anteriores, vou fazer uma pequena alteração para facilitar o entendimento posterior. A função nova será separada em duas partes, a primeira que criará um dicionário, e chamará uma segunda função (init), que receberá esse dicionário como primeiro argumento (seguindo o padrão das demais funções) e criará sua estrutura com os devidos valores.
A maioria das linguagens de programação que possuem o paradigma de programação orientado a objetos faz o uso de classes para definir a estrutura dos objetos. O Python também utiliza classes, que podem ser definidas com a palavra-chave class seguidas de um nome para ela. E dentro dessa estrutura, podem ser definidas funções para manipular os objetos daquela classe, que em algumas linguagens também são chamadas de métodos (funções declaradas dentro do escopo uma classe).
+
Para converter o dicionário para uma classe, o primeiro passo é implementar uma função para criar a estrutura desejada. Essa função deve possui o nome __init__, e é bastante similar a função init do código anterior:
As diferenças são que agora o primeiro parâmetro se chama self, que é um padrão utilizado no Python, e em vez de usar colchetes e aspas para acessar os dados, aqui basta utilizar o ponto e o nome do dado desejado (que aqui também pode ser chamado de atributo, visto que é uma variável do objeto). A função nova implementada anteriormente não é necessária, a própria linguagem cria um objeto e passa ele como primeiro argumento para o __init__. E assim para se criar um objeto da classe Pessoa basta chamar a classe como se fosse uma função, ignorando o argumento self e informando os demais, como se estivesse chamando a função __init__ diretamente:
+
p1=Pessoa('João','da Silva',20)
+
+
+
+
Nesse caso, como a própria classe cria um contexto diferente para as funções (escopo ou namespace), não está mais sendo utilizado arquivos diferentes, porém ainda é possível fazê-lo, sendo necessário apenas fazer o import adequado. Mas para simplificação, tanto a declaração da classe, como a criação do objeto da classe Pessoa podem ser feitas no mesmo arquivo, assim como os demais exemplos dessa postagem.
+
Outras funções
+
As demais funções feitas anteriormente para o dicionário também podem ser feitas na classe Pessoa, seguindo as mesmas diferenças já apontadas anteriormente:
Essa sintaxe é bastante semelhante a versão sem orientação a objetos implementada anteriormente. Porém quando se está utilizando objetos, é possível chamar essas funções com uma outra sintaxe, informando primeiro o objeto, seguido de ponto e o nome da função desejada, com a diferença de que não é mais necessário informar o objeto como primeiro argumento. Como a função foi chamada através de um objeto, o próprio Python se encarrega de passá-lo para o argumento self, sendo necessário informar apenas os demais argumentos:
Existem algumas diferenças entre as duas sintaxes, porém isso será tratado posteriormente. Por enquanto a segunda sintaxe pode ser vista como um açúcar sintático da primeira, ou seja, uma forma mais rápida e fácil de fazer a mesma coisa que a primeira, e por isso sendo a recomendada.
+
Considerações
+
Como visto nos exemplos, programação orientada a objetos é uma técnica para juntar variáveis em uma mesma estrutura e facilitar a escrita de funções que seguem um determinado padrão, recebendo a estrutura como argumento, porém a sintaxe mais utilizada no Python para chamar as funções de um objeto (métodos) posiciona a variável que guarda a estrutura antes do nome da função, em vez do primeiro argumento.
+
No Python, o argumento da estrutura ou objeto (self) aparece explicitamente como primeiro argumento da função, enquanto em outras linguagens essa variável pode receber outro nome (como this) e não aparece explicitamente nos argumentos da função, embora essa variável tenha que ser criada dentro do contexto da função para permitir manipular o objeto.
+
+
Esse artigo foi publicado originalmente no meu blog, passe por lá, ou siga-me no DEV para ver mais artigos que eu escrevi.
Eventualmente observo dificuldades de algumas pessoas em usar corretamente alguma função, seja porque a função deveria ser executada isoladamente, e utilizado a própria variável que foi passada como argumento posteriormente, seja porque deveria se atribuir o retorno da função a alguma variável, e utilizar essa nova variável. No Python, essa diferença pode ser observada nos métodos das listas sort e reverse para as funções sorted e reversed, que são implementadas com padrões diferentes, in place e cópia de valor respectivamente. Assim pretendo discutir esses dois padrões de funções, comentando qual a diferença e o melhor caso de aplicação de cada padrão.
+
Função de exemplo
+
Para demonstrar como esses padrões funcionam, será implementado uma função que recebe uma lista e calcula o dobro dos valores dessa lista. Exemplo:
+
entrada=[5,2,8,6,4]
+
+# Execução da função
+
+resultado=[10,4,16,12,8]
+
+
+
+
Função com in place
+
A ideia do padrão in place é alterar a própria variável recebida pela função (ou o próprio objeto, caso esteja lidando com orientação a objetos). Neste caso, bastaria calcular o dobro do valor de cada posição da lista, e sobrescrever a posição com seu resultado. Exemplo:
Com essa execução é possível observar que os valores da lista foram alterados, e que o retorno da função é nulo (None), ou seja, a função alterou a própria lista passada como argumento. Outro ponto importante a ser observado é a assinatura da função (tipo dos argumentos e do retorno da função), que recebe uma lista de inteiros e não tem retorno ou é nulo (None). Dessa forma embora seja possível chamar essa função diretamente quando está se informando os argumentos de outra função, como print(dobro_inplace(valores)), a função print receberia None e não a lista como argumento.
+
Função com cópia de valor
+
A ideia do padrão cópia de valor é criar uma cópia do valor passado como argumento e retornar essa cópia, sem alterar a variável recebida (ou criando um novo objeto, no caso de orientação a objetos). Neste caso, é necessário criar uma nova lista e adicionar nela os valores calculados. Exemplo:
Com essa execução é possível observar que a variável valores continua com os valores que tinha antes da execução da função, e a variável retorno apresenta uma lista com os dobros, ou seja, a função não altera a lista passada como argumento e retorna uma nova lista com os valores calculados. Observado a assinatura da função, ela recebe uma lista de inteiros e retorna uma lista de inteiros. Isso permite chamar essa função diretamente nos argumentos para outra função, como print(dobro_copia(valores)), nesse caso a função print receberia a lista de dobros como argumento. Porém caso o retorno da função não seja armazenado, parecerá que a função não fez nada, ou não funcionou. Então em alguns casos, quando o valor anterior não é mais necessário, pode-se reatribuir o retorno da função a própria variável passada como argumento:
+
valores=dobro_copia(valores)
+
+
+
+
Função híbrida
+
Ainda é possível mesclar os dois padrões de função, alterando o valor passado e retornando-o. Exemplo:
Nesse caso, pode-se apenas chamar a função, como também utilizá-la nos argumentos de outras funções. Porém para se ter os valores originais, deve-se fazer uma cópia manualmente antes de executar a função.
+
Exemplo na biblioteca padrão
+
Na biblioteca padrão do Python, existem os métodos sort e reverse que seguem o padrão in place, e as funções sorted e reversed que seguem o padrão cópia de valor, podendo ser utilizados para ordenar e inverter os valores de uma lista, por exemplo. Quando não é mais necessário uma cópia da lista com a ordem original, é preferível utilizar funções in place, que alteram a própria lista, e como não criam uma cópia da lista, utilizam menos memória. Exemplo:
Porém esse exemplo cria duas cópias da lista, uma em cada função. Para criar apenas uma cópia, pode-se misturar funções in place com cópia de valor. Exemplo:
Também vale observar que algumas utilizações dessas funções podem dar a impressão de que elas não funcionaram, como:
+
valores=[5,2,8,6,4]
+
+sorted(valores)
+print(valores)# Imprime a lista original, e não a ordenada
+
+print(valores.sort())# Imprime None e não a lista
+
+
+
+
Considerações
+
Nem sempre é possível utilizar o padrão desejado, strings no Python (str) são imutáveis, logo todas as funções que manipulam elas seguiram o padrão cópia de valor, e para outros tipos, pode ocorrer de só existir funções in place, sendo necessário fazer uma cópia manualmente antes de chamar a função, caso necessário. Para saber qual padrão a função implementa, é necessário consultar sua documentação, ou verificando sua assinatura, embora ainda possa existir uma dúvida entre cópia de valor e híbrida, visto que a assinatura dos dois padrões são iguais.
+
Os exemplos aqui dados são didáticos. Caso deseja-se ordenar de forma reversa, tanto o método sort, quanto a função sorted podem receber como argumento reverse=True, e assim já fazer a ordenação reversa. Assim como é possível criar uma nova lista já com os valores, sem precisar adicionar manualmente item por item, como os exemplos:
Muitas listas de exercícios de lógica de programação pedem em algum momento que um valor seja lido do teclado, e caso esse valor seja inválido, deve-se avisar, e repetir a leitura até que um valor válido seja informado. Utilizando a ideia de otimização do algoritmo passo a passo, começando com uma solução simples, pretendo estudar como reduzir a duplicação de código alterando o algoritmo, encapsulando a lógica em funções, e encapsulando em classes.
+
Exercício
+
Um exemplo de exercício que pede esse tipo de validação é a leitura de notas, que devem estar entre 0 e 10. A solução mais simples, consiste em ler um valor, e enquanto esse valor for inválido, dar o aviso e ler outro valor. Exemplo:
+
nota=float(input('Digite a nota: '))
+whilenota<0ornota>10:
+ print('Nota inválida')
+ nota=float(input('Digite a nota: '))
+
+
+
+
Esse algoritmo funciona, porém existe uma duplicação no código que faz a leitura da nota (uma antes do loop e outra dentro). Caso seja necessário uma alteração, como a mudança da nota para um valor inteiro entre 0 e 100, deve-se alterar os dois lugares, e se feito em apenas um lugar, o algoritmo poderia processar valores inválidos.
+
Alterando o algoritmo
+
Visando remover a repetição de código, é possível unificar a leitura do valor dentro do loop, uma vez que é necessário repetir essa instrução até que o valor válido seja obtido. Exemplo:
Dessa forma, não existe mais a repetição de código. A condição de parada, que antes verificava se o valor era inválido (o que pode ter uma leitura não tão intuitiva), agora verifica se é um valor válido (que é geralmente é mais fácil de ler e escrever a condição). E a ordem dos comandos dentro do loop, que agora estão em uma ordem que facilita a leitura, visto que no algoritmo anterior era necessário tem em mente o que era executado antes do loop.
+
Porém esses algoritmos validam apenas o valor lido, apresentando erro caso seja informado um valor com formato inválido, como letras em vez de números. Isso pode ser resolvido tratando as exceções lançadas. Exemplo:
Caso fosse necessário ler várias notas, com os algoritmos apresentados até então, seria necessário repetir todo esse trecho de código, ou utilizá-lo dentro de uma estrutura de repetição. Para facilitar sua reutilização, evitando a duplicação de código, é possível encapsular esse algoritmo dentro de uma função. Exemplo:
+
defnota_input(prompt):
+ whileTrue:
+ try:
+ nota=float(input(prompt))
+ if0<=nota<=10:
+ break
+ exceptValueError:
+ ...
+ print('Nota inválida!')
+ returnnota
+
+
+nota1=nota_input('Digite a primeira nota: ')
+nota2=nota_input('Digite a segunda nota: ')
+
+
+
+
Encapsulamento da lógica em classes
+
Em vez de encapsular essa lógica em uma função, é possível encapsulá-la em uma classe, o que permitiria separar cada etapa do algoritmo em métodos, assim como ter um método responsável por controlar qual etapa deveria ser chamada em qual momento. Exemplo:
Vale observar que o método __call__ permite que o objeto criado a partir dessa classe seja chamado como se fosse uma função. Nesse caso ele é o responsável por chamar cada etapa do algoritmo, como: ler_entrada que é responsável por ler o que foi digitado no teclado, transformar_entrada que é responsável por converter o texto lido para o tipo desejado (converter de str para float), e validar_nota que é responsável por dizer se o valor é válido ou não. Vale observar que ao dividir o algoritmo em métodos diferentes, seu código principal virou uma espécie de código comentado, descrevendo o que está sendo feito e onde está sendo feito.
+
Outra vantagem de encapsular a lógica em classe, em vez de uma função, é a possibilidade de generalizá-la. Se fosse necessário validar outro tipo de entrada, encapsulando em uma função, seria necessário criar outra função repetindo todo o algoritmo, alterando apenas a parte referente a transformação do valor lido, e validação, o que gera uma espécie de repetição de código. Ao encapsular em classes, é possível se aproveitar dos mecanismos de herança para evitar essa repetição. Exemplo:
Dessa forma, é possível reutilizar o código já existente para criar outras validações, sendo necessário implementar apenas como converter a str lida do teclado para o tipo desejado, e como esse valor deve ser validado. Não é necessário entender e repetir a lógica de ler o valor, validá-lo, imprimir a mensagem de erro, e repetir até que seja informado um valor válido.
+
Considerações
+
É possível encapsular a lógica de um algoritmo em funções ou em classes. Embora para fazê-lo em uma classe exija conhecimentos de programação orientada a objetos, o seu reaproveitamento é facilitado, abstraindo toda a complexidade do algoritmo, que pode ser disponibilizado através de uma biblioteca, exigindo apenas a implementações de métodos simples por quem for a utilizar.
+
Ainda poderia ser discutido outras formas de fazer essa implementação, como passar funções como parâmetro e a utilização de corrotinas no encapsulamento do algoritmo em função, assim como a utilização de classmethod, staticmethod e ABC no encapsulamento do algoritmo em classes.
+
+
Esse artigo foi publicado originalmente no meu blog, passe por lá, ou siga-me no DEV para ver mais artigos que eu escrevi.
Em algum momento, durante o seu processo de desenvolvimento com Django, pode ser que surja a necessidade de criar e restaurar o banco de dados da aplicação. Pensando nisso, resolvi fazer um pequeno tutorial, básico, de como realizar essa operação.
+
Nesse tutorial, usaremos o django-dbbackup, um pacote desenvolvido especificamente para isso.
+
Configurando nosso ambiente
+
Primeiro, partindo do início, vamos criar uma pasta para o nosso projeto e, nela, isolar o nosso ambiente de desenvolvimento usando uma virtualenv:
+
mkdir projeto_db &&cd projeto_db #criando a pasta do nosso projeto
+
+virtualenv -p python3.8 env &&source env/bin/activate #criando e ativando a nossa virtualenv
+
+
+
+
Depois disso e com o nosso ambiente já ativo, vamos realizar os seguintes procedimentos:
+
pip install -U pip #com isso, atualizamos a verão do pip instalado
+
+
+
+
Instalando as dependências
+
Agora, vamos instalar o Django e o pacote que usaremos para fazer nossos backups.
+
pip install Django==3.1.2 #instalando o Django
+
+pip install django-dbbackup #instalando o django-dbbackup
+
+
+
+
Criando e configurando projeto
+
Depois de instaladas nossas dependências, vamos criar o nosso projeto e configurar o nosso pacote nas configurações do Django.
+
django-admin startproject django_db . #dentro da nossa pasta projeto_db, criamos um projeto Django com o nome de django_db.
+
+
+
+
Depois de criado nosso projeto, vamos criar e popular o nosso banco de dados.
+
python manage.py migrate #com isso, sincronizamos o estado do banco de dados com o conjunto atual de modelos e migrações.
+
+
+
+
Criado nosso banco de dados, vamos criar um superusuário para podemos o painel admin do nosso projeto.
+
python manage.py createsuperuser
+
+
+
+
Perfeito. Já temos tudo que precisamos para executar nosso projeto. Para execução dele, é só fazermos:
+
python manage.py runserver
+
+
+
+
Você terá uma imagem assim do seu projeto:
+
+
Configurando o django-dbbackup
+
Dentro do seu projeto, vamos acessar o arquivo settings.py, como expresso abaixo:
+
django_db/
+├── settings.py
+
+
+
+
Dentro desse arquivos iremos, primeiro, adiconar o django-dbbackup às apps do projeto:
Depois de adicionado às apps, vamos dizer para o Django o que vamos salvar no backup e, depois, indicar a pasta para onde será encaminhado esse arquivo. Essa inserção deve ou pode ser feita no final do arquivo settings.py:
+
DBBACKUP_STORAGE='django.core.files.storage.FileSystemStorage'#o que salvar
+DBBACKUP_STORAGE_OPTIONS={'location':'backups/'}# onde salvar
+
+
+
+
Percebam que dissemos para o Django salvar o backup na pasta backups, mas essa pasta ainda não existe no nosso projeto. Por isso, precisamos criá-la [fora da pasta do projeto]:
+
mkdir backups
+
+
+
+
Criando e restaurando nosso backup
+
Já temos tudo pronto. Agora, vamos criar o nosso primeiro backup:
+
python manage.py dbbackup
+
+
+
+
Depois de exetudado, será criado um arquivo -- no nosso exemplo, esse arquivo terá uma extensão .dump --, salvo na pasta backups. Esse arquivo contem todo backup do nosso banco de dados.
+
Para recuperarmos nosso banco, vamos supor que migramos nosso sistema de um servidor antigo para um novo e, por algum motivo, nossa base de dados foi corrompida, inviabilizando seu uso. Ou seja, estamos com o sistema/projeto sem banco de dados -- ou seja, exlua ou mova a a sua base dados .sqlite3 para que esse exemplo seja útil --, mas temos os backups. Com isso, vamos restaurar o banco:
+
python manage.py dbrestore
+
+
+
+
Prontinho, restauramos nosso banco de dados. O interessante do django-dbbackup, dentre outras coisas, é que ele gera os backups com datas e horários específicos, facilitando o processo de recuperação das informações mais recentes.
(Ok a piada com seqtembro funciona melhor na versão em inglês, seqtember, mas simbora)
+
Por uma grande coincidência, obra do destino, ou nada disso, teremos um Setembro de 2020 repleto de eventos virtuais e gratuitos de alta qualidade sobre Qt e KDE.
+
Começando de 4 à 11 do referido mês teremos o Akademy 2020, o grande encontro mundial da comunidade KDE que esse ano, por motivos que todos sabemos, acontecerá de forma virtual. A programação do Akademy traz palestras, treinamentos, hacking sessions, discussões com foco em aplicações KDE específicas, e mais, reunindo hackers, designers, gerentes de projetos, tradutores, e colabores dos mais diversos segmentos para discutir e planejar o KDE e seus futuros passos.
+
+
E como falamos em KDE, por extensão, também falamos em Qt – afinal, grande parte das aplicações é escrita nesse framework. Portanto, mesmo que você trabalhe com Qt mas não use nada do KDE, vale a pena participar do evento – e também se perguntar “porque diabos não estou usando e desenvolvendo aplicações do KDE?”.
+
Um incentivo extra é que durante o Akademy, entre 7 e 11, acontecerá o Qt Desktop Days, evento da KDAB voltado para Qt no desktop (surpresa?). A programação preliminar já está disponível e será muito interessante ver os avanços da tecnologia em um campo que pode parecer menos sexy hoje em dia, por conta da muita atenção dada a projetos mobile ou embarcados, mas que pelo contrário, continua vibrante e recebendo muito investimento.
+
+
Após uma rápida pausa para um respiro, temos a Maratona Qt. Nosso amigo Sandro Andrade, professor do IFBA e colaborador de longa data do KDE, resolveu dedicar uma semana inteira, de 14 à 18 de setembro, para apresentar 5 tópicos sobre o Qt tratando de seus fundamentos e passos iniciais de cada um. O programa cobre QML, C++ e Qt, Qt no Android, no iOS, na web (sim!), computação gráfica e mesmo jogos! Extremamente recomendada pra todo mundo que conhece ou quer conhecer o framework.
+
A Maratona Qt vai servir como um esquenta para a QtCon Brasil 2020, esse ano também virtual. Em 26 e 27 de setembro o pessoal da qmob.solutions reunirá desenvolvedores Qt de vários países para apresentarem, entre outras coisas, trabalhos com Wayland, visão computacional e IA, análise de dados, Python, containers, prototipagem, embarcados, e outros, tudo envolvendo Qt! E também haverá uma apresentação sobre a próxima versão major da ferramenta, Qt 6.
+
Portanto pessoal, reservem este mês para uma grande imersão nos vários aspectos e possibilidades disponibilizadas pelo Qt.
Last time I described a way to search MAGs in metagenomes,
+and teased about interesting results.
+Let's dig in some of them!
+
I prepared a repo with the data and a notebook with the analysis I did in this
+post.
+You can also follow along in Binder,
+as well as do your own analysis!
+
Preparing some metadata
+
The supplemental materials for Tully et al include more details about each MAG,
+so let's download them.
+I prepared a small snakemake workflow to do that,
+as well as downloading information about the SRA datasets from Tara Oceans
+(the dataset used to generate the MAGs),
+as well as from Parks et al,
+which also generated MAGs from Tara Oceans.
+Feel free to include them in your analysis,
+but I was curious to find matches in other metagenomes.
+
Loading the data
+
The results from the MAG search are in a CSV file,
+with a column for the MAG name,
+another for the SRA dataset ID for the metagenome and a third column for the
+containment of the MAG in the metagenome.
+I also fixed the names to make it easier to query,
+and finally removed the Tara and Parks metagenomes
+(because we already knew they contained these MAGs).
+
This left us with 23,644 SRA metagenomes with matches,
+covering 2,291 of the 2,631 MAGs.
+These are results for a fairly low containment (10%),
+so if we limit to MAGs with more than 50% containment we still have 1,407 MAGs and 2,938 metagenomes left.
+
TOBG_NP-110, I choose you!
+
That's still a lot,
+so I decided to pick a candidate to check before doing any large scale analysis.
+I chose TOBG_NP-110 because there were many matches above 50% containment,
+and even some at 99%.
+Turns out it is also an Archaeal MAG that failed to be classified further than Phylum level (Euryarchaeota),
+with a 70.3% complete score in the original analysis.
+Oh, let me dissect the name a bit:
+TOBG is "Tara Ocean Binned Genome" and "NP" is North Pacific.
+
And so I went checking where the other metagenome matches came from.
+5 of the 12 matches above 50% containment come from one study,
+SRP044185,
+with samples collected from a column of water in a station in Manzanillo, Mexico.
+Other 3 matches come from
+SRP003331,
+in the South Pacific ocean (in northern Chile).
+Another match,
+ERR3256923,
+also comes from the South Pacific.
+
What else can I do?
+
I'm curious to follow the refining MAGs tutorial from the Meren Lab and see where this goes,
+and especially in using spacegraphcats
+to extract neighborhoods from the MAG and better evaluate what is missing or if there are other interesting bits that
+the MAG generation methods ended up discarding.
+
So, for now that's it.
+But more important,
+I didn't want to sit on these results until there is a publication in press,
+especially when there are people that can do so much more with these,
+so I decided to make it all public.
+It is way more exciting to see this being used to know more about these
+organisms than me being the only one with access to this info.
I don’t know who told students that the goal of research is to find some
+previously undiscovered research topic, claim individual ownership over it,
+& fiercely protect it from theft, but that almost sounds like, well,
+colonialism, capitalism, & policing
+
+
Amen.
+
I want to run this with my data!
+
Next time. But we will have a discussion about scientific infrastructure and
+sustainability first =]
(or: Top-down and bottom-up approaches for working around sourmash limitations)
+
In the last month I updated wort,
+the system I developed for computing sourmash signature for public genomic databases,
+and started calculating signatures
+for the metagenomes in the Sequence Read Archive.
+This is a more challenging subset than the microbial datasets I was doing previously,
+since there are around 534k datasets from metagenomic sources in the SRA,
+totalling 447 TB of data.
+Another problem is the size of the datasets,
+ranging from a couple of MB to 170 GB.
+Turns out that the workers I have in wort are very good for small-ish datasets,
+but I still need to figure out how to pull large datasets faster from the SRA,
+because the large ones take forever to process...
+
The good news is that I managed to calculate signatures for almost 402k of them
+1,
+which already let us work on some pretty exciting problems =]
+
Looking for MAGs in the SRA
+
Metagenome-assembled genomes are essential for studying organisms that are hard to isolate and culture in lab,
+especially for environmental metagenomes.
+Tully et al published 2,631 draft MAGs from 234 samples collected during the Tara Oceans expedition,
+and I wanted to check if they can also be found in other metagenomes besides the Tara Oceans ones.
+The idea is to extract the reads from these other matches and evaluate how the MAG can be improved,
+or at least evaluate what is missing in them.
+I choose to use environmental samples under the assumption they are easier to deposit on the SRA and have public access,
+but there are many human gut microbiomes in the SRA and this MAG search would work just fine with those too.
+
Moreover,
+I want to search for containment,
+and not similarity.
+The distinction is subtle,
+but similarity takes into account both datasets sizes
+(well, the size of the union of all elements in both datasets),
+while containment only considers the size of the query.
+This is relevant because the similarity of a MAG and a metagenome is going to be very small (and is symmetrical),
+but the containment of the MAG in the metagenome might be large
+(and is asymmetrical, since the containment of the metagenome in the MAG is likely very small because the metagenome is so much larger than the MAG).
+
The computational challenge: indexing and searching
+
sourmash signatures are a small fraction of the original size of the datasets,
+but when you have hundreds of thousands of them the collection ends up being pretty large too.
+More precisely, 825 GB large.
+That is way bigger than any index I ever built for sourmash,
+and it would also have pretty distinct characteristics than what we usually do:
+we tend to index genomes and run search (to find similar genomes) or gather
+(to decompose metagenomes into their constituent genomes),
+but for this MAG search I want to find which metagenomes have my MAG query above a certain containment threshold.
+Sort of a sourmash search --containment,
+but over thousands of metagenome signatures.
+The main benefit of an SBT index in this context is to avoid checking all signatures because we can prune the search early,
+but currently SBT indices need to be totally loaded in memory during sourmash index.
+I will have to do this in the medium term,
+but I want a solution NOW! =]
+
sourmash 3.4.0 introduced --from-file in many commands,
+and since I can't build an index I decided to use it to load signatures for the metagenomes.
+But... sourmash search tries to load all signatures in memory,
+and while I might be able to find a cluster machine with hundreds of GBs of RAM available,
+that's not very practical.
+
So, what to do?
+
The top-down solution: a snakemake workflow
+
I don't want to modify sourmash now,
+so why not make a workflow and use snakemake to run one sourmash search --containment for each metagenome?
+That means 402k tasks,
+but at least I can use batches and SLURM job arrays to submit reasonably-sized jobs to our HPC queue.
+After running all batches I summarized results for each task,
+and it worked well for a proof of concept.
+
But... it was still pretty resource intensive:
+each task was running one query MAG against one metagenome,
+and so each task needed to do all the overhead of starting the Python interpreter and parsing the query signature,
+which is exactly the same for all tasks.
+Extending it to support multiple queries to the same metagenome would involve duplicating tasks,
+and 402k metagenomes times 2,631 MAGs is...
+a very large number of jobs.
+
I also wanted to avoid clogging the job queues,
+which is not very nice to the other researchers using the cluster.
+This limited how many batches I could run in parallel...
+
The bottom-up solution: Rust to the rescue!
+
Thinking a bit more about the problem,
+here is another solution:
+what if we load all the MAGs in memory
+(as they will be queried frequently and are not that large),
+and then for each metagenome signature load it,
+perform all MAG queries,
+and then unload the metagenome signature from memory?
+This way we can control memory consumption
+(it's going to be proportional to all the MAG sizes plus the size of the largest metagenome)
+and can also efficiently parallelize the code because each task/metagenome is independent
+and the MAG signatures can be shared freely (since they are read-only).
+
This could be done with the sourmash Python API plus multiprocessing or some
+other parallelization approach (maybe dask?),
+but turns out that everything we need comes from the Rust API.
+Why not enjoy a bit of the fearless concurrency that is one of the major Rust goals?
+
The whole code ended up being 176 lines long,
+including command line parsing using strucopt and parallelizing the search using rayon
+and a multiple-producer, single-consumer channel to write results to an output
+(either the terminal or a file).
+This version took 11 hours to run,
+using less than 5GB of RAM and 32 processors,
+to search 2k MAGs against 402k metagenomes.
+And, bonus! It can also be parallelized again if you have multiple machines,
+so it potentially takes a bit more than an hour to run if you can allocate 10 batch jobs,
+with each batch 1/10 of the metagenome signatures.
+
So, is bottom-up always the better choice?
+
I would like to answer "Yes!",
+but bioinformatics software tends to be organized as command line interfaces,
+not as libraries.
+Libraries also tend to have even less documentation than CLIs,
+and this particular case is not a fair comparison because...
+Well, I wrote most of the library,
+and the Rust API is not that well documented for general use.
+
But I'm pretty happy with how the sourmash CLI is viable both for the top-down approach
+(and whatever workflow software you want to use) as well as how the Rust core worked for the bottom-up approach.
+I think the most important is having the option to choose which way to go,
+especially because now I can use the bottom-up approach to make the sourmash CLI
+and Python API better.
+The top-down approach is also way more accessible in general,
+because you can pick your favorite workflow software and use all the tricks you're comfortable with.
+
But, what about the results?!?!?!
+
Next time. But I did find MAGs with over 90% containment in very different locations,
+which is pretty exciting!
+
I also need to find a better way of distributing all these signature,
+because storing 4 TB of data in S3 is somewhat cheap,
+but transferring data is very expensive.
+All signatures are also available on IPFS,
+but I need more people to host them and share.
+Get in contact if you're interested in helping =]
+
And while I'm asking for help,
+any tips on pulling data faster from the SRA are greatly appreciated!
pulling about a 100 TB in 3 days, which was pretty fun to see because I
+ended up DDoS myself because I couldn't download the generated sigs fast enough
+from the S3 bucket where they are temporarily stored =P ↩
sourmash 3.3 was released last week,
+and it is the first version supporting zipped databases.
+Here is my personal account of how that came to be =]
+
What is a sourmash database?
+
A sourmash database contains signatures (typically Scaled MinHash sketches built from genomic datasets) and
+an index for allowing efficient similarity and containment queries over these signatures.
+The two types of index are SBT,
+a hierarchical index that uses less memory by keeping data on disk,
+and LCA,
+an inverted index that uses more memory but is potentially faster.
+Indices are described as JSON files,
+with LCA storing all the data in one JSON file and SBT opting for saving a description of the index structure in JSON,
+and all the data into a hidden directory with many files.
+
We distribute some prepared databases (with SBT indices) for Genbank and RefSeq as compressed TAR files.
+The compressed file is ~8GB,
+but after decompressing it turns into almost 200k files in a hidden directory,
+using about 40 GB of disk space.
+
Can we avoid generating so many hidden files?
+
The initial issue in this saga is dib-lab/sourmash#490,
+and the idea was to take the existing support for multiple data storages
+(hidden dir,
+TAR files,
+IPFS and Redis) and save the index description in the storage,
+allowing loading everything from the storage.
+Since we already had the databases as TAR files,
+the first test tried to use them but it didn't take long to see it was a doomed approach:
+TAR files are terrible from random access
+(or at least the tarfile module in Python is).
+
Zip files showed up as a better alternative,
+and it helps that Python has the zipfile module already available in the
+standard library.
+Initial tests were promising,
+and led to dib-lab/sourmash#648.
+The main issue was performance:
+compressing and decompressing was slow,
+but there was also another limitation...
+
Loading Nodegraphs from a memory buffer
+
Another challenge was efficiently loading the data from a storage.
+The two core methods in a storage are save(location, content),
+where content is a bytes buffer,
+and load(location),
+which returns a bytes buffer that was previously saved.
+This didn't interact well with the khmerNodegraphs (the Bloom Filter we use for SBTs),
+since khmer only loads data from files,
+not from memory buffers.
+We ended up doing a temporary file dance,
+which made things slower for the default storage (hidden dir),
+where it could have been optimized to work directly with files,
+and involved interacting with the filesystem for the other storages
+(IPFS and Redis could be pulling data directly from the network,
+for example).
+
This one could be fixed in khmer by exposing C++ stream methods,
+and I did a small PoC to test the idea.
+While doable,
+this is something that was happening while the sourmash conversion to Rust was underway,
+and depending on khmer was a problem for my Webassembly aspirations...
+so,
+having the Nodegraph implemented in Rust seemed like a better direction,
+That has actually been quietly living in the sourmash codebase for quite some time,
+but it was never exposed to the Python (and it was also lacking more extensive
+tests).
+
After the release of sourmash 3 and the replacement of the C++ for the Rust implementation,
+all the pieces for exposing the Nodegraph where in place,
+so dib-lab/sourmash#799 was the next step.
+It wasn't a priority at first because other optimizations
+(that were released in 3.1 and 3.2)
+were more important,
+but then it was time to check how this would perform.
+And...
+
Your Rust code is not so fast, huh?
+
Turns out that my Nodegraph loading code was way slower than khmer.
+The Nodegraph binary format is well documented,
+and doing an initial implementation wasn't so hard by using the byteorder crate
+to read binary data with the right endianess,
+and then setting the appropriate bits in the internal fixedbitset in memory.
+But the khmer code doesn't parse bit by bit:
+it reads a long char buffer directly,
+and that is many orders of magnitude faster than setting bit by bit.
+
And there was no way to replicate this behavior directly with fixedbitset.
+At this point I could either bit-indexing into a large buffer
+and lose all the useful methods that fixedbitset provides,
+or try to find a way to support loading the data directly into fixedbitset and
+open a PR.
It was more straightforward than I expected,
+but it did expose the internal representation of fixedbitset,
+so I was a bit nervous it wasn't going to be merged.
+But bluss was super nice,
+and his suggestions made the PR way better!
+This simplified the final Nodegraph code,
+and actually was more correct
+(because I was messing a few corner cases when doing the bit-by-bit parsing before).
+Win-win!
+
Nodegraphs are kind of large, can we compress them?
+
Being able to save and load Nodegraphs in Rust allowed using memory buffers,
+but also opened the way to support other operations not supported in khmer Nodegraphs.
+One example is loading/saving compressed files,
+which is supported for Countgraph
+(another khmer data structure,
+based on Count-Min Sketch)
+but not in Nodegraph.
+
If only there was an easy way to support working with compressed files...
+
Oh wait, there is! niffler is a crate that I made with Pierre Marijon based
+on some functionality I saw in one of his projects,
+and we iterated a bit on the API and documented everything to make it more
+useful for a larger audience.
+niffler tries to be as transparent as possible,
+with very little boilerplate when using it but with useful features nonetheless
+(like auto detection of the compression format).
+If you want more about the motivation and how it happened,
+check this Twitter thread.
+
The cool thing is that adding compressed files support in sourmash was mostly
+one-line changes for loading
+(and a bit more for saving,
+but mostly because converting compression levels could use some refactoring).
+
Putting it all together: zipped SBT indices
+
With all these other pieces in places,
+it's time to go back to dib-lab/sourmash#648.
+Compressing and decompressing with the Python zipfile module is slow,
+but Zip files can also be used just for storage,
+handing back the data without extracting it.
+And since we have compression/decompression implemented in Rust with niffler,
+that's what the zipped sourmash databases are:
+data is loaded and saved into the Zip file without using the Python module
+compression/decompression,
+and all the work is done before (or after) in the Rust side.
+
This allows keeping the Zip file with similar sizes to the original TAR files we started with,
+but with very low overhead for decompression.
+For compression we opted for using Gzip level 1,
+which doesn't compress perfectly but also doesn't take much longer to run:
+
+
+
+
Level
+
Size
+
Time
+
+
+
+
+
0
+
407 MB
+
16s
+
+
+
1
+
252 MB
+
21s
+
+
+
5
+
250 MB
+
39s
+
+
+
9
+
246 MB
+
1m48s
+
+
+
+
In this table, 0 is without compression,
+while 9 is the best compression.
+The size difference from 1 to 9 is only 6 MB (~2% difference)
+but runs 5x faster,
+and it's only 30% slower than saving the uncompressed data.
+
The last challenge was updating an existing Zip file.
+It's easy to support appending new data,
+but if any of the already existing data in the file changes
+(which happens when internal nodes change in the SBT,
+after a new dataset is inserted) then there is no easy way to replace the data in the Zip file.
+Worse,
+the Python zipfile will add the new data while keeping the old one around,
+leading to ginormous files over time1
+So, what to do?
+
I ended up opting for dealing with the complexity and complicating the ZipStorage implementation a bit,
+by keeping a buffer for new data.
+If it's a new file or it already exists but there are no insertions
+the buffer is ignored and all works as before.
+
If the file exists and new data is inserted,
+then it is first stored in the buffer
+(where it might also replace a previous entry with the same name).
+In this case we also need to check the buffer when trying to load some data
+(because it might exist only in the buffer,
+and not in the original file).
+
Finally,
+when the ZipStorage is closed it needs to verify if there are new items in the buffer.
+If not,
+it is safe just to close the original file.
+If there are new items but they were not present in the original file,
+then we can append the new data to the original file.
+The final case is if there are new items that were also in the original file,
+and in this case a new Zip file is created and all the content from buffer and
+original file are copied to it,
+prioritizing items from the buffer.
+The original file is replaced by the new Zip file.
+
Turns out this worked quite well! And so the PR was merged =]
+
The future
+
Zipped databases open the possibility of distributing extra data that might be
+useful for some kinds of analysis.
+One thing we are already considering is adding taxonomy information,
+let's see what else shows up.
+
Having Nodegraph in Rust is also pretty exciting,
+because now we can change the internal representation for something that uses
+less memory (maybe using RRR encoding?),
+but more importantly:
+now they can also be used with Webassembly,
+which opens many possibilities for running not only signature computation but
+also search and gather in the browser,
+since now we have all the pieces to build it.
The zipfile module does throw a UserWarning pointing that duplicated files were inserted,
+which is useful during development but generally doesn't show during regular usage... ↩
sourmash 3 was released last week,
+finally landing the Rust backend.
+But, what changes when developing new features in sourmash?
+I was thinking about how to best document this process,
+and since PR #826 is a short example touching all the layers I decided to do a
+small walkthrough.
+
Shall we?
+
The problem
+
The first step is describing the problem,
+and trying to convince reviewers (and yourself) that the changes bring enough benefits to justify a merge.
+This is the description I put in the PR:
+
+
Calling .add_hash() on a MinHash sketch is fine,
+but if you're calling it all the time it's better to pass a list of hashes and call .add_many() instead.
+Before this PR add_many just called add_hash for each hash it was passed,
+but now it will pass the full list to Rust (and that's way faster).
+
No changes for public APIs,
+and I changed the _signatures method in LCA to accumulate hashes for each sig first,
+and then set them all at once.
+This is way faster,
+but might use more intermediate memory (I'll evaluate this now).
+
+
There are many details that sound like jargon for someone not familiar with the codebase,
+but if I write something too long I'll probably be wasting the reviewers time too.
+The benefit of a very detailed description is extending the knowledge for other people
+(not necessarily the maintainers),
+but that also takes effort that might be better allocated to solve other problems.
+Or, more realistically, putting out other fires =P
+
Nonetheless,
+some points I like to add in PR descriptions:
+- why is there a problem with the current approach?
+- is this the minimal viable change, or is it trying to change too many things
+ at once? The former is way better, in general.
+- what are the trade-offs? This PR is using more memory to lower the runtime,
+ but I hadn't measure it yet when I opened it.
+- Not changing public APIs is always good to convince reviewers.
+ If the project follows a semantic versioning scheme,
+ changes to the public APIs are major version bumps,
+ and that can brings other consequences for users.
+
Setting up for changing code
+
If this was a bug fix PR,
+the first thing I would do is write a new test triggering the bug,
+and then proceed to fix it in the code
+(Hmm, maybe that would be another good walkthrough?).
+But this PR is making performance claims ("it's going to be faster"),
+and that's a bit hard to codify in tests.
+1
+Since it's also proposing to change a method (_signatures in LCA indices) that is better to benchmark with a real index (and not a toy example),
+I used the same data and command I run in sourmash_resources to check how memory consumption and runtime changed.
+For reference, this is the command:
I'm using the benchmark feature from snakemake in sourmash_resources to
+track how much memory, runtime and I/O is used for each command (and version) of sourmash,
+and generate the plots in the README in that repo.
+That is fine for a high-level view ("what's the maximum memory used?"),
+but not so useful for digging into details ("what method is consuming most memory?").
+
Another additional problem is the dual2 language nature of sourmash,
+where we have Python calling into Rust code (via CFFI).
+There are great tools for measuring and profiling Python code,
+but they tend to not work with extension code...
+
So, let's bring two of my favorite tools to help!
+
Memory profiling: heaptrack
+
heaptrack is a heap profiler, and I first heard about it from Vincent Prouillet.
+Its main advantage over other solutions (like valgrind's massif) is the low
+overhead and... how easy it is to use:
+just stick heaptrack in front of your command,
+and you're good to go!
+
Example output:
+
$ heaptrack sourmash search -o out.csv --scaled 2000 -k 51 HSMA33OT.fastq.gz.sig genbank-k51.lca.json.gz
+
+heaptrack stats:
+ allocations: 1379353
+ leaked allocations: 1660
+ temporary allocations: 168984
+Heaptrack finished! Now run the following to investigate the data:
+
+ heaptrack --analyze heaptrack.sourmash.66565.gz
+
+
+
+
heaptrack --analyze is a very nice graphical interface for analyzing the results,
+but for this PR I'm mostly focusing on the Summary page (and overall memory consumption).
+Tracking allocations in Python doesn't give many details,
+because it shows the CPython functions being called,
+but the ability to track into the extension code (Rust) allocations is amazing
+for finding bottlenecks (and memory leaks =P).
+3
+
CPU profiling: py-spy
+
Just as other solutions exist for profiling memory,
+there are many for profiling CPU usage in Python,
+including profile and cProfile in the standard library.
+Again, the issue is being able to analyze extension code,
+and bringing the cannon (the perf command in Linux, for example) looses the
+benefit of tracking Python code properly (because we get back the CPython
+functions, not what you defined in your Python code).
+
Enters py-spy by Ben Frederickson,
+based on the rbspy project by Julia Evans.
+Both use a great idea:
+read the process maps for the interpreters and resolve the full stack trace information,
+with low overhead (because it uses sampling).
+py-spy also goes further and resolves native Python extensions stack traces,
+meaning we can get the complete picture all the way from the Python CLI to the
+Rust core library!4
+
py-spy is also easy to use:
+stick py-spy record --output search.svg -n -- in front of the command,
+and it will generate a flamegraph in search.svg.
+The full command for this PR is
sigd[vv].add_hash(k) is the culprit.
+Each call to .add_hash has to go thru CFFI to reach the extension code,
+and the overhead is significant.
+It is a similar situation to accessing array elements in NumPy:
+it works,
+but it is way slower than using operations that avoid crossing from Python to
+the extension code.
+What we want to do instead is call .add_many(hashes),
+which takes a list of hashes and process it entirely in Rust
+(ideally. We will get there).
+
But, to have a list of hashes, there is another issue with this code.
There are two nested for loops,
+and add_hash is being called with values from the inner loop.
+So... we don't have the list of hashes beforehand.
+
But we can change the code a bit to save the hashes for each signature
+in a temporary list,
+and then call add_many on the temporary list.
+Like this:
There is a trade-off here:
+if we save the hashes in temporary lists,
+will the memory consumption be so high that the runtime gains of calling
+add_many in these temporary lists be cancelled?
+
Time to measure it =]
+
+
+
+
version
+
mem
+
time
+
+
+
+
+
original
+
1.5 GB
+
160s
+
+
+
list
+
1.7GB
+
173s
+
+
+
+
Wait, it got worse?!?! Building temporary lists only takes time and memory,
+and bring no benefits!
+
This mystery goes away when you look at the add_many method:
+
defadd_many(self,hashes):
+ "Add many hashes in at once."
+ ifisinstance(hashes,MinHash):
+ self._methodcall(lib.kmerminhash_add_from,hashes._objptr)
+ else:
+ forhashinhashes:
+ self._methodcall(lib.kmerminhash_add_hash,hash)
+
+
+
+
The first check in the if statement is a shortcut for adding hashes from
+another MinHash, so let's focus on else part...
+And turns out that add_many is lying!
+It doesn't process the hashes in the Rust extension,
+but just loops and call add_hash for each hash in the list.
+That's not going to be any faster than what we were doing in _signatures.
self._methodcall is a convenience method defined in RustObject
+which translates a method-like call into a function call,
+since our C layer only has functions.
+This is the C prototype for this function:
You can almost read it as a Python method declaration,
+where KmerMinHash *ptr means the same as the self in Python methods.
+The other two arguments are a common idiom when passing pointers to data in C,
+with insize being how many elements we have in the list.
+5.
+CFFI is very good at converting Python lists into pointers of a specific type,
+as long as the type is of a primitive type
+(uint64_t in our case, since each hash is a 64-bit unsigned integer number).
+
And the Rust code with the implementation of the function:
The weird ffi_fn! {} syntax around the function is a macro in Rust:
+it changes the final generated code to convert the return value (Result<()>) into something that is valid C code (in this case, void).
+What happens if there is an error, then?
+The Rust extension has code for passing back an error code and message to Python,
+as well as capturing panics (when things go horrible bad and the program can't recover)
+in a way that Python can then deal with (raising exceptions and cleaning up).
+It also sets the #[no_mangle] attribute in the function,
+meaning that the final name of the function will follow C semantics (instead of Rust semantics),
+and can be called more easily from C and other languages.
+This ffi_fn! macro comes from symbolic,
+a big influence on the design of the Python/Rust bridge in sourmash.
+
unsafe is the keyword in Rust to disable some checks in the code to allow
+potentially dangerous things (like dereferencing a pointer),
+and it is required to interact with C code.
+unsafe doesn't mean that the code is always unsafe to use:
+it's up to whoever is calling this to verify that valid data is being passed and invariants are being preserved.
+
If we remove the ffi_fn! macro and the unsafe keyword,
+we have
The type of an argument comes after the name of the argument in Rust,
+ while it's the other way around in C.
+ Same for the return type (it is omitted in the Rust function, which means it
+ is -> (), equivalent to a void return type in C).
+
In Rust everything is immutable by default, so we need to say that we want
+ a mutable pointer to a KmerMinHash item: *mut KmerMinHash).
+ In C everything is mutable by default.
+
u64 in Rust -> uint64_t in C
+
usize in Rust -> uintptr_t in C
+
+
Let's check the implementation of the function now.
+We start by converting the ptr argument (a raw pointer to a KmerMinHash struct)
+into a regular Rust struct:
+
letmh={
+assert!(!ptr.is_null());
+&mut*ptr
+};
+
+
+
+
This block is asserting that ptr is not a null pointer,
+and if so it dereferences it and store in a mutable reference.
+If it was a null pointer the assert! would panic (which might sound extreme,
+but is way better than continue running because dereferencing a null pointer is
+BAD).
+Note that functions always need all the types in arguments and return values,
+but for variables in the body of the function
+Rust can figure out types most of the time,
+so no need to specify them.
+
The next block prepares our list of hashes for use:
We are again asserting that the hashes_ptr is not a null pointer,
+but instead of dereferencing the pointer like before we use it to create a slice,
+a dynamically-sized view into a contiguous sequence.
+The list we got from Python is a contiguous sequence of size insize,
+and the slice::from_raw_parts function creates a slice from a pointer to data and a size.
+
Oh, and can you spot the bug?
+I created the slice using *mut u64,
+but the data is declared as *const u64.
+Because we are in an unsafe block Rust let me change the mutability,
+but I shouldn't be doing that,
+since we don't need to mutate the slice.
+Oops.
+
Finally, let's add hashes to our MinHash!
+We need a for loop, and call add_hash for each hash:
We finish the function with Ok(()) to indicate no errors occurred.
+
Why is calling add_hash here faster than what we were doing before in Python?
+Rust can optimize these calls and generate very efficient native code,
+while Python is an interpreted language and most of the time don't have the same
+guarantees that Rust can leverage to generate the code.
+And, again,
+calling add_hash here doesn't need to cross FFI boundaries or,
+in fact,
+do any dynamic evaluation during runtime,
+because it is all statically analyzed during compilation.
+
Putting it all together
+
And... that's the PR code.
+There are some other unrelated changes that should have been in new PRs,
+but since they were so small it would be more work than necessary.
+OK, that's a lame excuse:
+it's confusing for reviewers to see these changes here,
+so avoid doing that if possible!
+
But, did it work?
+
+
+
+
version
+
mem
+
time
+
+
+
+
+
original
+
1.5 GB
+
160s
+
+
+
list
+
1.7GB
+
73s
+
+
+
+
We are using 200 MB of extra memory,
+but taking less than half the time it was taking before.
+I think this is a good trade-off,
+and so did the reviewer and the PR was approved.
The first version of the PR used a set instead of a list to accumulate hashes.
+Since a set doesn't have repeated elements,
+this could potentially use less memory.
+The code:
CFFI doesn't know how to convert a set into something that C understands,
+so we need to call list(hashes) to convert it into a list.
+Since Python (and CFFI) can't know if the data is going to be used later
+6
+it needs to keep it around
+(and be eventually deallocated by the garbage collector).
+And that's how we get at least double the memory being allocated...
+
There is another lesson here.
+If we look at the for loop again:
each k is already unique because they are keys in the hashval_to_idx dictionary,
+so the initial assumption
+(that a set might save memory because it doesn't have repeated elements)
+is... irrelevant for the problem =]
Footnotes
We do have https://asv.readthedocs.io/ set up for micro-benchmarks,
+and now that I think about it...
+I could have started by writing a benchmark for add_many,
+and then showing that it is faster.
+I will add this approach to the sourmash PR checklist =] ↩
It would be super cool to have the unwinding code from py-spy in heaptrack,
+and be able to see exactly what Python methods/lines of code were calling the
+Rust parts... ↩
Even if py-spy doesn't talk explicitly about Rust,
+it works very very well, woohoo! ↩
Let's not talk about lack of array bounds checks in C... ↩
something that the memory ownership model in Rust does, BTW ↩
In January I wrote a post for the Rust 2019 call for blogs.
+The 2020 call is aiming for an RFC and roadmap earlier this time,
+so here is my 2020 post =]
+
Last call review: what happened?
+
An attribute proc-macro like #[wasm_bindgen] but for FFI
+
This sort of happened... because WebAssembly is growing =]
+
I was very excited when Interface Types showed up in August,
+and while it is still very experimental it is moving fast and bringing saner
+paths for interoperability than raw C FFIs.
+David Beazley even point this at the end of his PyCon India keynote,
+talking about how easy is to get information out of a WebAssembly module
+compared to what had to be done for SWIG.
+
This doesn't solve the problem where strict C compatibility is required,
+or for platforms where a WebAssembly runtime is not available,
+but I think it is a great solution for scientific software
+(or, at least, for my use cases =]).
+
"More -sys and Rust-like crates for interoperability with the larger ecosystems" and "More (bioinformatics) tools using Rust!"
+
I did some of those this year (bbhash-sys and mqf),
+and also found some great crates to use in my projects.
+Rust is picking up steam in bioinformatics,
+being used as the primary choice for high quality software
+(like varlociraptor,
+or the many coming from 10X Genomics)
+but it is still somewhat hard to find more details
+(I mostly find it on Twitter,
+and sometime Google Scholar alerts).
+It would be great to start bringing this info together,
+which leads to...
+
"A place to find other scientists?"
+
Hey, this one happened! Luca Palmieri started a conversation on reddit and
+the #science-and-ai Discord channel on the Rust community server was born!
+I think it works pretty well,
+and Luca also has being doing a great job running workshops
+and guiding the conversation around rust-ml.
+
Rust 2021: Interoperability
+
Rust is amazing because it is very good at bringing many concepts and ideas that
+seem contradictory at first,
+but can really shine when synthesized.
+But can we share this combined wisdom and also improve the situation in other
+places too?
+Despite the "Rewrite it in Rust" meme,
+increased interoperability is something that is already driving a lot of the
+best aspects of Rust:
+
+
+
Interoperability with other languages: as I said before,
+ with WebAssembly (and Rust being having the best toolchain for it)
+ there is a clear route to achieve this,
+ but it will not replace all the software that already exist and can benefit
+ from FFI and C compatibility.
+ Bringing together developers from the many language specific binding
+ generators (helix, neon, rustler, PyO3...) and figuring out what's missing from
+ them (or what is the common parts that can be shared) also seems productive.
+
+
+
Interoperability with new and unexplored domains.
+ I think Rust benefits enormously from not focusing only in one domain,
+ and choosing to prioritize CLI, WebAssembly, Networking and Embedded is a good
+ subset to start tackling problems,
+ but how to guide other domains to also use Rust and come up with new
+ contributors and expose missing pieces of the larger picture?
+
+
+
Another point extremely close to interoperability is training.
+A great way to interoperate with other languages and domains is having good
+documentation and material from transitioning into Rust without having to figure
+everything at once.
+Rust documentation is already amazing,
+especially considering the many books published by each working group.
+But... there is a gap on the transitions,
+both from understanding the basics of the language and using it,
+to the progression from beginner to intermediate and expert.
+
I see good resources for JavaScript and Python developers,
+but we are still covering a pretty small niche:
+programmers curious enough to go learn another language,
+or looking for solutions for problems in their current language.
+
Can we bring more people into Rust?
+RustBridge is obviously the reference here,
+but there is space for much,
+much more.
+Using Rust in The Carpentries lessons?
+Creating RustOpenSci,
+mirroring the communities of practice of rOpenSci and pyOpenSci?
Como exemplo, vamos criar um novo dicionário chamado new_dict com os valores de dict_1 e dict_2 logo acima. Uma abordagem bem conhecida é utilizar o método update.
Este método funciona bem, porém temos de chamar o método update para cada dict que desejamos mesclar em new_dict. Não seria interessante se fosse possível passar todos os dicts necessários já na inicialização de new_dict?
+
Novidades do Python 3
+
O Python 3 introduziu uma maneira bem interessante de se fazer isso, utilizando os operadores **.
+
new_dict={
+ **dict_1,
+ **dict_2,
+}
+
+
+
+
Assim, de maneira semelhante ao exemplo anterior, temos que new_dict será :
Ao utilizamos o procedimento de inicialização acima, devemos tomar conseiderar alguns fatores. Apenas os valores do primeiro nível serão realmente duplicados no novo dicionário. Como exemplo, vamos alterar uma chave presente em ambos os dicts e verificar se as mesmas possuem o mesmo valor:
Como no exemplo anterior, podemos imaginar que foi realizado uma cópia de todos os elementos de dict_3, porém isso não é totalmente verdade. O que realmente aconteceu é que foi feita uma cópia superficial dos valores de dict_3, ou seja, apenas os valores de primeiro nível foram duplicados. Observe o que acontece quando alteramos o valor do dict presente na chave c.
+
>>new_dict['c']['d']=11
+>>print(new_dict['c']['d'])
+11
+>>print(dict_3['c']['d'])
+11
+# valor anterior era 5
+
+
+
+
No caso da chave c, ela contem uma referência para outra estrutura de dados (um dict, no caso). Quando alteramos algum valor de dict_3['c'], isso reflete em todos os dict que foram inicializados com dict_3. Em outras palavras, deve-se ter cuidado ao inicializar um dict a partir de outros dicts quando os mesmos possuírem valores complexos, como list, dict ou outros objetos (os atributos deste objeto não serão duplicados).
+
De modo a contornar este inconveniente, podemos utilizar o método deepcopy da lib nativa copy. Agora, ao inicializarmos new_dict:
O método deepcopy realiza uma cópia recursiva de cada elemento de dict_3, resolvendo nosso problema. Veja mais um exemplo:
+
>>new_dict['c']['d']=11
+>>print(new_dict['c']['d'])
+11
+>>print(dict_3['c']['d'])
+5
+# valor não foi alterado
+
+
+
+
Conclusão
+
Este artigo tenta demonstrar de maneira simples a criação de dicts, utilizando os diversos recursos que a linguagem oferece bem como os prós e contras de cada abordagem.
+
Referências
+
Para mais detalhes e outros exemplos, deem uma olhada neste post do forum da Python Brasil aqui.
+O movimento é uma característica que está presente na maioria dos jogos. Ao saltar entre plataformas, atirar contra a horda de inimigos, pilotar uma nave espacial e correr pelas estradas estamos exercendo movimento, interagindo com o ambiente do jogo, aplicando ações e causando reações.
+Neste capítulo iremos conhecer os conceitos básicos de movimentação de objetos na tela e sua interação com outros elementos através da detecção de colisão.
+Movimento Se você vem acompanhando esta série de postagens, teve um breve exemplo de movimentação na postagem sobre game loop, onde uma bola que se movimentava quicando pela tela foi implementada.
+Jogos sempre me conectam com tecnologia desde o início.
+Eu e meu pai montamos nosso primeiro computador (um Pentium 286) e a primeira coisa que eu me lembro de fazer, foi jogar os jogos de DOS como Prince of Persia e Lunar Lander. Eu aprendi vários comandos de CLI só para poder jogar os meus jogos favoritos.
+A paixão por jogar e fazer jogos sempre me acompanhou como um hobby. I tenho uma série de posts sobre pygame neste blog onde eu passo pelos conceitos básicos de desenvolvimento de jogos tentando explicar para pessoas que estejam iniciando seu aprendizado na área.
Este tutorial é baseado no Intro to Django que fica na parte de baixo da página start do Django project.
+
Até a data deste post o Django está na versão 2.2.2, e requer Python 3.
+
O que você precisa?
+
Python 3.6 ou superior, pip e virtualenv.
+
Considere que você tenha instalado Python 3.6 ou superior, pip e virtualenv.
+
Criando o ambiente
+
Crie uma pasta com o nome django2-pythonclub
+
$ mkdir django2-pythonclub
+$ cd django2-pythonclub
+
+
+
+
A partir de agora vamos considerar esta como a nossa pasta principal.
+
Considerando que você está usando Python 3, digite
+
python3 -m venv .venv
+
+
+
+
Lembre-se de colocar esta pasta no seu .gitignore, caso esteja usando.
+
echo .venv >> .gitignore
+
+
+
+
Depois ative o ambiente digitando
+
source .venv/bin/activate
+
+
+
+
+
Lembre-se, sempre quando você for mexer no projeto, tenha certeza de ter ativado o virtualenv, executando o comando source .venv/bin/activate. Você deve repetir esse comando toda a vez que você abrir um novo terminal.
+
+
Instalando Django 2.2.2
+
Basta digitar
+
pip install django==2.2.2
+
+
+
+
Dica: se você digitar pip freeze você verá a versão dos programas instalados.
Escondendo a SECRET_KEY e trabalhando com variáveis de ambiente
+
É muito importante que você não deixe sua SECRET_KEY exposta. Então remova-o imediatamente do seu settings.py ANTES mesmo do primeiro commit. Espero que você esteja usando Git.
+
Vamos usar o python-decouple escrito por Henrique Bastos para gerenciar nossas variáveis de ambiente. Repare que já instalamos ele logo acima.
+
Em seguida você vai precisar criar um arquivo .env, para isso rode o comando a seguir, ele vai criar uma pasta contrib e dentro dele colocar um arquivo env_gen.py
+
if [ ! -d contrib ]; then mkdir contrib; fi; git clone https://gist.github.com/22626de522f5c045bc63acdb8fe67b24.git contrib/
+rm -rf contrib/.git/ # remova a pasta .git que está dentro de contrib.
+
+
+
+
Em seguida rode
+
python contrib/env_gen.py
+
+
+
+
que ele vai criar o arquivo .env.
+
Supondo que você está versionando seu código com Git, é importante que você escreva isso dentro do seu arquivo .gitignore, faça direto pelo terminal
Já que falamos do python-decouple, precisamos de mais alguns ajustes
+
fromdecoupleimportconfig,Csv
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY=config('SECRET_KEY')
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG=config('DEBUG',default=False,cast=bool)
+
+ALLOWED_HOSTS=config('ALLOWED_HOSTS',default=[],cast=Csv())
+
+
+
+
Veja que é importante manter sua SECRET_KEY bem guardada (em outro lugar).
+
Então crie um arquivo .env e guarde sua SECRET_KEY dentro dele, exemplo:
fromdjango.dbimportmodels
+fromdjango.urlsimportreverse_lazy
+
+
+classBand(models.Model):
+
+ """A model of a rock band."""
+ name=models.CharField(max_length=200)
+ can_rock=models.BooleanField(default=True)
+
+ classMeta:
+ ordering=('name',)
+ verbose_name='band'
+ verbose_name_plural='bands'
+
+ def__str__(self):
+ returnself.name
+
+ defget_absolute_url(self):
+ # retorna a url no formato /bands/1/
+ returnreverse_lazy('band_detail',kwargs={'pk':self.pk})
+
+ defget_members_count(self):
+ # count members by band
+ # conta os membros por banda
+ returnself.band.count()
+
+
+classMember(models.Model):
+
+ """A model of a rock band member."""
+ name=models.CharField("Member's name",max_length=200)
+ instrument=models.CharField(choices=(
+ ('g',"Guitar"),
+ ('b',"Bass"),
+ ('d',"Drums"),
+ ('v',"Vocal"),
+ ('p',"Piano"),
+ ),
+ max_length=1
+ )
+
+ band=models.ForeignKey("Band",related_name='band',on_delete=models.CASCADE)
+
+ classMeta:
+ ordering=('name',)
+ verbose_name='member'
+ verbose_name_plural='members'
+
+ def__str__(self):
+ returnself.name
+
+
+
+
Tem algumas coisas que eu não estou explicando aqui para o tutorial ficar curto, mas uma coisa importante é que, como nós editamos o models.py vamos precisar criar um arquivo de migração do novo modelo. Para isso digite
Obs: deixei as demais urls comentada porque precisa da função em views.py para que cada url funcione. Descomente cada url somente depois que você tiver definido a função em classe em views.py a seguir.
Para fazer a busca por nome de banda usamos o comando search = request.GET.get('search_box'), onde search_box é o nome do campo no template band_list.html.
+
E os nomes são retornados a partir do comando bands = bands.filter(name__icontains=search). Onde icontains procura um texto que contém a palavra, ou seja, você pode digitar o nome incompleto (ignora maiúsculo ou minúsculo).
+
defband_list(request):
+ """ A view of all bands. """
+ bands=Band.objects.all()
+ search=request.GET.get('search_box')
+ ifsearch:
+ bands=bands.filter(name__icontains=search)
+ returnrender(request,'bands/band_list.html',{'bands':bands})
+
+
+
+
Em urls.py pode descomentar a linha a seguir:
+
path('bands/',v.band_list,name='bands'),
+
+
+
+
A função band_contact mostra como tratar um formulário na view. Esta função requer BandContactForm, explicado em forms.py.
+
defband_contact(request):
+ """ A example of form """
+ ifrequest.method=='POST':
+ form=BandContactForm(request.POST)
+ else:
+ form=BandContactForm()
+ returnrender(request,'bands/band_contact.html',{'form':form})
+
+
+
+
Em urls.py pode descomentar a linha a seguir:
+
path('contact/',v.band_contact,name='contact'),
+
+
+
+
A função band_detail retorna todos os membros de cada banda, usando o pk da banda junto com o comando filter em members.
+
defband_detail(request,pk):
+ """ A view of all members by bands. """
+ band=Band.objects.get(pk=pk)
+ members=Member.objects.all().filter(band=band)
+ context={'members':members,'band':band}
+ returnrender(request,'bands/band_detail.html',context)
+
BandCreate e MemberCreate usam o Class Based View para tratar formulário de uma forma mais simplificada usando a classe CreateView. O reverse_lazy serve para tratar a url de retorno de página.
+
As classes a seguir requerem BandForm e MemberForm, explicado em forms.py.
A próxima função requer que você entre numa página somente quando estiver logado.
+
[@login_required](https://docs.djangoproject.com/en/2.2/topics/auth/default/#the-login-required-decorator) é um decorator.
+
login_url='/accounts/login/' é página de erro, ou seja, quando o usuário não conseguiu logar.
+
E render(request, 'bands/protected.html',... é página de sucesso.
+
@login_required(login_url='/accounts/login/')
+defprotected_view(request):
+ """ A view that can only be accessed by logged-in users """
+ returnrender(request,'bands/protected.html',{'current_user':request.user})
+
+
+
+
HttpResponse retorna uma mensagem simples no navegador sem a necessidade de um template.
+
defmessage(request):
+ """ Message if is not authenticated. Simple view! """
+ returnHttpResponse('Access denied!')
+
Para criar novas migrações com base nas alterações feitas nos seus modelos
+
$ python manage.py makemigrations bands
+
Obs: talvez dê erro porque está faltando coisas de forms.py, explicado mais abaixo.
+
Para aplicar as migrações
+
$ python manage.py migrate
+
Para criar um usuário e senha para o admin
+
$ python manage.py createsuperuser
+
Para rodar a aplicação localmente
+
$ python manage.py runserver
+
Após criar um super usuário você pode entrar em localhost:8000/admin
+
Obs: Se você entrar agora em localhost:8000 vai faltar o template home.html. Explicado mais abaixo.
+
shell_plus
+
É o interpretador interativo do python rodando via terminal direto na aplicação do django.
+
Com o comando a seguir abrimos o shell do Django.
+
$ python manage.py shell
+
Mas se você está usando o django-extensions (mostrei como configurá-lo no settings.py), então basta digitar
+
$ python manage.py shell_plus
+
Veja a seguir como inserir dados direto pelo shell.
+
>>>frommyproject.bands.modelsimportBand,Member
+>>># Com django-extensions não precisa fazer o import
+>>># criando o objeto e salvando
+>>>band=Band.objects.create(name='Metallica')
+>>>band.name
+>>>band.can_rock
+>>>band.id
+>>># criando uma instancia da banda a partir do id
+>>>b=Band.objects.get(id=band.id)
+>>># criando uma instancia do Membro e associando o id da banda a ela
+>>>m=Member(name='James Hetfield',instrument='b',band=b)
+>>>m.name
+>>># retornando o instrumento
+>>>m.instrument
+>>>m.get_instrument_display()
+>>>m.band
+>>># salvando
+>>>m.save()
+>>># listando todas as bandas
+>>>Band.objects.all()
+>>># listando todos os membros
+>>>Member.objects.all()
+>>># criando mais uma banda
+>>>band=Band.objects.create(name='The Beatles')
+>>>band=Band.objects.get(name='The Beatles')
+>>>band.id
+>>>b=Band.objects.get(id=band.id)
+>>># criando mais um membro
+>>>m=Member(name='John Lennon',instrument='v',band=b)
+>>>m.save()
+>>># listando tudo novamente
+>>>Band.objects.all()
+>>>Member.objects.all()
+>>>exit()
+
+
+
+
Criando os templates
+
Você pode criar os templates com os comandos a seguir...
Criamos uma customização para o admin onde em members aparece um filtro por bandas.
+
fromdjango.contribimportadmin
+from.modelsimportBand,Member
+
+
+classMemberAdmin(admin.ModelAdmin):
+ """Customize the look of the auto-generated admin for the Member model."""
+ list_display=('name','instrument')
+ list_filter=('band',)
+
+
+admin.site.register(Band)# Use the default options
+admin.site.register(Member,MemberAdmin)# Use the customized options
+
+
+
+
Carregando dados de um CSV
+
Vamos baixar alguns arquivos para criar os dados no banco a partir de um CSV.
The Rust community requested feedback last year for where the language should go
+in 2018, and now they are running it again for 2019.
+Last year I was too new in Rust to organize a blog post,
+but after an year using it I feel more comfortable writing this!
Anything that involves doing science using computers counts as
+scientific programming. It includes from embedded software
+running on satellites to climate models
+running in supercomputers, from shell scripts running tools in a pipeline to
+data analysis using notebooks.
+
It also makes the discussion harder, because it's too general! But it is very
+important to keep in mind, because scientists are not your regular user: they
+are highly qualified in their field of expertise, and they are also pushing the
+boundaries of what we know (and this might need flexibility in their tools).
+
In this post I will be focusing more in two areas: array computing (what most
+people consider 'scientific programming' to be) and "data structures".
+
Array computing
+
This one is booming in the last couple of years due to industry interest in data
+sciences and deep learning (where they will talk about tensors instead of arrays),
+and has its roots in models running in supercomputers (a field where Fortran is
+still king!). Data tends to be quite regular (representable with matrices) and
+amenable to parallel processing.
+
A good example is the SciPy stack in Python, built on top of NumPy and SciPy.
+The adoption of the SciPy stack (both in academia and industry) is staggering,
+and many alternative implementations try to provide a NumPy-like API to try to
+capture its mindshare.
+
This is the compute-intensive side science (be it CPU or GPU/TPU), and also the kind
+of data that pushed CPU evolution and is still very important in defining policy
+in scientific computing funding (see countries competing for the largest
+supercomputers and measuring performance in floating point operations per second).
+
Data structures for efficient data representation
+
For data that is not so regular the situation is a bit different. I'll use
+bioinformatics as an example: the data we get out of nucleotide sequencers is usually
+represented by long strings (of ACGT), and algorithms will do a lot of string processing
+(be it building string-overlap graphs for assembly, or searching for substrings
+in large collections). This is only one example: there are many analyses that
+will work with other types of data, and most of them don't have a
+universal data representation as in the array computing case.
+
This is the memory-intensive science, and it's hard to measure performance in
+floating point operations because... most of the time you're not even using
+floating point numbers. It also suffers from limited data locality (which is
+almost a prerequisite for compute-intensive performance).
+
High performance core, interactive API
+
There is something common in both cases: while performance-intensive
+code is implemented in C/C++/Fortran, users usually interact with the API from
+other languages (especially Python or R) because it's faster to iterate and
+explore the data, and many of the tools already available in these languages are
+very helpful for these tasks (think Jupyter/pandas or RStudio/tidyverse).
+These languages are used to define the computation, but it is a lower-level core
+library that drives it (NumPy or Tensorflow follow this idea, for example).
+
How to make Rust better for science?
+
The biggest barrier to learning Rust is the ownership model, and while we can
+agree it is an important feature it is also quite daunting for newcomers,
+especially if they don't have previous programming experience and exposure to
+what bugs are being prevented. I don't see it being the first language we teach
+to scientists any time soon, because the majority of scientists are not system
+programmers, and have very different expectations for a programming language.
+That doesn't mean that they can't benefit from Rust!
+
Rust is already great for building the performance-intensive parts,
+and thanks to Cargo it is also a better alternative for sharing this code around,
+since they tend to get 'stuck' inside Python or R packages.
+And the 'easy' approach of vendoring C/C++ instead of having packages make it
+hard to keep track of changes and doesn't encourage reusable code.
+
And, of course, if this code is Rust instead of C/C++ it also means that Rust
+users can use them directly, without depending on the other languages. Seems
+like a good way to bootstrap a scientific community in Rust =]
+
What I would like to see in 2019?
+
An attribute proc-macro like #[wasm_bindgen] but for FFI
+
While FFI is an integral part of Rust goals (interoperability with C/C++), I
+have serious envy of the structure and tooling developed for WebAssembly! (Even
+more now that it works in stable too)
+
We already have #[no_mangle] and pub extern "C", but they are quite
+low-level. I would love to see something closer to what wasm-bindgen does,
+and define some traits (like IntoWasmAbi) to make it easier to
+pass more complex data types through the FFI.
+
I know it's not that simple, and there are different design restrictions than
+WebAssembly to take into account... The point here is not having the perfect
+solution for all use cases, but something that serves as an entry point and helps
+to deal with the complexity while you're still figuring out all the quirks and
+traps of FFI. You can still fallback and have more control using the lower-level
+options when the need rises.
+
More -sys and Rust-like crates for interoperability with the larger ecosystems
+
There are new projects bringing more interoperability to dataframes and tensors.
+While this ship has already sailed and they are implemented in C/C++,
+it would be great to be a first-class citizen,
+and not reinvent the wheel.
+(Note: the arrow project already have pretty good Rust support!)
+
In my own corner (bioinformatics), the Rust-bio community is doing a
+great job of wrapping useful libraries in C/C++ and exposing them to
+Rust (and also a shout-out to 10X Genomics for doing this work for
+other tools while also contributing to Rust-bio!).
+
More (bioinformatics) tools using Rust!
+
We already have great examples like finch and yacrd,
+since Rust is great for single binary distribution of programs.
+And with bioinformatics focusing so much in independent tools chained together in workflows,
+I think we can start convincing people to try it out =]
+
A place to find other scientists?
+
Another idea is to draw inspiration from rOpenSci and have a Rust equivalent,
+where people can get feedback about their projects and how to better integrate it with other crates.
+This is quite close to the working group idea,
+but I think it would serve more as a gateway to other groups,
+more focused on developing entry-level docs and bringing more scientists to the
+community.
+
Final words
+
In the end, I feel like this post ended up turning into my 'wishful TODO list'
+for 2019, but I would love to find more people sharing these goals (or willing
+to take any of this and just run with it, I do have a PhD to finish! =P)
Quando estamos escrevendo um código qualquer, possivelmente
+a expressão que mais utilizamos é o if. Para qualquer
+tarefas que buscamos automatizar ou problemas que buscamos
+resolver, sempre acabamos caindo em lógicas como "Se isso
+acontecer, então faça aquilo, senão faça aquele outro...".
+
Quando estamos falando de ações a serem executadas, pessoalmente
+gosto da forma com que o código fica organizado em python quando
+usamos este tipo de condições, por exemplo:
Graças a indentação e ao espaçamento, vemos onde onde começa e/ou
+termina o bloco executado caso a varável vencer_o_thanos seja
+True. Quanto mais if's você aninhar, mais bonito seu
+código fica e em momento algum o mesmo se torna mais confuso
+(ao menos, não deveria se tornar). Entretanto, sempre fico
+extremamente incomodado quando tenho de escrever um bloco apenas
+marcar uma variável, como por exemplo:
Por isso, para trabalhar com variáveis que possuem um valor condicional,
+gosto sempre de trabalhar com expressões condicionais, ou como costumam
+ser chamadas, operadores ternários.
+
Operadores ternários são todos os operadores que podem receber três
+operandos. Como as expressões condicionais costumam ser os operadores
+ternários mais populares nas linguagens em que aparecem, acabamos por
+associar estes nomes e considerar que são a mesma coisa. Cuidado ao tirar
+este tipo de conclusão, mesmo que toda vogal esteja no alfabeto, o
+alfabeto não é composto apenas por vogais.
+
A estrutura de uma expressão condicional é algo bem simples, veja só:
Resumidamente, teremos um valor seguido de uma condição e por fim seu
+valor caso a condição seja falsa. Pessoalmente acredito que apesar de um
+pouco diferente, essa forma de escrita para casos como o exemplificado acima
+é muito mais clara, mais explicita.
+
Se você fizer uma tradução literal das booleanas utilizadas no primeiro exemplo,
+lerá algo como paz é verdadeira caso vencer_o_thanos, caso contrário é Falsa.
+já o segundo exemplo fica mais claro ainda, pois lemos algo como
+tipo_de_x é par caso o resto da divisão de x por 2 seja 0, se não, tipo_de_x é impar..
+
Interpretar código dessa forma pode ser estranho para um programador. Interpretar
+uma abertura de chave ou uma indentação já é algo mas natural. Todavia, para aqueles
+que estão começando, o raciocínio ocorre de forma muito mais parecida com a descrita
+acima. Espero que tenham gostado do texto e que esse conhecimento lhes seja útil.
Today I got a pleasant surprise: Olga Botvinnik posted on Twitter
+about a poster she is presenting at the Beyond the Cell Atlas conference
+and she name-dropped a bunch of people that helped her. The cool thing? They
+are all open source developers, and Olga interacted thru GitHub to askforfeatures,
+report bugs and even submitpullrequests.
+
That's what open science is about: collaboration, good practices, and in the end coming up
+with something that is larger than each individual piece. Now sourmash is better,
+bamnostic is better, reflow is better. I would like to see this becoming more and
+more common =]
A quick announcement: I wrote a Rust implementation of ntHash and published
+it in crates.io. It implements an Iterator to take advantage of the
+rolling properties of ntHash which make it so useful in bioinformatics (where
+we work a lot with sliding windows over sequences).
More info in the docs, and if you want an optimization versioning bug
+discussion be sure to check the ntHash bug? repo,
+which has a (slow) Python implementation and a pretty nice analysis notebook.
sourmash calculates MinHash signatures for genomic datasets,
+meaning we are reducing the data (via subsampling) to a small
+representative subset (a signature) capable of answering one question:
+how similar is this dataset to another one? The key here is that a dataset with
+10-100 GB will be reduced to something in the megabytes range, and two approaches
+for doing that are:
+
+
The user install our software in their computer.
+ This is not so bad anymore (yay bioconda!), but still requires knowledge
+ about command line interfaces and how to install all this stuff. The user
+ data never leaves their computer, and they can share the signatures later
+ if they want to.
+
Provide a web service to calculate signatures. In this case no software
+ need to be installed, but it's up to someone (me?) to maintain a server running with
+ an API and frontend to interact with the users. On top of requiring more
+ maintenance, another drawback is that
+ the user need to send me the data, which is very inefficient network-wise
+ and lead to questions about what I can do with their raw data (and I'm not
+ into surveillance capitalism, TYVM).
+
+
But... what if there is a third way?
+
What if we could keep the frontend code from the web service (very
+user-friendly) but do all the calculations client-side (and avoid the network
+bottleneck)? The main hurdle
+here is that our software is implemented in Python (and C++), which are not
+supported in browsers. My first solution was to write the core features of
+sourmash in JavaScript, but that quickly started hitting annoying things
+like JavaScript not supporting 64-bit integers. There is also the issue of
+having another codebase to maintain and keep in sync with the original sourmash,
+which would be a relevant burden for us. I gave a lab meeting about this
+approach, using a drag-and-drop UI as proof of concept. It did work but it
+was finicky (dealing with the 64-bit integer hashes is not fun). The good thing
+is that at least I had a working UI for further testing1
+
In "Oxidizing sourmash: Python and FFI" I described my road to learn Rust,
+but something that I omitted was that around the same time the WebAssembly
+support in Rust started to look better and better and was a huge influence in
+my decision to learn Rust. Reimplementing the sourmash C++ extension in Rust and
+use the same codebase in the browser sounded very attractive,
+and now that it was working I started looking into how to use the WebAssembly
+target in Rust.
WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine.
+Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust,
+enabling deployment on the web for client and server applications.
+
+
You can write WebAssembly by hand, but the goal is to have it as lower level
+target for other languages. For me the obvious benefit is being able to use
+something that is not JavaScript in the browser, even though the goal is not to replace
+JS completely but complement it in a big pain point: performance. This also
+frees JavaScript from being the target language for other toolchains,
+allowing it to grow into other important areas (like language ergonomics).
With the Rust implementation in place and with all tests working on sourmash, I
+added the finishing touches using wasm-bindgen and built an NPM package using
+wasm-pack: sourmash is a Rust codebase compiled to WebAssembly and ready
+to use in JavaScript projects.
Since I already had the working UI from the previous PoC, I refactored the code
+to use the new WebAssembly module and voilà! It works!2.
+3
+But that was the demo from a year ago with updated code and I got a bit
+better with frontend development since then, so here is the new demo:
+
+
sourmash + Wasm
+
+
Drag & drop a FASTA or FASTQ file here to calculate the sourmash signature.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
For the source code for this demo, check the sourmash-wasm directory.
+
Next steps
+
The proof of concept works, but it is pretty useless right now.
+I'm thinking about building it as a Web Component and making it really easy
+to add to any webpage4.
+
Another interesting feature would be supporting more input formats (the GMOD
+project implemented a lot of those!), but more features are probably better
+after something simple but functional is released =P
+
Next time!
+
Where we will go next? Maybe explore some decentralized web technologies like
+IPFS and dat, hmm? =]
even if horrible, I
+need to get some design classes =P ↩
the first version
+of this demo only worked in Chrome because they implemented the BigInt proposal,
+which is not in the official language yet. The funny thing is that BigInt would
+have made the JS implementation of sourmash viable, and I probably wouldn't have
+written the Rust implementation =P.
+Turns out that I didn't need the BigInt support if I didn't expose any 64-bit
+integers to JS, and that is what I'm doing now. ↩
Along the way I ended up writing a new FASTQ parser... because it wouldn't
+be bioinformatics if it didn't otherwise, right? =P ↩
or maybe a React component? I really would like to
+have something that works independent of framework, but not sure what is the
+best option in this case... ↩
I think the first time I heard about Rust was because Frank Mcsherry chose it to
+write a timely dataflow implementation.
+Since then it started showing more and more in my news sources,
+leading to Armin Ronacher publishing a post in the Sentry blog last November about
+writing Python extensions in Rust.
+
Last December I decided to give it a run:
+I spent some time porting the C++ bits of sourmash
+to Rust.
+The main advantage here is that it's a problem I know well,
+so I know what the code is supposed to do and can focus on figuring out
+syntax and the mental model for the language.
+I started digging into the symbolic codebase and understanding what they did,
+and tried to mirror or improve it for my use cases.
+
(About the post title: The process of converting a codebase to Rust is referred as "Oxidation" in
+the Rust community, following the codename Mozilla chose for the process of
+integrating Rust components into the Firefox codebase.
+1
+Many of these components were tested and derived in Servo, an experimental
+browser engine written in Rust, and are being integrated into Gecko,
+the current browser engine (mostly written in C++).)
+
Why Rust?
+
There are other programming languages more focused on scientific software
+that could be used instead, like Julia2. Many programming languages start from a
+specific niche (like R and statistics,
+or Maple and mathematics) and grow into larger languages over time.
+While Rust goal is not to be a scientific language,
+its focus on being a general purpose language allows a phenomenon similar
+to what happened with Python, where people from many areas pushed the
+language in different directions (system scripting, web development,
+numerical programming...) allowing developers to combine all these things
+in their systems.
+
But by far my interest in Rust is for the many best practices it brings to the default experience:
+integrated package management (with Cargo),
+documentation (with rustdoc), testing and benchmarking.
+It's understandable that older languages like C/C++ need
+more effort to support some of these features (like modules and an unified
+build system), since they are designed by standard and need to keep backward
+compatibility with codebases that already exist.
+Nonetheless, the lack of features increase the effort needed to have good
+software engineering practices, since you need to choose a solution that might
+not be compatible with other similar but slightly different options,
+leading to fragmentation and increasing the impedance to use these features.
+
Another big reason is that Rust doesn't aim to completely replace what already
+exists, but complement and extend it. Two very good talks about how to do this,
+one by Ashley Williams, another by E. Dunham.
+
Converting from a C++ extension to Rust
+
The current implementation of the core data structures in sourmash is in a
+C++ extension wrapped with Cython. My main goals for converting the code are:
+
+
+
support additional languages and platforms. sourmash is available as a Python
+ package and CLI, but we have R users in the lab that would benefit from having
+ an R package, and ideally we wouldn't need to rewrite the software every time
+ we want to support a new language.
+
+
+
reducing the number of wheel packages necessary (one for each OS/platform).
+
+
+
in the long run, use the Rust memory management concepts (lifetimes, borrowing)
+to increase parallelism in the code.
+
+
+
Many of these goals are attainable with our current C++ codebase, and
+"rewrite in a new language" is rarely the best way to solve a problem.
+But the reduced burden in maintenance due to better tooling,
+on top of features that would require careful planning to execute
+(increasing the parallelism without data races) while maintaining compatibility
+with the current codebase are promising enough to justify this experiment.
+
+
Cython provides a nice gradual path to migrate code from Python to C++,
+since it is a superset of the Python syntax. It also provides low overhead
+for many C++ features, especially the STL containers, with makes it easier
+to map C++ features to the Python equivalent.
+For research software this also lead to faster exploration of solutions before
+having to commit to lower level code, but without a good process it might also
+lead to code never crossing into the C++ layer and being stuck in the Cython
+layer. This doesn't make any difference for a Python user, but it becomes
+harder from users from other languages to benefit from this code (since your
+language would need some kind of support to calling Python code, which is not
+as readily available as calling C code).
+
Depending on the requirements, a downside is that Cython is tied to the CPython API,
+so generating the extension requires a development environment set up with
+the appropriate headers and compiler. This also makes the extension specific
+to a Python version: while this is not a problem for source distributions,
+generating wheels lead to one wheel for each OS and Python version supported.
+
The new implementation
+
This is the overall architecture of the Rust implementation:
+
+It is pretty close to what symbolic does,
+so let's walk through it.
+
The Rust code
+
If you take a look at my Rust code, you will see it is very... C++. A lot of the
+code is very similar to the original implementation, which is both a curse and a
+blessing: I'm pretty sure that are more idiomatic and performant ways of doing
+things, but most of the time I could lean on my current mental model for C++ to
+translate code. The biggest exception was the merge function, were I was doing
+something on the C++ implementation that the borrow checker didn't like.
+Eventually I found it was because it couldn't keep track of the lifetime
+correctly and putting braces around it fixed the problem,
+which was both an epiphany and a WTF moment. Here is an example that triggers
+the problem, and the solution.
+
"Fighting the borrow checker" seems to be a common theme while learning Rust,
+but the compiler really tries to help you to understand what is happening and
+(most times) how to fix it. A lot of people grow to hate the borrow checker,
+but I see it more as a 'eat your vegetables' situation: you might not like it
+at first, but it's better in the long run. Even though I don't have a big
+codebase in Rust yet, it keeps you from doing things that will come back to bite
+you hard later.
+
Generating C headers for Rust code: cbindgen
+
With the Rust library working, the next step was taking the Rust code and generate C headers describing the
+functions and structs we expose with the #[no_mangle] attribute in Rust
+(these are defined in the ffi.rs module in sourmash-rust).
+This attribute tells the Rust compiler to generate names that are compatible
+with the C ABI, and so can be called from other languages that implement FFI
+mechanisms. FFI (the foreign function interface) is quite low-level,
+and pretty much defines things that C can represent: integers, floats, pointers
+and structs. It doesn't support higher level concepts like objects or generics,
+so in a sense it looks like a feature funnel.
+This might sound bad, but ends up being something that other languages can
+understand without needing too much extra functionality in their runtimes,
+which means that most languages have support to calling code through an FFI.
+
Writing the C header by hand is possible, but is very error prone.
+A better solution is to use cbindgen,
+a program that takes Rust code and generate a C header file automatically.
+cbindgen is developed primarily to generate the C headers for webrender,
+the GPU-based renderer for servo,
+so it's pretty likely that if it can handle a complex codebase it will work
+just fine for the majority of projects.
+
Interfacing with Python: CFFI and Milksnake
+
Once we have the C headers, we can use the FFI to
+call Rust code in Python. Python has a FFI module in the standard library: ctypes,
+but the Pypy developers also created CFFI, which has more features.
+
The C headers generated by cbindgen can be interpreted by CFFI to generate
+a low-level Python interface for the code. This is the equivalent of declaring
+the functions/methods and structs/classes in a pxd file (in the Cython
+world): while the code is now usable in Python, it is not well adapted to
+the features and idioms available in the language.
+
Milksnake is the package developed by Sentry that takes care of running cargo
+for the Rust compilation and generating the CFFI boilerplate,
+making it easy to load the low-level CFFI bindings in Python.
+With this low-level binding available we can now write something more Pythonic
+(the pyx file in Cython), and I ended up just renaming the _minhash.pyx file
+back to minhash.py and doing one-line fixes to replace the Cython-style code
+with the equivalent CFFI calls.
+
All of these changes should be transparent to the Python code, and to guarantee
+that I made sure that all the current tests that we have (both for the Python
+module and the command line interface) are still working after the changes.
+It also led to finding some quirks in the implementation,
+and even improvements in the current C++ code (because we were moving a lot of
+data from C++ to Python).
+
Where I see this going
+
It seems it worked as an experiment,
+and I presented a poster at GCCBOSC 2018 and SciPy 2018 that
+was met with excitement by many people.
+Knowing that it is possible,
+I want to reiterate some points why Rust is pretty exciting for bioinformatics
+and science in general.
+
Bioinformatics as libraries (and command line tools too!)
+
Bioinformatics is an umbrella term for many different methods, depending on
+what analysis you want to do with your data (or model).
+In this sense, it's distinct from other scientific areas where it is possible
+to rely on a common set of libraries (numpy in linear algebra, for example), since a
+library supporting many disjoint methods tend to grow too big and hard to
+maintain.
+
The environment also tends to be very diverse, with different languages being
+used to implement the software. Because it is hard to interoperate,
+the methods tend to be implemented in command line programs that are stitched
+together in pipelines, a workflow describing how to connect the input and output of many different tools to
+generate results.
+Because the basic unit is a command-line tool,
+pipelines tend to rely on standard operating system abstractions like
+files and pipes to make the tools communicate with each other. But since tools
+might have input requirements distinct from what the previous tool provides,
+many times it is necessary to do format conversion or other adaptations to make the
+pipeline work.
+
Using tools as blackboxes, controllable through specific parameters at the
+command-line level, make exploratory analysis and algorithm reuse harder:
+if something needs to be investigated the user needs to resort to perturbations
+of the parameters or the input data, without access to the more feature-rich and
+meaningful abstraction happening inside the tool.
+
Even if many languages are used for writing the software, most of the time there
+is some part written in C or C++ for performance reasons, and these tend to be
+the core data structures of the computational method. Because it is not easy to
+package your C/C++ code in a way that other people can readily use it,
+most of this code is reinvented over and over again, or is copy and pasted into
+codebases and start diverging over time. Rust helps solve this problem with the
+integrated package management, and due to the FFI it can also be reused inside
+other programs written in other languages.
+
sourmash is not going to be Rust-only and abandon Python,
+and it would be crazy to do so when it has so many great exploratory tools
+for scientific discovery. But now we can also use our method in other languages
+and environment, instead of having our code stuck in one language.
+
Don't rewrite it all!
+
I could have gone all the way and rewrite sourmash in Rust3, but it would be incredibly disruptive for
+the current sourmash users and it would take way longer to pull off. Because
+Rust is so focused in supporting existing code, you can do a slow transition and
+reuse what you already have while moving into more and more Rust code.
+A great example is this one-day effort by Rob Patro to bring CQF (a C
+codebase) into Rust, using bindgen (a generator of C bindings for Rust).
+Check the Twitter thread for more =]
+
Good scientific citizens
+
There is another MinHash implementation already written in Rust, finch.
+Early in my experiment I got an email from them asking if I wanted to work
+together, but since I wanted to learn the language I kept doing my thing. (They
+were totally cool with this, by the way). But the fun thing is that Rust has a
+pair of traits called From and Into that you can implement for your
+type, and so I did that and now we can have interoperable
+implementations. This synergy allows finch to use sourmash methods,
+and vice versa.
+
Maybe this sounds like a small thing, but I think it is really exciting. We can
+stop having incompatible but very similar methods, and instead all benefit from
+each other advances in a way that is supported by the language.
+
Next time!
+
Turns out Rust supports WebAssembly as a target,
+so... what if we run sourmash in the browser?
+That's what I'm covering in the next blog post,
+so stay tuned =]
The creator of the language is known to keep making up different
+explanations for the name of the language,
+but in this case "oxidation" refers to the chemical process that creates
+rust, and rust is the closest thing to metal (metal being the hardware).
+There are many terrible puns in the Rust community. ↩
Even more now that it hit 1.0,
+it is a really nice language ↩
Apesar de termos muitas formas de enviarmos arquivos para servidores hoje em dia, como por exemplo o scp e rsync, podemos usar o python com seus módulos built-in para enviar arquivos a servidores usando struct para serializar os dados e socket para criar uma conexão cliente/servidor.
+
Struct
+
O módulo struct é usado para converter bytes no python em formatos do struct em C.
+Com ele podemos enviar num único conjunto de dados o nome de um arquivo e os bytes referentes ao seus dados.
+
Struct também é utilizado para serializar diversos tipos de dados diferentes, como bytes, inteiros, floats além de outros, no nosso caso usaremos apenas bytes.
+
Vamos criar um arquivo para serializar.
+
!echo"Upload de arquivos com sockets e struct\nCriando um arquivo para serializar.">arquivo_para_upload
+
+
+
+
Agora em posse de um arquivo vamos criar nossa estrutura de bytes para enviar.
Por padrão, struct usa caracteres no início da sequência dos dados para definir a ordem dos bytes, tamanho e alinhamento dos bytes nos dados empacotados.
+Esses caracteres podem ser vistos na seção 7.1.2.1 da documentação.
+Como não definimos, será usado o @ que é o padrão.
definimos que nossa estrutura serializada seria de dois conjuntos de caracteres, a primeira com o tamanho do nome do arquivo, e a segunda com o tamanho total dos dados lidos em
+
dados_arquivo=arq.read()
+
+
+
+
Se desempacotarmos os dados, teremos uma lista com o nome do arquivo e os dados lidos anteriormente.
+
serializar.unpack(dados_upload)[0]
+
+
+
+
b'arquivo_para_upload'
+
+
+
+
serializar.unpack(dados_upload)[1]
+
+
+
+
b'Upload de arquivos com sockets e struct\\nCriando um arquivo para serializar.\n'
+
+
+
+
Agora de posse dos nossos dados já serializados, vamos criar um cliente e um servidor com socket para transferirmos nosso arquivo.
+
Socket
+
O modulo socket prove interfaces de socket BSD, disponiveis em praticamente todos os sistemas operacionais.
+
Familias de sockets
+
Diversas famílias de sockets podem ser usadas para termos acessos a objetos que nos permitam fazer chamadas de sistema.
+Mais informações sobre as famílias podem ser encontradas na seção 18.1.1 da documentação. No nosso exemplo usaremos a AF_INET.
+
AF_INET
+
AF_INET precisa basicamente de um par de dados, contendo um endereço IPv4 e uma porta para ser instanciada.
+Para endereços IPv6 o modulo disponibiliza o AF_INET6
+
Constante [SOCK_STREAM]
+
As constantes representam as famílias de sockets, como a constante AF_INET e os protocolos usados como parâmetros para o modulo socket.
+Um dos protocolos mais usados encontrados na maioria dos sistemas é o SOCK_STREAM.
+
Ele é um protocolo baseado em comunicação que permite que duas partes estabeleçam uma conexão e conversem entre si.
+
Servidor e cliente socket
+
Como vamos usar um protocolo baseado em comunicação, iremos construir o servidor e cliente paralelamente para um melhor entendimento.
+
+
Servidor
+
+
Para esse exemplo eu vou usar a porta 6124 para o servidor, ela esta fora da range reservada pela IANA para sistemas conhecidos, que vai de 0-1023.
+
Vamos importar a bilioteca socket e definir um host e porta para passarmos como parametro para a constante AF_INET.
Agora usaremos o metodo bind para criarmos um ponto de conexão para nosso cliente. Esse método espera por uma tupla contento o host e porta como parâmetros.
+
sock.bind((host,porta))
+
+
+
+
Agora vamos colocar nosso servidor socket em modo escuta com o metodo listen. Esse método recebe como parâmetro um número inteiro (backlog) definindo qual o tamanho da fila que será usada para receber pacotes SYN até dropar a conexão. Usaremos um valor baixo o que evita SYN flood na rede. Mais informações sobre backlog podem ser encontradas na RFC 7413.
+
sock.listen(5)
+
+
+
+
Agora vamos colocar o nosso socket em um loop esperando por uma conexão e um início de conversa. Pra isso vamos usar o metodo accept que nos devolve uma tupla, onde o primeiro elemento é um novo objeto socket para enviarmos e recebermos informações, e o segundo contendo informações sobre o endereço de origem e porta usada pelo cliente.
+
Vamos criar um diretório para salvar nosso novo arquivo.
+
!mkdirarquivos_recebidos
+
+
+
+
Os dados são enviados sempre em bytes. Leia os comentários
+
whileTrue:
+ novo_sock,cliente=sock.accept()
+ withnovo_sock:# Caso haja uma nova conexão
+ ouvir=novo_sock.recv(1024)# Colocamos nosso novo objeto socket para ouvir
+ ifouvir!=b"":# Se houver uma mensagem...
+ """
+ Aqui usaremos os dados enviados na mensagem para criar nosso serielizador.
+
+ Com ele criado poderemos desempacotar os dados assim que recebermos.
+ Veja no cliente mais abaixo qual a primeira mensagem enviada.
+ """
+ mensagem,nome,dados=ouvir.decode().split(":")
+ serializar=struct.Struct("{}s {}s".format(len(nome.split()[0]),int(dados.split()[0])))
+ novo_sock.send("Pode enviar!".encode())# Enviaremos uma mensagem para o cliente enviar os dados.
+ dados=novo_sock.recv(1024)# Agora iremos esperar por eles.
+ nome,arquivo=serializar.unpack(dados)# Vamos desempacotar os dados
+ """
+ Agora enviamos uma mensagem dizendo que o arquivo foi recebido.
+
+ E iremos salva-lo no novo diretório criado.
+ """
+ novo_sock.send("Os dados do arquivo {} foram enviados.".format(nome.decode()).encode())
+ withopen("arquivos_recebidos/{}".format(nome.decode()),'wb')asnovo_arquivo:
+ novo_arquivo.write(arquivo)
+ print("Arquivo {} salvo em arquivos_recebidos.".format(nome.decode()))
+
+ Arquivoarquivo_para_uploadsalvoemarquivos_recebidos.
+
+
+
+
+
Cliente
+
+
Nosso cliente irá usar o metodo connect para se conectar no servidor e a partir dai começar enviar e receber mensagens. Ele também recebe como parâmetros uma tupla com o host e porta de conexão do servidor.
+
host='127.0.0.1'
+porta=6124
+sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)# Cria nosso objeto socket
+sock.connect((host,porta))
+sock.send("Enviarei um arquivo chamado: {} contendo: {} bytes".format(
+ arquivo,len(dados_arquivo)).encode())# Enviamos a mensagem com o nome e tamanho do arquivo.
+ouvir=sock.recv(1024)# Aguardamos uma mensagem de confirmação do servidor.
+ifouvir.decode()=="Pode enviar!":
+ sock.send(dados_upload)# Enviamos os dados empacotados.
+ resposta=sock.recv(1024)# Aguardamos a confirmação de que os dados foram enviados.
+ print(resposta.decode())
+
+ Osdadosdoarquivoarquivo_para_uploadforamenviados.
+
+
+
+
Agora podemos checar nossos arquivos e ver se eles foram salvos corretamente.
Muitos administradores de redes e sysadmins encontram problemas de conectividade nos ambientes que administram e por muitas vezes o problema é um simples IP duplicado causando todo mal estar. Agora veremos como usar o scapy e defaultdict da lib collections para monitorar esses IPs.
+
Scapy
+
O Scapy é uma poderosa biblioteca de manipulação de pacotes interativa, com abrangencia a uma enorme quantidade de protocolos provenientes da suite TCP/IP.
+Mais informações sobre o scpay pode ser encontrada na documentação oficial.
+Nesse caso em especifico iremos utilizar do scapy a metaclasse ARP e a função sniff.
+
fromscapy.allimportARP,sniff
+
+
+
+
sniff
+
Vamos usar a função sniff para monitorar os pacotes que trafegam na rede usando o protocolo ARP.
+Pra isso vamos utilizar dela quatro parametros basicos:
prn, chama uma função para ser aplicada a cada pacote capturado pelo sniff.
+
+
+
filter, irá filtrar todos os pacotes que contiverem o protocolo ARP.
+
+
+
iface, determina a interface de rede que será monitorada.
+
+
+
timeout, irá determinar que nosso monitoramento da rede se dara por 60 segundos.
+
+
+
ARP
+
ARP é uma metaclasse de pacotes com dados sobre o protocolo arp pertencente a camada de enlace de dados.
+Iremos utilizar essa metaclasse para filtrar os pacotes com informações de pacotes com respostas a requisições arp. (opcode == 2 [is at])
+As informações sobre o protocolo ARP podem serm encontradas na rfc826 no site do IETF.
+
collections.defaultdict
+
defaultdict é uma subclasse de dict que prove uma instancia de variavel para a chamada de uma chave inexistente.
Basicamente nossa função irá monitorar por um certo tempo o trafego de pacotes pela rede adicionar a nossa variavel list_ips o endereço ou endereços MAC encontrados.
+
Definindo a função que será passada como parametro para o sniff.
+
Para cada pacote capturado pela função sniff, será checado se o opcode corresponde a um response do protocolo arp.
+Caso seja, sera adicionado a nossa defaultdict.
+
defpacotes(pacote):
+ """Checa se o valor do opcode dentro do protocolo arp é igual a 2."""
+ ifpacote[ARP].op==2:
+ # Se for adiciona o ip de origem e seu mac à dict list_ips
+ list_ips[pacote[ARP].psrc].add(pacote[ARP].hwsrc)
+
+
+
+
Limpando a tabela arp
+
Para que seja feita novas requisições arp, iremos limpar nossa tabela arp e iniciar o monitoramento da rede.
+Pra isso iremos usar o comando arp, dentro do shell do sistema. (Como uso FreeBSD vou definir uma função chamando um comando pelo csh)
+
importos
+os.system('which arp')
+/usr/sbin/arp
+
+
+
+
Com posse do caminho do comando arp, irei definir uma função que limpe a tabela e inicie o monitore a rede por 60 segundos.
+
defmonitorar(interface):
+ """
+ O comando arp no FreeBSD usa os parametros:
+
+ -d para deletar as entradas
+ -i para declarar a interface
+ -a para representar todas entradas a serem deletas.
+ """
+ cmd="/usr/sbin/arp -d -i {} -a".format(interface)
+ os.system(cmd)
+ sniff(prn=pacotes,filter="arp",iface=interface,timeout=10)
+
+
+
+
E por ultimo chamar a função de monitoramento.
+No meu caso eu vou monitorar a interface em0.
Eu uso um script rodando nos switchs e gateway da rede que me enviam mensagens assim que ips duplicados são encontrados na rede.
+Também da pra usar o arping do scpay para fazer as requisições arp e coletar os responses.
+C++ pode ter a fama de ser uma linguagem com complexidade comparável à legislação tributária brasileira, mas ao mesmo tempo é uma linguagem extremamente poderosa, podendo ser usada tanto desde microcontroladores até super computadores, sem contar satélites espaciais. Tamanha complexidade pode assustar quem vem de linguagens mais simples como Python (malloc? rvalue references?). Uma ajuda … Continuar lendo →
Geralmente, quando se fala em SGBDs OpenSource, a primeira resposta que se ouve é MySQL/MariaDB. Eu sempre torço meu nariz para respostas como essa… Implicancia pessoal? Talvez um pouco, mas existem muitos fundamentos…
+Imagine que você tenha uma lista com as URLs extraídas de uma página web e queira eliminar as duplicatas da mesma. Transformar a lista em um conjunto (set) talvez seja a forma mais comum de se fazer isso. Tipo assim: Porém, observe que perdemos a ordem original dos elementos. Esse é um efeito colateral indesejado […]
Repare que no exemplo anterior tivemos que definir os métodos get(), post(), put() e delete(). Podemos reduzir ainda mais esse código com o uso de mixins.
O uso de requests e responses torna nossa api mais flexível. A funcionalidade principal do objeto Request é o atributo request.data, que é semelhante ao request.POST, mas é mais útil para trabalhar com APIs.
+
Objeto Response
+
Introduzimos aqui um objeto Response, que é um tipo de TemplateResponse que leva conteúdo não renderizado e usa a negociação de conteúdo para determinar o tipo de conteúdo correto para retornar ao cliente.
+
returnResponse(data)# Renderiza para o tipo de conteúdo conforme solicitado pelo cliente.
+
+
+
+
Repare também no uso de status code pré definidos, exemplo: status.HTTP_400_BAD_REQUEST.
+
E usamos o decorador @api_view para trabalhar com funções. Ou APIView para classes.
O que são iteráveis? Basicamente e a grosso modo, iteráveis em python são todos os objetos que implementam o método __getitem__ ou __iter__. Beleza, vamos partir do simples.
+
Quase todos os tipos de dados em python são iteráveis, por exemplo: listas, strings, tuplas, dicionários, conjuntos, etc...
Em python, o comando for nos fornece um iterador implícito. O que? Não entendi.
+
O laço for em python itera em cada elemento da sequência. Como no exemplo, o for, ou foreach, no caso vai passando por cada elemento da sequência. Não é necessária a implementação de um index como na linguagem C, onde a iteração é explícita:
+
for(i=0;i>10;i++){
+ sequencia[i];
+}
+
+
+
+
2.1 __getitem__
+
O padrão de projeto iterator em python já vem implementado por padrão, como já foi dito antes. Basta que um objeto tenha os métodos __iter__ ou __getitem__ para que um laço possa ser utilizado.
+
Vamos exemplificar:
+
classiteravel:
+ """
+ Um objeto que implementa o `__getitem__` pode ser acessado por posição
+ """
+ def__init__(self,sequencia):
+ self.seq=sequencia
+
+ def__getitem__(self,posicao):
+ """
+ Por exemplo, quando tentamos acessar um elemento da sequência usando
+ slice:
+ >>> iteravel[2]
+
+ O interpretador python chama o __getitem__ do objeto e nos retorna
+ a posição solicitada
+
+ um exemplo:
+ IN: lista = [1, 2, 3, 4]
+ IN: lista[0]
+ OUT: 1
+ """
+ returnself.seq[posicao]
+
+
+
+
Então, pode-se compreender, sendo bem rústico, que todos os objetos que implementam __getitem__ são iteráveis em python.
+
2.2 __iter__
+
Agora os objetos que implementam __iter__ tem algumas peculiaridades. Por exemplo, quando o iterável (vamos pensar no for) chamar a sequência, ela vai pedir o __iter__ que vai retornar uma instância de si mesmo para o for e ele vai chamar o __next__ até que a exceção StopIteration aconteça.
+
Uma classe que implementa __iter__:
+
classiteravel:
+ def__init__(self,sequencia):
+ self.data=sequencia
+
+ def__next__(self):
+ """
+ Neste caso, como o método pop do objeto data é chamado
+ (vamos pensar em uma lista) ele vai retornar o primeiro elemento
+ (o de index 0) e vai remove-lo da lista
+
+ E quando a sequência estiver vazia ele vai nos retornar um StopIteration
+ O que vai fazer com que a iteração acabe.
+
+ Porém, estamos iterando com pop, o que faz a nossa iteração ser a única,
+ pois ela não pode ser repetida, dado que os elementos foram
+ removidos da lista
+ """
+ ifnotself.sequencia:
+ raiseStopIteration
+ returnself.sequencia.pop()
+
+ def__iter__(self):
+ returnself
+
+
+
+
Como é possível notar, o objeto com __iter__ não necessita de um __getitem__ e vise-versa. As diferenças partem do ponto em que um pode ser acessado por index/slice e outro não. Também um bom ponto é que nesse nosso caso, removemos os elementos da sequência, ou seja, ela se torna descartável.
+
Esse conceito, de ser descartado, pode parecer um pouco estranho no início, mas economiza muita memória. E, como estamos falando de programação funcional, pode-se dizer que nossa sequência se torna imutável, pois não existe uma maneira de mudar os valores contidos na lista do objeto.
+
Seguem dois links maravilhosos explicando sobre iteração em python:
O primeiro é a PEP sobre as estruturas dos iteráveis e o segundo um video do Guru Luciano Ramalho explicando tudo sobre iteradores.
+
Ah... Ia quase me esquecendo, se você não entendeu muita coisa sobre os dunders, você pode ler o Python data model. Obs: não me responsabilizo pelo programador melhor que você sairá desta página.
+
Embora esse tópico seja talvez o mais curto, ele vai ser de fundamental importância para o entendimento de um pouco de tudo nesse 'Curso'. É sério. Vamos entender como trabalhar com iteráveis de uma maneira bonita no próximo tópico.
Como nem tudo são flores, vamos começar do começo e entender algumas características das funções do python (o objeto função) e dar uma revisada básica em alguns conceitos de função só pra gente não se perder no básico depois. Então o primeiro tópico vai se limitar a falar da estrutura básica das funções em python, sem entrar profundamente em cada um dos tópicos. Será uma explanação de código e abrir a cabeça para novas oportunidades de código mais pythonicos e que preferencialmente gere menos efeito colateral. Mas calma, não vamos ensinar a fazer funções, você já está cheio disso.
+
1.1 Funções como objeto de primeira classe
+
Funções como objeto de primeira classe, são funções que se comportam como qualquer tipo nativo de uma determinada linguagem. Por exemplo:
+
# uma lista
+
+lista=[1,'str',[1,2],(1,2),{1,2},{1:'um'}]
+
+
+
+
Todos esses exemplos são tipos de objetos de primeira classe em Python, mas no caso as funções também são. Como assim? Pode-se passar funções como parâmetro de uma outra função, podemos armazenar funções em variáveis, pode-se definir funções em estruturas de dados:
+
# Funções como objeto de primeira classe
+
+func=lambdax:x# a função anônima, lambda, foi armazenada em uma variável
+
+deffunc_2(x):
+ returnx+2
+
+lista=[func,func_2]# a variável que armazena a função foi inserida em uma estrutura, assim como uma função gerada com def
+
+lista_2=[lambdax:x,lambdax:x+1]# aqui as funções foram definidas dentro de uma estrutura
+
+
+
+
Como é possível notar, em python, as funções podem ser inseridas em qualquer contexto e também geradas em tempo de execução. Com isso nós podemos, além de inserir funções em estruturas, retornar funções, passar funções como parâmetro (HOFs), definir funções dentro de funções(closures) e assim por diante. Caso você tenha aprendido a programar usando uma linguagem em que as funções não são objetos de primeira classe, não se assuste. Isso faz parte da rotina comum do python. Preferencialmente, e quase obrigatoriamente, é melhor fazer funções simples, pequenas e de pouca complexidade para que elas não sofram interferência do meio externo, gerem menos manutenção e o melhor de tudo, possam ser combinadas em outras funções. Então vamos lá!
+
1.2 Funções puras
+
Funções puras são funções que não sofrem interferência do meio externo. Vamos começar pelo exemplo ruim:
mais_cinco() é o exemplo claro de uma função que gera efeito colateral. Uma função pura deve funcionar como uma caixa preta, todas as vezes em que o mesmo input for dado nela, ela terá que retornar o mesmo valor. Agora vamos usar o mesmo exemplo, só alterando a linha do return:
Pode parecer trivial, mas muitas vezes por comodidade deixamos o meio influenciar no comportamento de uma função. Por definição o Python só faz possível, e vamos falar disso em outro tópico, a leitura de variáveis externas. Ou seja, dentro do contexto da função as variáveis externas não podem ser modificadas, mas isso não impede que o contexto externo a modifique. Se você for uma pessoa inteligente como o Jaber deve saber que nunca é uma boa ideia usar valores externos. Mas, caso seja necessário, você pode sobrescrever o valor de uma variável no contexto global usando a palavra reservada global. O que deve ficar com uma cara assim:
+
valor=5
+
+defteste():
+ globalvalor# aqui é feita a definição
+ valor=7
+
+print(valor)# 7
+
+
+
+
Só lembre-se de ser sempre coerente quando fizer isso, as consequências podem ser imprevisíveis. Nessa linha de funções puras e pequeninas, podemos caracterizar, embora isso não as defina, funções de ordem superior, que são funções que recebem uma função como argumento, ou as devolvem, e fazem a chamada das mesmas dentro do contexto da função que a recebeu como parâmetro. Isso resulta em uma composição de funções, o que agrega muito mais valor caso as funções não gerem efeitos colaterais.
+
1.3 Funções de ordem superior (HOFs)
+
Funções de ordem superior são funções que recebem funções como argumento(s) e/ou retornam funções como resposta. Existem muitas funções embutidas em python de ordem superior, como: map, filter, zip e praticamente todo o módulo functools import functools. Porém, nada impede de criarmos novas funções de ordem superior. Um ponto a ser lembrado é que map e filter não tem mais a devida importância em python com a entrada das comprehensions (embora eu as adore), o que nos faz escolher única e exclusivamente por gosto, apesar de comprehensions serem mais legíveis (vamos falar disso em outro contexto), existem muitos casos onde elas ainda fazem sentido. Mas sem me estender muito, vamos ao código:
+
func=lambdax:x+2# uma função simples, soma mais 2 a qualquer inteiro
+
+deffunc_mais_2(funcao,valor):
+ """
+ Executa a função passada por parâmetro e retorna esse valor somado com dois
+
+ Ou seja, é uma composição de funções:
+
+ Dado que func(valor) é processado por func_func:
+ func_mais_2(func(valor)) == f(g(x))
+ """
+ returnfuncao(valor)+2
+
+
+
+
Um ponto a tocar, e o que eu acho mais bonito, é que a função vai retornar diferentes respostas para o mesmo valor, variando a entrada da função. Nesse caso, dada a entrada de um inteiro ele será somado com 2 e depois com mais dois. Mas, vamos estender este exemplo:
+
func=lambdax:x+2# uma função simples, soma mais 2 a qualquer inteiro
+
+deffunc_mais_2(funcao,valor):
+ """
+ Função que usamos antes.
+ """
+ returnfuncao(valor)+2
+
+assertfunc_mais_2(func,2)==6# true
+
+deffunc_quadrada(val):
+ """
+ Eleva o valor de entrada ao quadrado.
+ """
+ returnval*val
+
+assertfunc_mais_2(func_quadrada,2)==6# true
+
+
+
+
1.3.1 Um exemplo usando funções embutidas:
+
Muitas das funções embutidas em python são funções de ordem superior (HOFs) como a função map, que é uma das minhas preferidas. Uma função de map recebe uma função, que recebe um único argumento e devolve para nós uma nova lista com a função aplicada a cada elemento da lista:
Mas fique tranquilo, falaremos muito mais sobre isso.
+
1.4 __call__
+
Por que falar de classes? Lembre-se, Python é uma linguagem construída em classes, e todos os objetos que podem ser chamados/invocados implementam o método __call__:
Isso quer dizer que podemos gerar classes que se comportam como funções?
+
SIIIIIM. Chupa Haskell
+
Essa é uma parte interessante da estrutura de criação do Python a qual veremos mais em outro momento sobre introspecção de funções, mas vale a pena dizer que classes, funções nomeadas, funções anônimas e funções geradoras usam uma base comum para funcionarem, essa é uma das coisas mais bonitas em python e que em certo ponto fere a ortogonalidade da linguagem, pois coisas iguais tem funcionamentos diferentes, mas facilita o aprendizado da linguagem, mas não é nosso foco agora.
+
1.5 Funções geradoras
+
Embora faremos um tópico extremamente focado em funções geradoras, não custa nada dar uma palinha, não?
+
Funções geradoras são funções que nos retornam um iterável. Mas ele é lazy (só é computado quando invocado). Para exemplo de uso, muitos conceitos precisam ser esclarecidos antes de entendermos profundamente o que acontece com elas, mas digo logo: são funções lindas <3
+
Para que uma função seja geradora, em tese, só precisamos trocar o return por yield:
Passando bem por cima, uma função geradora nos retorna um iterável que é preguiçoso. Ou seja, ele só vai efetuar a computação quando for chamado.
+
1.6 Funções anônimas (lambda)
+
Funções anônimas, ou funções lambda, são funções que podem ser declaradas em qualquer contexto. Tá... Todo tipo de função em python pode ser declarada em tempo de execução. Porém funções anônimas podem ser atribuídas a variáveis, podem ser definidas dentro de sequências e declaradas em um argumento de função. Vamos olhar sua sintaxe:
+
lambdaargumento:argumento
+
+
+
+
A palavra reservada lambda define a função, assim como uma def. Porém em uma def quase que instintivamente sempre quebramos linha:
+
deffunc():
+ pass
+
+
+
+
Uma das diferenças triviais em python é que as funções anônimas não tem nome. Tá, isso era meio óbvio, mas vamos averiguar:
O resultado '<lambda>' será o mesmo para qualquer função. Isso torna sua depuração praticamente impossível em python. Por isso os usuários de python (e nisso incluo todos os usuários, até aqueles que gostam de funcional) não encorajam o uso de funções lambda a todos os contextos da linguagem. Mas, em funções que aceitam outra funções isso é meio que uma tradição, caso a função (no caso a que executa o código a ser usado pelo lambda) não esteja definida e nem seja reaproveitada em outro contexto. Eu gosto de dizer que lambdas são muito funcionais em aplicações parciais de função. Porém, os lambdas não passam de açúcar sintático em Python, pois não há nada que uma função padrão (definida com def), não possa fazer de diferente. Até a introspecção retorna o mesmo resultado:
+
deffunc():
+ pass
+
+type(func)# function
+
+lambda_func=lambdaarg:arg
+
+type(lambda_func)# function
+
+
+
+
Uma coisa que vale ser lembrada é que funções anônimas em python só executam uma expressão. Ou seja, não podemos usar laços de repetição (while, for), tratamento de exceções (try, except, finally). Um simples if com uso de elif também não pode ser definido. Como sintaticamente só são aceitas expressões, o único uso de um if é o ternário:
Embora essa seja uma explanação inicial sobre as funções anônimas, grande parte dos tópicos fazem uso delas e vamos poder explorar melhor sua infinitude.
+
Mas por hoje é só e no tópico seguinte vamos discutir, mesmo que superficialmente, iteradores e iteráveis e suas relações com a programação funcional.
Sinta-se um vencedor, se você chegou até aqui, isso significa que quer aprender mais sobre o mundo da programação.
+
Aprender novos paradígmas podem te trazer muitas coisas positivas, assim como aprender linguagens diferentes, pois paradígmas e linguagens transpõem maneiras, estruturas e métodos de implementação completamente diferentes. Com isso você pode ter mais ferramentas para usar no dia a dia. Você pode aumentar sua capacidade de expressar ideias de diferentes maneiras. Eu penso que o maior limitador de um programador é a linguagem de programação em que ele tem domínio. Quando você aprende linguagens imperativas, como C, Python, Java e etc..., você se vê limitado ao escopo de criar e manipular variáveis. Não que isso seja uma coisa ruim, porém existem outras maneiras de resolver problemas e quando você tem conhecimento disso consegue avaliar melhor quando implementar cada tipo de coisa.
+
Você pode me dizer que aprender diferentes tipos de estruturas e maneiras de computar é uma coisa negativa pois tudo é variável nesse contexto. Mas eu penso exatamente o contrário, quanto mais você aprender da sua língua nativa, no caso estamos falando em português, maior o campo de domínio que você tem sobre como se comunicar e expressar ideias. Assim como aprender outras línguas te darão mais fundamentos para expressar ideias em outros idiomas, que não são melhores que os seu, mas diferentes e compõem diferentes estruturas, e isso pode ser libertador. Não quero me prolongar nesse assunto, mas dizer que isso pode acrescentar muito na suas habilidades cognitivas, até mesmo para usar ferramentas que você já usa no seu dia a dia.
+
Vamos começar fazendo uma tentativa de entender os paradígmas de programação, sem muito falatório e complicações. Um exemplo muito legal é do David Mertz em "Functional Programming in Python":
+
+
+
Usa-se programação funcional quando se programa em Lisp, Haskell, Scala, Erlang, F# etc..
+
+
+
Do mesmo modo que se usa programação imperativa quando se programada C/C++, Pascal, Java, Python etc...
+
+
+
Também quando se programa Prolog estamos programando usando o paradígma lógico.
+
+
+
Apesar de não ser uma definição muito elegante, talvez seja a melhor a ser dada em muitas ocasiões. Vamos tentar ser um pouco mais objetivos em relação ao estilo de computação, embora essa discussão não tenha fim:
+
+
+
O foco de usar programação imperativa está no ato de mudar variáveis. A computação se dá pela modificação dos estados das variáveis iniciais. Sendo assim, vamos pensar que tudo é definido no início e vai se modificando até que o resultado esperado seja obtido.
+
+
+
Na programação funcional, se tem a noção de que o estado deve ser substituído, no caso da avaliação, para criação de um novo 'objeto' que no caso são funções.
+
+
+
0.1 Mas de onde vem a programação funcional?
+
O florescer da programação funcional nasce no Lisp (acrônomo para List Processing) para tentar resolver alguns problemas de inteligência artificial que eram provenientes da linguística, que tinha foco em processamento de linguagem natural que por sua vez eram focados em processamento de listas em geral. Isso justifica uma grande parte do conteúdo que vamos ver aqui e seus tipos de dados variam somente entre listas e átomos. E assim foi mantido o foco de processamento de listas em todas as linguagens funcionais e suas funções e abstrações para resolver problemas relativos a listas e estruturas iteráveis. Uma curiosidade é que para quem não sabe porque em lisp existem tantos parênteses é que ele é baseado em s-expression, uma coisa que temos um "equivalente" evoluído em python, que parte dos teoremas de gramáticas livres de contexto:
+
(+ 45)
+
+
+
+
Sim, isso é uma soma em lisp. Diferente das linguagens imperativas como costumamos ver:
+
4+5
+
+
+
+
Uma assertiva pode ser feita dessa maneira:
+
+
Funcional (ou declarativa)
+
+
(= 4(+ 22))
+
+
+
+
+
Imperativa
+
+
(2+2)==4
+
+
+
+
Chega de enrolação e vamos correr com essa introdução, não viemos aqui para aprender Lisp ou C. Mas acho que parte desse contexto pode nos ajudar e muito quando formos nos aprofundar em alguns tópicos. Pretendo sempre que iniciar uma nova ferramenta da programação funcional ao menos explicar em que contexto ela foi desenvolvida e para resolver cada tipo de problema.
+
0.2 Técnicas usadas por linguagens funcionais
+
Vamos tentar mapear o que as linguagens funcionais fazem de diferente das linguagens imperativas, mas não vamos nos aprofundar nesse tópicos agora, pois são coisas às vezes complexas sem o entendimento prévio de outros contextos, mas vamos tentar só explanar pra que você se sinta empolgado por estar aqui:
+
+
+
Funções como objetos de primeira classe:
+
+
São funções que podem estar em qualquer lugar (em estruturas, declaradas em tempo de execução).
+
+
+
+
Funções de ordem superior:
+
+
São funções que podem receber funções como argumentos e retornar funções.
+
+
+
+
Funções puras:
+
+
São funções que não sofrem interferências de meios externos (variáveis de fora). Evita efeitos colaterais.
+
+
+
+
Recursão, como oposição aos loops:
+
+
Frequentemente a recursão na matemática é uma coisa mais intuitiva e é só chamar tudo outra vez, no lugar de ficar voltando ao ponto inicial da iteração.
+
+
+
+
Foco em processamento de iteráveis:
+
+
Como dito anteriormente, pensar em como as sequências podem nos ajudar a resolver problemas.
+
+
+
+
O que deve ser computado, não como computar:
+
+
Não ser tão expressivo e aceitar que as intruções não tem necessidade de estar explicitas todas as vezes, isso ajuda em legibilidade.
+
+
+
+
Lazy evaluation:
+
+
Criar sequências infinitas sem estourar nossa memória.
+
+
+
+
0.3 Python é uma linguagem funcional?
+
Não. Mas é uma linguagem que implementa muitos paradígmas e porque não usar todos de uma vez?
+
O objetivo desse 'conjunto de vídeos' é escrever código que gere menos efeito colateral e código com menos estados. Só que isso tudo feito na medida do possível, pois Python não é uma linguagem funcional. Porém, podemos contar o máximo possível com as features presentes do paradígma em python.
+
Exemplos de funcional (básicos) em python:
+
# Gerar uma lista da string # Imperativo
+string='Python'
+lista=[]# estado inicial
+
+forlinstring:
+ lista.append(l)# cada iteração gera um novo estado
+
+print(lista)# ['P', 'y', 't', 'h', 'o', 'n']
+
+
+
+
# Gerar uma lista da string # Funcional
+string=lambdax:x
+
+lista=list(map(str,string('Python')))# atribuição a um novo objeto
+
+print(lista)# ['P', 'y', 't', 'h', 'o', 'n']
+
+
+
+
Como você pode ver, depois de uma explanação básica das técnicas, a segunda implementação não sofre interferência do meio externo (Funções puras), evita loops e sua saída sem o construtor de list é lazy. Mas não se assuste, vamos abordar tudo isso com calma.
+
0.4 A quem esse 'curso' é destinado?
+
Primeiramente gostaria de dizer que roubei essa ideia dos infinitos livros da O’Reilly, que sempre exibem esse tópico. Mas vamos ao assunto. Este curso é para você que sabe o básico de Python, e quando digo básico quero dizer que consegue fazer qualquer coisa com um pouco de pesquisa na internet. O básico de programação se reduz a isso. Vamos falar sobre coisas simples e coisas mais complexas, mas pretendo manter o bom senso para que todos possam absorver o máximo de conteúdo possível.
+
Então, caso você venha do Python (OO ou procedural) você vai encontrar aqui uma introdução a programação funcional descontraída e sem uma tonelada de material difícil de entender. Caso você venha de linguagens funcionais como Haskell e Lisp, você pode se sentir um pouco estranho com tantas declarações, mas aprenderá a se expressar em Python. Caso você venha de linguagens funcionais modernas como Clojure e Scala, as coisas são bem parecidas por aqui.
+
Então tente tirar o máximo de proveito. Vamos nos divertir.
+
0.5 Apresentando o Jaber
+
Jaber é nosso aluno de mentira, mas vamos pensar que ele é um aluno que senta na primeira fileira e pergunta de tudo, sempre que acha necessário. Roubei essa ideia do livro de expressões regulares do Aurélio. Ele tem um personagem, Piazinho, e acho que toda interação com ele é sempre graciosa e tira dúvidas quando tudo parece impossível.
+
0.6 Sobre as referências
+
Não gosto muito de citar referências pois procurei não copiar texto dos livros, mas muita coisa contida neles serve de base para o entendimento de certos tópicos. Outro motivo é o nível de complexidade dos exemplos ou explicações que tentei reduzir ao máximo enquanto escrevia esses roteiros. Para um exemplo, você pode olhar o livro do Steven Lott, cheio de fórmulas e abstrações matemáticas que em certo ponto acabam comprometendo o entendimento de quem não tem uma sólida base em computação teórica ou matemática.
+
Como um todo, as referências serviram como guia, foi o que lí quando dúvidas para explicações surgiram. Não tiro nenhum crédito delas e as exponho para que todos saibam que existem muitos livros bons e que boa parte do que é passado aqui, foi aprendido neles.
+
0.7 Mais sobre o histórico das linguagens funcionais
+
Se você pretende realmente se aprofundar no assunto enquanto acompanha esse curso, fazer uma imersão ou coisa parecida. Tudo começa com o cálculo lambda mentalizado pelo incrível Alonzo Church. Caso você não o conheça, ele foi um matemático fantástico e teve uma carreira acadêmica brilhante. Foi o orientador de pessoas incríveis como Alan Turing, Raymond Smullyan etc...
+
Outro grande homem e que vale a pena mencionar e ser buscado é o Haskell Curry, um lógico que trouxe excelentes contribuições para o que chamamos hoje de programação funcional.
+
A primeira linguagem funcional 'oficial' (não gosto muito de dizer isso) é o Lisp (List Processing) criada pelo fenomenal John McCarthy que também vale a pena ser pesquisado e estudado.
+
Veremos o básico sobre os tipos de função no próximo tópico.
Peewee é um ORM destinado a criar e gerenciar tabelas de banco de dados relacionais através de objetos Python. Segundo a wikipedia, um ORM é:
+
+
Mapeamento objeto-relacional (ou ORM, do inglês: Object-relational mapping) é uma técnica de desenvolvimento > utilizada para reduzir a impedância da programação orientada aos objetos utilizando bancos de dados relacionais. As tabelas do banco de dados são representadas através de classes e os registros de cada tabela são representados como instâncias das classes correspondentes.
+
+
O que um ORM faz é, basicamente, transformar classes Python em tabelas no banco de dados, além de permitir construir querys usando diretamente objetos Python ao invés de SQL.
+
O Peewee é destinado a projetos de pequeno/médio porte e se destaca pela simplicidade quando comparado a outros ORM mais conhecidos, como o SQLAlchemy. Uma analogia utilizada pelo autor da API e que acho muito interessante é que Peewee está para o SQLAlchemy assim como SQLite está para o PostgreSQL.
+
Em relação aos recursos por ele oferecidos, podemos citar que ele possui suporte nativo a SQLite, PostgreSQL e MySQL, embora seja necessário a instalação de drivers para utilizá-lo com PostgreSQL e MySQL e suporta tanto Python 2.6+ quanto Python 3.4+.
+
Neste tutorial, utilizaremos o SQLite, por sua simplicidade de uso e pelo Python possuir suporte nativo ao mesmo (usaremos o Python 3.5).
+
Instalação
+
O Peewee pode ser facilmente instalado com o gerenciador de pacotes pip (recomendo a instalação em um virtualenv):
+
pip install peewee
+
+
+
+
Criando o banco de dados
+
Para criar o banco de dados é bem simples. Inicialmente passamos o nome do nosso banco de dados (a extensão *.db indica um arquivo do SQLite).
+
importpeewee
+
+# Aqui criamos o banco de dados
+db=peewee.SqliteDatabase('codigo_avulso.db')
+
+
+
+
Diferente de outros bancos de dados que funcionam através um servidor, o SQLite cria um arquivo de extensão *.db, onde todos os nossos dados são armazenados.
+
+
Caso deseje ver as tabelas existentes no arquivo codigo_avulso.db, instale o aplicativo SQLiteBrowser. Com ele fica fácil monitorar as tabelas criadas e acompanhar o tutorial.
+
+
sudo apt-get install sqlitebrowser
+
+
+
+
A título de exemplo, vamos criar um banco destinado a armazenar nomes de livros e de seus respectivos autores. Iremos chamá-lo de models.py.
+
Inicialmente, vamos criar a classe base para todos os nossos models. Esta é uma abordagem recomendada pela documentação e é considerada uma boa prática. Também adicionaremos um log para acompanharmos as mudanças que são feitas no banco:
+
# models.py
+
+importpeewee
+
+# Criamos o banco de dados
+db=peewee.SqliteDatabase('codigo_avulso.db')
+
+
+classBaseModel(peewee.Model):
+ """Classe model base"""
+
+ classMeta:
+ # Indica em qual banco de dados a tabela
+ # 'author' sera criada (obrigatorio). Neste caso,
+ # utilizamos o banco 'codigo_avulso.db' criado anteriormente
+ database=db
+
+
+
+
A class BaseModel é responsável por criar a conexão com nosso banco de dados.
+
Agora, vamos criar a model que representa os autores:
+
# models.py
+
+classAuthor(BaseModel):
+
+ """
+ Classe que representa a tabela Author
+ """
+ # A tabela possui apenas o campo 'name', que receberá o nome do autor sera unico
+ name=peewee.CharField(unique=True)
+
+
+
+
Se observamos a model Author, veremos que não foi especificado nenhuma coluna como primary key (chave primaria), sendo assim o Peewee irá criar um campo chamado id do tipo inteiro com auto incremento para funcionar como chave primária.
+Em seguida, no mesmo arquivo models.py criamos a classe que representa os livros. Ela possui uma relação de "muitos para um" com a tabela de autores, ou seja, cada livro possui apenas um autor, mas um autor pode possuir vários livros.
+
# models.py
+
+classBook(BaseModel):
+ """
+ Classe que representa a tabela Book
+ """
+
+ # A tabela possui apenas o campo 'title', que receberá o nome do livro
+ title=peewee.CharField(unique=True)
+
+ # Chave estrangeira para a tabela Author
+ author=peewee.ForeignKeyField(Author)
+
+
+
+
Agora, adicionamos o código que cria as tabelas Author e Book.
+
# models.py
+
+if__name__=='__main__':
+ try:
+ Author.create_table()
+ print("Tabela 'Author' criada com sucesso!")
+ exceptpeewee.OperationalError:
+ print("Tabela 'Author' ja existe!")
+
+ try:
+ Book.create_table()
+ print("Tabela 'Book' criada com sucesso!")
+ exceptpeewee.OperationalError:
+ print("Tabela 'Book' ja existe!")
+
+
+
+
excerpt
+Agora executamos o models.py:
+
python models.py
+
+
+
+
A estrutura do diretório ficou assim:
+
.
+├── codigo_avulso.db
+├── models.py
+
+
+
+
Após executarmos o código, será criado um arquivo de nome codigo_avulso.db no mesmo diretório do nosso arquivo models.py, contendo as tabelas Author e Book.
+
Realizando o CRUD
+
Agora vamos seguir com as 4 principais operações que podemos realizar em um banco de dados, também conhecida como CRUD.
+
A sigla CRUD é comumente utilizada para designar as quatro operações básicas que pode-se executar em um banco de dados, sendo elas:
+
- Create (criar um novo registro no banco)
+- Read (ler/consultar um registro)
+- Update (atualizar um registro)
+- Delete (excluir um registro do banco)
+
+
+
+
Iremos abordar cada uma dessas operações.
+
Create: Inserindo dados no banco
+
Agora, vamos popular nosso banco com alguns autores e seus respectivos livros. Para isso criamos um arquivo create.py. A estrutura do diretório ficou assim:
A criação dos registros no banco pode ser feito através do método create, quando desejamos inserir um registro apenas; ou pelo método insert_many, quando desejamos inserir vários registros de uma vez em uma mesma tabela.
+
# create.py
+
+frommodelsimportAuthor,Book
+
+# Inserimos um autor de nome "H. G. Wells" na tabela 'Author'
+author_1=Author.create(name='H. G. Wells')
+
+# Inserimos um autor de nome "Julio Verne" na tabela 'Author'
+author_2=Author.create(name='Julio Verne')
+
+book_1={
+ 'title':'A Máquina do Tempo',
+ 'author_id':author_1,
+}
+
+book_2={
+ 'title':'Guerra dos Mundos',
+ 'author_id':author_1,
+}
+
+book_3={
+ 'title':'Volta ao Mundo em 80 Dias',
+ 'author_id':author_2,
+}
+
+book_4={
+ 'title':'Vinte Mil Leguas Submarinas',
+ 'author_id':author_1,
+}
+
+books=[book_1,book_2,book_3,book_4]
+
+# Inserimos os quatro livros na tabela 'Book'
+Book.insert_many(books).execute()
+
+
+
+
Read: Consultando dados no banco
+
O Peewee possui comandos destinados a realizar consultas no banco. De maneira semelhante ao conhecido SELECT. Podemos fazer essa consulta de duas maneiras. Se desejamos o primeiro registro que corresponda a nossa pesquisa, podemos utilizar o método get().
+
# read.py
+
+frommodelsimportAuthor,Book
+
+book=Book.get(Book.title=="Volta ao Mundo em 80 Dias").get()
+print(book.title)
+
+# Resultado
+# * Volta ao Munto em 80 Dias
+
+
+
+
Porém, se desejamos mais de um registro, utilizamos o método select. Por exemplo, para consultar todos os livros escritos pelo autor "H. G. Wells".
+
# read.py
+
+books=Book.select().join(Author).where(Author.name=='H. G. Wells')
+
+# Exibe a quantidade de registros que corresponde a nossa pesquisa
+print(books.count())
+
+forbookinbooks:
+ print(book.title)
+
+# Resultado:
+# * A Máquina do Tempo
+# * Guerra dos Mundos
+# * Vinte Mil Leguas Submarinas
+
+
+
+
Também podemos utilizar outras comandos do SQL como limit e group (para mais detalhes, ver a documentação aqui).
Alterar dados também é bem simples. No exemplo anterior, se observarmos o resultado da consulta dos livros do autor "H. G. Wells", iremos nos deparar com o livro de título "Vinte Mil Léguas Submarinas". Se você, caro leitor, gosta de contos de ficção-científica, sabe que esta obra foi escrito por "Julio Verne", coincidentemente um dos autores que também estão cadastrados em nosso banco. Sendo assim, vamos corrigir o autor do respectivo livro.
+
Primeiro vamos buscar o registro do autor e do livro:
Assim como as operações anteriores, também podemos deletar registros do banco de maneira bem prática. Como exemplo, vamos deletar o livro "Guerra dos Mundos" do nosso banco de dados.
+
# delete.py
+
+frommodelsimportAuthor,Book
+
+# Buscamos o livro que desejamos excluir do banco
+book=Book.get(Book.title=="Guerra dos Mundos")
+
+# Excluimos o livro do banco
+book.delete_instance()
+
É isso pessoal. Este tutorial foi uma introdução bem enxuta sobre o Peewee. Ainda existem muitos tópicos que não abordei aqui, como a criação de primary_key, de campos many2many entre outros recursos, pois foge do escopo deste tutorial. Se você gostou do ORM, aconselho a dar uma olhada também na sua documentação, para conseguir extrair todo o potencial da ferramenta. A utilização de um ORM evita que o desenvolvedor perca tempo escrevendo query SQL e foque totalmente no desenvolvimento de código.
+For the Portuguese speaking readers of this blog, I am starting an Introductory course on Cryptocurrencies and applications on the blockchain which is an online version of a standard classroom course I am starting now at FGV.
+
+This is the first lecture which is basically an intro to the topic and the structure of the course.
+
+The online version should have the main content of the lectures on
+
+
+
+
+
diff --git a/opml.xml b/opml.xml
new file mode 100644
index 0000000..081d418
--- /dev/null
+++ b/opml.xml
@@ -0,0 +1,67 @@
+
+
+
+ Planeta PythonBrasil
+ Thu, 17 Apr 2025 02:15:10 +0000
+ Python Brasil
+ contato@python.org.br
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rss10.xml b/rss10.xml
new file mode 100644
index 0000000..4bc9030
--- /dev/null
+++ b/rss10.xml
@@ -0,0 +1,8850 @@
+
+
+
+ Planeta PythonBrasil
+ http://planet.python.org.br/
+ Planeta PythonBrasil - http://planet.python.org.br/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Rodrigo Delduca: Building and Publishing Games to Steam Directly from GitHub Actions
+ https://nullonerror.org/2025/03/23/building-and-publishing-games-to-steam-directly-from-gitHub-actions/
+ <p>I have been using GitHub Actions extensively both at work and in personal projects, as shown in this post <a href="https://nullonerror.org/2023/11/01/what-i-ve-been-automating-with-github-actions-an-automated-life/">What I’ve been automating with GitHub Actions, an automated life</a>.</p>
+
+<p>In my free time, I’m working on a 2D hide-and-seek game, and as you might expect, I’ve automated the entire release pipeline for publishing on Steam. After a few attempts, when it finally worked, it felt like magic: all I had to do was create a new tag, and within minutes, the Steam client was downloading the update.</p>
+
+<p>As I mentioned earlier, I have a 2D engine that, while simple, is quite comprehensive. With each new tag, I compile it in parallel for Windows, macOS, Linux, and WebAssembly. Once compilation is complete, I create a release and publish it on GitHub. <a href="https://github.com/willtobyte/carimbo/releases">Releases · willtobyte/carimbo</a></p>
+
+<p>This way</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Release</span>
+
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">push</span><span class="pi">:</span>
+ <span class="na">tags</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s2">"</span><span class="s">v*.*.*"</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">release</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">${{ matrix.config.os }}</span>
+
+ <span class="na">permissions</span><span class="pi">:</span>
+ <span class="na">contents</span><span class="pi">:</span> <span class="s">write</span>
+
+ <span class="na">strategy</span><span class="pi">:</span>
+ <span class="na">fail-fast</span><span class="pi">:</span> <span class="no">true</span>
+
+ <span class="na">matrix</span><span class="pi">:</span>
+ <span class="na">config</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">macOS</span>
+ <span class="na">os</span><span class="pi">:</span> <span class="s">macos-latest</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Ubuntu</span>
+ <span class="na">os</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">WebAssembly</span>
+ <span class="na">os</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Windows</span>
+ <span class="na">os</span><span class="pi">:</span> <span class="s">windows-latest</span>
+
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Cache Dependencies</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/cache@v4</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">path</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">~/.conan2/p</span>
+ <span class="s">C:/Users/runneradmin/.conan2/p</span>
+ <span class="na">key</span><span class="pi">:</span> <span class="s">${{ matrix.config.name }}-${{ hashFiles('**/conanfile.py') }}</span>
+ <span class="na">restore-keys</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">${{ matrix.config.name }}-</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Prepare Build Directory</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">mkdir build</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup Python</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-python@v5</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">python-version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3.12"</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Conan</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">pip install conan</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Detect Conan Profile</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">conan profile detect --force</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set Conan Center</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">conan remote update conancenter --url https://center2.conan.io</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Detect WebAssembly Conan Profile</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'WebAssembly'</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">cat > ~/.conan2/profiles/webassembly <<EOF</span>
+ <span class="s">include(default)</span>
+
+ <span class="s">[settings]</span>
+ <span class="s">arch=wasm</span>
+ <span class="s">os=Emscripten</span>
+
+ <span class="s">[tool_requires]</span>
+ <span class="s">*: emsdk/3.1.73</span>
+ <span class="s">EOF</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Windows Or macOS Dependencies</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'Windows' || matrix.config.name == 'macOS'</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">conan install . --output-folder=build --build=missing --settings compiler.cppstd=20 --settings build_type=Release</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Ubuntu Dependencies</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'Ubuntu'</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">conan install . --output-folder=build --build=missing --settings compiler.cppstd=20 --settings build_type=Release --conf "tools.system.package_manager:mode=install" --conf "tools.system.package_manager:sudo=True"</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install WebAssembly Dependencies</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'WebAssembly'</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">conan install . --output-folder=build --build=missing --profile=webassembly --settings compiler.cppstd=20 --settings build_type=Release</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Configure</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">cmake .. -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">cmake --build . --parallel 8 --config Release --verbose</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Create Artifacts Directory</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">mkdir artifacts</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Compress Artifacts</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'macOS'</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">chmod -R a+rwx carimbo</span>
+ <span class="s">tar -cpzvf macOS.tar.gz carimbo</span>
+ <span class="s">mv macOS.tar.gz ../artifacts</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Compress Artifacts</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'Ubuntu'</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">chmod +x carimbo</span>
+ <span class="s">tar -czvf Ubuntu.tar.gz --mode='a+rwx' carimbo</span>
+ <span class="s">mv Ubuntu.tar.gz ../artifacts</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Compress Artifacts</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'WebAssembly'</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">zip -jr WebAssembly.zip carimbo.wasm carimbo.js</span>
+ <span class="s">mv WebAssembly.zip ../artifacts</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Compress Artifacts</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'Windows'</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+ <span class="na">shell</span><span class="pi">:</span> <span class="s">powershell</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">Compress-Archive -LiteralPath 'Release/carimbo.exe' -DestinationPath "../artifacts/Windows.zip"</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Release</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">softprops/action-gh-release@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">tag_name</span><span class="pi">:</span> <span class="s">${{ github.event.inputs.tagName }}</span>
+ <span class="na">prerelease</span><span class="pi">:</span> <span class="s">${{ github.events.inputs.prerelease }}</span>
+ <span class="na">files</span><span class="pi">:</span> <span class="s">artifacts/*</span>
+</code></pre></div></div>
+
+<p>Publishing on Steam is quite simple. First, you need a developer account with the correct documentation and fees paid.</p>
+
+<p>After that, you’ll need to generate some secret keys as follows:</p>
+
+<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>steamcmd +login <username> <password> +quit
+</code></pre></div></div>
+
+<p>If you don’t have the steamcmd application installed, you’ll need to install it using:</p>
+
+<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cast <span class="nb">install</span> <span class="nt">--cask</span> steamcmd
+</code></pre></div></div>
+
+<p>Copy the contents of the authentication file:</p>
+
+<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> ~/Library/Application<span class="se">\ </span>Support/Steam/config/config.vdf | <span class="nb">base64</span> | pbcopy
+</code></pre></div></div>
+
+<p><strong>Note:</strong> You must have MFA enabled. After logging in, run the command below and copy the output into a GitHub Action variable named <code class="language-plaintext highlighter-rouge">STEAM_CONFIG_VDF</code>.</p>
+
+<p>Also, create the variables <code class="language-plaintext highlighter-rouge">STEAM_USERNAME</code> with your username and <code class="language-plaintext highlighter-rouge">STEAM_APP_ID</code> with your game’s ID.</p>
+
+<p>Additionally, the Action downloads the latest Carimbo release for Windows only (sorry Linux and macOS users, my time is limited). Ideally, I should pin the runtime version (the Carimbo version) using something like a runtime.txt file. Maybe I’ll implement this in the future, but for now, everything runs on the bleeding edge. :-)</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">on</span><span class="pi">:</span>
+ <span class="na">push</span><span class="pi">:</span>
+ <span class="na">tags</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s2">"</span><span class="s">v*.*.*"</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">publish</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">CARIMBO_TAG</span><span class="pi">:</span> <span class="s2">"</span><span class="s">v1.0.65"</span>
+
+ <span class="na">permissions</span><span class="pi">:</span>
+ <span class="na">contents</span><span class="pi">:</span> <span class="s">write</span>
+
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Clone the repository</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install 7zip</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">sudo apt install p7zip-full</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Create bundle</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">7z a -xr'!.git/*' -xr'!.git' -xr'!.*' -t7z -m0=lzma -mx=6 -mfb=64 -md=32m -ms=on bundle.7z .</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Download Carimbo runtime</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">GITHUB_TOKEN</span><span class="pi">:</span> <span class="s">${{ secrets.GITHUB_TOKEN }}</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">gh release download ${{ env.CARIMBO_TAG }} --repo willtobyte/carimbo --pattern "Windows.zip"</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Extract Carimbo runtime</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">7z x Windows.zip -o.</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Copy files</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">mkdir -p output</span>
+ <span class="s">mv bundle.7z output/</span>
+ <span class="s">mv carimbo.exe output/</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Upload build to Steam</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">game-ci/steam-deploy@v3</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">username</span><span class="pi">:</span> <span class="s">${{ secrets.STEAM_USERNAME }}</span>
+ <span class="na">configVdf</span><span class="pi">:</span> <span class="s">${{ secrets.STEAM_CONFIG_VDF }}</span>
+ <span class="na">appId</span><span class="pi">:</span> <span class="s">${{ secrets.STEAM_APP_ID }}</span>
+ <span class="na">rootPath</span><span class="pi">:</span> <span class="s">output</span>
+ <span class="na">depot1Path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">."</span>
+ <span class="na">releaseBranch</span><span class="pi">:</span> <span class="s">prerelease</span>
+</code></pre></div></div>
+
+<p>Boom! If everything is correct, your game should appear in your Steam client under the list of owned games.</p>
+ 2025-03-23T00:00:00+00:00
+ Rodrigo Delduca
+
+
+ Nilo Ney Coutinho Menezes: Advent of code 2024 - Day 15 of 25
+ https://blog.nilo.pro.br/en/posts/2024-12-16-adventofcode2024-day15/
+ <p>The fifteenth day of Advent of Code was yesterday 15/12/2024!
+If you&rsquo;re here for the first time today, don&rsquo;t forget to read the posts about
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-06-adventofcode2024-day01/">Day 1</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-06-adventofcode2024-day02/">Day 2</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-06-adventofcode2024-day03/">Day 3</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-06-adventofcode2024-day04/">Day 4</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-06-adventofcode2024-day05/">Day 5</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-07-adventofcode2024-day06/">Day 6</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-08-adventofcode2024-day07/">Day 7</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-09-adventofcode2024-day08/">Day 8</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-10-adventofcode2024-day09/">Day 9</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-11-adventofcode2024-day10/">Day 10</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-12-adventofcode2024-day11/">Day 11</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-13-adventofcode2024-day12/">Day 12</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-14-adventofcode2024-day13/">Day 13</a> and
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-15-adventofcode2024-day14/">Day 14</a>.</p>
+<p>I classified the <a href="https://adventofcode.com/2024/day/15">fifteenth problem</a> as medium (part 1) and medium (part 2).</p>
+<h1 id="tips">Tips</h1>
+<ul>
+<li>The first part of the problem is to read the maze (warehouse) and the robot commands.</li>
+<li>Use the example inputs to test the solution.</li>
+<li>You need to detect in the maze input where the walls, robot, and boxes are.</li>
+<li>The commands are on multiple lines, it&rsquo;s easier if you concatenate them into a single string.</li>
+<li>You can implement robot directions (commands) using vectors.</li>
+<li>Vectors can also be used to mark box and wall positions.</li>
+<li>The walls surround the entire warehouse, you don&rsquo;t need to worry about the robot leaving the warehouse.</li>
+<li>To check if the robot can move, verify if the new position is free. In this case, move the robot by updating its position.</li>
+<li>If the next position is occupied by a wall, the robot cannot move and stays in the same place.</li>
+<li>To know if boxes can be moved, you can recursively try all boxes from the movement direction. For each box, check if it can move (blank space in the movement direction). If one of the boxes cannot be moved, the movement is canceled.</li>
+<li>Remember that the robot can push multiple boxes at once. A recursive function can help solve this problem.</li>
+<li>In part 1, boxes are the same size as walls, meaning each box occupies one position. In part 2, each box occupies two positions.</li>
+<li>Part 2 is a bit more complex because you can move more than columns and rows of boxes, but entire parts of the warehouse.</li>
+<li>Horizontal movement is similar to part 1, as each box has a height of only one character, like in part 1.</li>
+<li>Each movement in part 2 can move up to 2 boxes at once. But this movement is done in cascade, so these two boxes can move other 2, 3, or more boxes. Each box movement can move zero, one, or two boxes at each step, but can cause the movement of many others.</li>
+<li>In part 2, you need to verify if the entire movement is valid. For example, when the boxes being pushed form a V or get stuck on a wall, they all have to stop moving. If you implement the movement function as in part 1, you&rsquo;ll see that the boxes will move in several pieces, which leads to the wrong answer.</li>
+</ul>
+<h1 id="how-much-python-do-you-need-to-know">How much Python do you need to know?</h1>
+<p>The chapters refer to my book <a href="https://python.nilo.pro.br/">Introduction to Programming with Python</a>.</p>
+<ul>
+<li>Lists - Chapter 6</li>
+<li>Strings - Chapter 7</li>
+<li>Recursive Functions - Chapter 8</li>
+<li>Classes, inheritance - Chapter 10</li>
+</ul>
+<h1 id="answer">Answer</h1>
+<p>Here&rsquo;s the code that solves the problem. You&rsquo;ll see the scars in the code from switching between part 1 and part 2, but the code works relatively well.</p>
+<p>You can use the Warehouse, Boxes, Wall, Robot, Problem, and Problem2 classes to debug your solution. The <code>draw</code> and <code>prompt</code> methods can help interpret and draw the commands step by step. Although they&rsquo;re not called in the answer, they&rsquo;re in the code if you need them.</p>
+<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> sys
+</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> helpers <span style="color:#f92672">import</span> read_file_without_blank_lines, Vector2d, Maze
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>TEST_INPUT <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;&#34;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O.O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##@.O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#...O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.#.O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#...O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#......#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;^^&gt;&gt;&gt;vv&lt;v&gt;&gt;v&lt;&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>INPUT <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;&#34;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O..O.O#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#......O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.OO..O.O#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O@..O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#O#..O...#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#O..O..O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.OO.O.OO#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#....O...#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;vv&gt;^&lt;v^&gt;v&gt;^vv^v&gt;v&lt;&gt;v^v&lt;v&lt;^vv&lt;&lt;&lt;^&gt;&lt;&lt;&gt;&lt;&gt;&gt;v&lt;vvv&lt;&gt;^v^&gt;^&lt;&lt;&lt;&gt;&lt;&lt;v&lt;&lt;&lt;v^vv^v&gt;^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">vvv&lt;&lt;^&gt;^v^^&gt;&lt;&lt;&gt;&gt;&gt;&lt;&gt;^&lt;&lt;&gt;&lt;^vv^^&lt;&gt;vvv&lt;&gt;&gt;&lt;^^v&gt;^&gt;vv&lt;&gt;v&lt;&lt;&lt;&lt;v&lt;^v&gt;^&lt;^^&gt;&gt;&gt;^&lt;v&lt;v
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&gt;&lt;&gt;vv&gt;v^v^&lt;&gt;&gt;&lt;&gt;&gt;&gt;&gt;&lt;^^&gt;vv&gt;v&lt;^^^&gt;&gt;v^v^&lt;^^&gt;v^^&gt;v^&lt;^v&gt;v&lt;&gt;&gt;v^v^&lt;v&gt;v^^&lt;^^vv&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;&lt;v&lt;^&gt;&gt;^^^^&gt;&gt;&gt;v^&lt;&gt;vvv^&gt;&lt;v&lt;&lt;&lt;&gt;^^^vv^&lt;vvv&gt;^&gt;v&lt;^^^^v&lt;&gt;^&gt;vvvv&gt;&lt;&gt;&gt;v^&lt;&lt;^^^^^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^&gt;&lt;^&gt;&lt;&gt;&gt;&gt;&lt;&gt;^^&lt;&lt;^^v&gt;&gt;&gt;&lt;^&lt;v&gt;^&lt;vv&gt;&gt;v&gt;&gt;&gt;^v&gt;&lt;&gt;^v&gt;&lt;&lt;&lt;&lt;v&gt;&gt;v&lt;v&lt;v&gt;vvv&gt;^&lt;&gt;&lt;&lt;&gt;^&gt;&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^&gt;&gt;&lt;&gt;^v&lt;&gt;&lt;^vvv&lt;^^&lt;&gt;&lt;v&lt;&lt;&lt;&lt;&lt;&gt;&lt;^v&lt;&lt;&lt;&gt;&lt;&lt;&lt;^^&lt;v&lt;^^^&gt;&lt;^&gt;&gt;^&lt;v^&gt;&lt;&lt;&lt;^&gt;&gt;^v&lt;v^v&lt;v^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&gt;^&gt;&gt;^v&gt;vv&gt;^&lt;&lt;^v&lt;&gt;&gt;&lt;&lt;&gt;&lt;&lt;v&lt;&lt;v&gt;&lt;&gt;v&lt;^vv&lt;&lt;&lt;&gt;^^v^&gt;^^&gt;&gt;&gt;&lt;&lt;^v&gt;&gt;v^v&gt;&lt;^^&gt;&gt;^&lt;&gt;vv^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;&gt;&lt;^^&gt;^^^&lt;&gt;&lt;vvvvv^v&lt;v&lt;&lt;&gt;^v&lt;v&gt;v&lt;&lt;^&gt;&lt;&lt;&gt;&lt;&lt;&gt;&lt;&lt;&lt;^^&lt;&lt;&lt;^&lt;&lt;&gt;&gt;&lt;&lt;&gt;&lt;^^^&gt;^^&lt;&gt;^&gt;v&lt;&gt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^^&gt;vv&lt;^v^v&lt;vv&gt;^&lt;&gt;&lt;v&lt;^v&gt;^^^&gt;&gt;&gt;^^vvv^&gt;vvv&lt;&gt;&gt;&gt;^&lt;^&gt;&gt;&gt;&gt;&gt;^&lt;&lt;^v&gt;^vvv&lt;&gt;^&lt;&gt;&lt;&lt;v&gt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">v^^&gt;&gt;&gt;&lt;&lt;^^&lt;&gt;&gt;^v^&lt;v^vv&lt;&gt;v^&lt;&lt;&gt;^&lt;^v^v&gt;&lt;^&lt;&lt;&lt;&gt;&lt;&lt;^&lt;v&gt;&lt;v&lt;&gt;vv&gt;&gt;v&gt;&lt;v^&lt;vv&lt;&gt;v^&lt;&lt;^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">read_input</span>(input: str) <span style="color:#f92672">-&gt;</span> tuple[str, str]:
+</span></span><span style="display:flex;"><span> maze <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> commands <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> line <span style="color:#f92672">in</span> input<span style="color:#f92672">.</span>splitlines():
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> line<span style="color:#f92672">.</span>strip():
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> line<span style="color:#f92672">.</span>startswith(<span style="color:#e6db74">&#34;#&#34;</span>):
+</span></span><span style="display:flex;"><span> maze<span style="color:#f92672">.</span>append(line)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>:
+</span></span><span style="display:flex;"><span> commands<span style="color:#f92672">.</span>append(line)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> maze, commands
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Vector2dWithSymbol</span>(Vector2d):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x, symbol):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>symbol <span style="color:#f92672">=</span> symbol
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __add__(self, other):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> Vector2dWithSymbol(self<span style="color:#f92672">.</span>y <span style="color:#f92672">+</span> other<span style="color:#f92672">.</span>y, self<span style="color:#f92672">.</span>x <span style="color:#f92672">+</span> other<span style="color:#f92672">.</span>x, self<span style="color:#f92672">.</span>symbol)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __sub__(self, other):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> Vector2dWithSymbol(self<span style="color:#f92672">.</span>y <span style="color:#f92672">-</span> other<span style="color:#f92672">.</span>y, self<span style="color:#f92672">.</span>x <span style="color:#f92672">-</span> other<span style="color:#f92672">.</span>x, self<span style="color:#f92672">.</span>symbol)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Box</span>(Vector2dWithSymbol):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x, symbol<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;O&#34;</span>):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, symbol)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Wall</span>(Vector2dWithSymbol):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, <span style="color:#e6db74">&#34;#&#34;</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Robot</span>(Vector2dWithSymbol):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, <span style="color:#e6db74">&#34;@&#34;</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Warehouse</span>(Maze):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">init_drawing</span>(self, background<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;.&#34;</span>):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>background <span style="color:#f92672">=</span> background
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>drawing <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> _ <span style="color:#f92672">in</span> range(self<span style="color:#f92672">.</span>max_y):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>drawing<span style="color:#f92672">.</span>append([background] <span style="color:#f92672">*</span> self<span style="color:#f92672">.</span>max_x)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">draw</span>(self, objects: list[Vector2dWithSymbol]):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> obj <span style="color:#f92672">in</span> objects:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>drawing[obj<span style="color:#f92672">.</span>y][obj<span style="color:#f92672">.</span>x] <span style="color:#f92672">=</span> obj<span style="color:#f92672">.</span>symbol
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">dump_drawing</span>(self, output<span style="color:#f92672">=</span>sys<span style="color:#f92672">.</span>stdout):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> line <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>drawing:
+</span></span><span style="display:flex;"><span> print(<span style="color:#e6db74">&#34;&#34;</span><span style="color:#f92672">.</span>join(line), file<span style="color:#f92672">=</span>output)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __iter__(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> iter(self<span style="color:#f92672">.</span>maze)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Problem</span>:
+</span></span><span style="display:flex;"><span> DIRECTIONS <span style="color:#f92672">=</span> {
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;&lt;&#34;</span>: Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;^&#34;</span>: Vector2d(<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;&gt;&#34;</span>: Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;v&#34;</span>: Vector2d(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>),
+</span></span><span style="display:flex;"><span> }
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, maze: list[str], commands: list[str]):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>commands <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;</span><span style="color:#f92672">.</span>join(commands)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse <span style="color:#f92672">=</span> Warehouse(maze)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>setup()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">setup</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>walls <span style="color:#f92672">=</span> set()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes <span style="color:#f92672">=</span> dict()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> y, line <span style="color:#f92672">in</span> enumerate(self<span style="color:#f92672">.</span>warehouse):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> x, char <span style="color:#f92672">in</span> enumerate(line):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;#&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>walls<span style="color:#f92672">.</span>add(Wall(y, x))
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;O&#34;</span>:
+</span></span><span style="display:flex;"><span> box <span style="color:#f92672">=</span> Box(y, x)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes[box] <span style="color:#f92672">=</span> box
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;@&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">=</span> Robot(y, x)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">draw</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>init_drawing()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>draw(self<span style="color:#f92672">.</span>walls)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>draw(self<span style="color:#f92672">.</span>boxes)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>draw([self<span style="color:#f92672">.</span>robot])
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>dump_drawing()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">execute_commands</span>(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> command <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>commands:
+</span></span><span style="display:flex;"><span> direction <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>DIRECTIONS[command]
+</span></span><span style="display:flex;"><span> new_position <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">+</span> direction
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> new_position <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> self<span style="color:#f92672">.</span>push(new_position, direction):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">elif</span> new_position <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>walls:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">+=</span> direction
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">push</span>(self, position: Vector2d, direction: Vector2d, execute: bool <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>):
+</span></span><span style="display:flex;"><span> new_position <span style="color:#f92672">=</span> position <span style="color:#f92672">+</span> direction
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> new_position <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>walls:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> new_position <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> can_push <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>push(new_position, direction, execute)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> can_push:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> execute:
+</span></span><span style="display:flex;"><span> box <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>boxes<span style="color:#f92672">.</span>pop(position)
+</span></span><span style="display:flex;"><span> box<span style="color:#f92672">.</span>y <span style="color:#f92672">=</span> new_position<span style="color:#f92672">.</span>y
+</span></span><span style="display:flex;"><span> box<span style="color:#f92672">.</span>x <span style="color:#f92672">=</span> new_position<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes[box] <span style="color:#f92672">=</span> box
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">gps_boxes</span>(self):
+</span></span><span style="display:flex;"><span> total <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> box <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> total <span style="color:#f92672">+=</span> box<span style="color:#f92672">.</span>y <span style="color:#f92672">*</span> <span style="color:#ae81ff">100</span> <span style="color:#f92672">+</span> box<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> total
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">prompt</span>(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">while</span> <span style="color:#66d9ef">True</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>draw()
+</span></span><span style="display:flex;"><span> commands <span style="color:#f92672">=</span> input(<span style="color:#e6db74">&#34;Commands: &#34;</span>)<span style="color:#f92672">.</span>lower()<span style="color:#f92672">.</span>strip()
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> commands:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>commands <span style="color:#f92672">=</span> commands
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Problem2</span>(Problem):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, maze: list[str], commands: list[str]):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(maze, commands)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>max_x <span style="color:#f92672">*=</span> <span style="color:#ae81ff">2</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">setup</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>walls <span style="color:#f92672">=</span> set()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes <span style="color:#f92672">=</span> dict()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> y, line <span style="color:#f92672">in</span> enumerate(self<span style="color:#f92672">.</span>warehouse):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> x, char <span style="color:#f92672">in</span> enumerate(line):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;#&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>walls<span style="color:#f92672">.</span>add(Wall(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>))
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>walls<span style="color:#f92672">.</span>add(Wall(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>))
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;O&#34;</span>:
+</span></span><span style="display:flex;"><span> box1 <span style="color:#f92672">=</span> Box(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>, <span style="color:#e6db74">&#34;[&#34;</span>)
+</span></span><span style="display:flex;"><span> box2 <span style="color:#f92672">=</span> Box(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>, <span style="color:#e6db74">&#34;]&#34;</span>)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes[box1] <span style="color:#f92672">=</span> box1
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes[box2] <span style="color:#f92672">=</span> box2
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;@&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">=</span> Robot(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">push</span>(self, position: Vector2d, direction: Vector2d, execute: bool <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>):
+</span></span><span style="display:flex;"><span> new_position <span style="color:#f92672">=</span> position <span style="color:#f92672">+</span> direction
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> direction <span style="color:#f92672">in</span> [Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>), Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>)] <span style="color:#f92672">or</span> new_position <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>walls:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> super()<span style="color:#f92672">.</span>push(position, direction, execute)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> position <span style="color:#f92672">not</span> <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> box1, box2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>find_box(position)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> can_push1 <span style="color:#f92672">=</span> super()<span style="color:#f92672">.</span>push(box1, direction, <span style="color:#66d9ef">False</span>)
+</span></span><span style="display:flex;"><span> can_push2 <span style="color:#f92672">=</span> super()<span style="color:#f92672">.</span>push(box2, direction, <span style="color:#66d9ef">False</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> can_push1 <span style="color:#f92672">and</span> can_push2:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> execute:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>push_v(box1, box2, direction)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">find_box</span>(self, position):
+</span></span><span style="display:flex;"><span> box1 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>boxes[position]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> box1<span style="color:#f92672">.</span>symbol <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;[&#34;</span>:
+</span></span><span style="display:flex;"><span> box2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>boxes[position <span style="color:#f92672">+</span> Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>)]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>:
+</span></span><span style="display:flex;"><span> box2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>boxes[position <span style="color:#f92672">+</span> Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> box1, box2
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">push_v</span>(self, position1, position2, direction):
+</span></span><span style="display:flex;"><span> np1 <span style="color:#f92672">=</span> position1 <span style="color:#f92672">+</span> direction
+</span></span><span style="display:flex;"><span> np2 <span style="color:#f92672">=</span> position2 <span style="color:#f92672">+</span> direction
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> np1 <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>push_v(<span style="color:#f92672">*</span>self<span style="color:#f92672">.</span>find_box(np1), direction)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> np2 <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>push_v(<span style="color:#f92672">*</span>self<span style="color:#f92672">.</span>find_box(np2), direction)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> position, new_position <span style="color:#f92672">in</span> zip([position1, position2], [np1, np2]):
+</span></span><span style="display:flex;"><span> box <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>boxes<span style="color:#f92672">.</span>pop(position)
+</span></span><span style="display:flex;"><span> box<span style="color:#f92672">.</span>y <span style="color:#f92672">=</span> new_position<span style="color:#f92672">.</span>y
+</span></span><span style="display:flex;"><span> box<span style="color:#f92672">.</span>x <span style="color:#f92672">=</span> new_position<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes[box] <span style="color:#f92672">=</span> box
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">gps_boxes</span>(self):
+</span></span><span style="display:flex;"><span> total <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> box <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> box<span style="color:#f92672">.</span>symbol <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;[&#34;</span>:
+</span></span><span style="display:flex;"><span> total <span style="color:#f92672">+=</span> box<span style="color:#f92672">.</span>y <span style="color:#f92672">*</span> <span style="color:#ae81ff">100</span> <span style="color:#f92672">+</span> box<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> total
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problem <span style="color:#f92672">=</span> Problem(<span style="color:#f92672">*</span>read_input(TEST_INPUT))
+</span></span><span style="display:flex;"><span>problem<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problem<span style="color:#f92672">.</span>gps_boxes()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Part 1 - small input test:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">2028</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problem <span style="color:#f92672">=</span> Problem(<span style="color:#f92672">*</span>read_input(INPUT))
+</span></span><span style="display:flex;"><span>problem<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problem<span style="color:#f92672">.</span>gps_boxes()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Part 1 - large input test:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">10092</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problem <span style="color:#f92672">=</span> Problem(<span style="color:#f92672">*</span>read_input(read_file_without_blank_lines(<span style="color:#e6db74">&#34;15/input.txt&#34;</span>)))
+</span></span><span style="display:flex;"><span>problem<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problem<span style="color:#f92672">.</span>gps_boxes()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Part 1 - large input:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">1552463</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#75715e"># Part 2</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problem <span style="color:#f92672">=</span> Problem2(<span style="color:#f92672">*</span>read_input(INPUT))
+</span></span><span style="display:flex;"><span>problem<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problem<span style="color:#f92672">.</span>gps_boxes()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Part 2 - large input test:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">9021</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problem <span style="color:#f92672">=</span> Problem2(<span style="color:#f92672">*</span>read_input(read_file_without_blank_lines(<span style="color:#e6db74">&#34;15/input.txt&#34;</span>)))
+</span></span><span style="display:flex;"><span>problem<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problem<span style="color:#f92672">.</span>gps_boxes()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Part 2 - large input:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">1554058</span>
+</span></span></code></pre></div>
+ 2024-12-16T00:49:05+00:00
+
+
+ Nilo Ney Coutinho Menezes: Advent of code 2024 - Dia 15 de 25
+ https://blog.nilo.pro.br/posts/2024-12-16-adventofcode2024-dia15/
+ <p>O décimo quinto dia do Advent of Code foi ontem 15/12/2024!
+Se você caiu aqui hoje pela primeira vez, não deixe de ler os posts sobre o
+<a href="https://blog.nilo.pro.br/posts/2024-12-02-adventofcode2024-dia01/">Dia 1</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-03-adventofcode2024-dia02/">Dia 2</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-04-adventofcode2024-dia03/">Dia 3</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-05-adventofcode2024-dia04/">Dia 4</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-06-adventofcode2024-dia05/">Dia 5</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-07-adventofcode2024-dia06/">Dia 6</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-08-adventofcode2024-dia07/">Dia 7</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-09-adventofcode2024-dia08/">Dia 8</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-10-adventofcode2024-dia09/">Dia 9</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-11-adventofcode2024-dia10/">Dia 10</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-12-adventofcode2024-dia11/">Dia 11</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-13-adventofcode2024-dia12/">Dia 12</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-14-adventofcode2024-dia13/">Dia 13</a> e
+<a href="https://blog.nilo.pro.br/posts/2024-12-15-adventofcode2024-dia14/">Dia 14</a>.</p>
+<p>Classifiquei o <a href="https://adventofcode.com/2024/day/15">décimo quinto problema</a> como médio (parte 1) e médio (parte 2).</p>
+<h1 id="dicas">Dicas</h1>
+<ul>
+<li>A primeira parte do problema é ler o labirinto (armazém) e os comandos do robô.</li>
+<li>Utilize as entradas de exemplo para testar a solução.</li>
+<li>Você precisa detectar na entrada do labirinto, onde estão as paredes, o robô e as caixas.</li>
+<li>Os comandos estão em várias linhas, fica mais fácil se você concatená-las em uma só string.</li>
+<li>Você pode implementar as direções do robô (comandos) usando vetores.</li>
+<li>Vetores também podem ser utilizados para marcar as posições das caixas e paredes.</li>
+<li>As paredes circundam todo o armazém, você não precisa de preocupar com o robô saindo do armazém.</li>
+<li>Para verificar se o robô pode se mexer, verifique se a nova posição está livre. Neste caso, mova o robô, atualizando a posição.</li>
+<li>Se a posição seguinte for ocupada por uma parede, o robô não pode se mexer e fica no mesmo lugar.</li>
+<li>Para saber se pode mover as caixas, você pode tentar recursivamente todas as caixas a partir da direção do movimento. A cada caixa, verifique se ela pode se mover (espaço em branco na direção do movimento). Caso uma das caixas não possa ser movida, o movimento é cancelado.</li>
+<li>Lembre-se que o robô pode empurrar várias caixas ao mesmo tempo. Uma função recursiva pode ajudar a resolver este problema.</li>
+<li>Na parte 1, as caixas são do mesmo tamanho das paredes, ou seja, cada caixa ocupa uma posição. Na parte 2, cada caixa ocupa duas posições.</li>
+<li>A parte 2 é um pouco mais complexa, pois você pode mover mais que colunas e linhas de caixas, mas partes inteiras do armazém.</li>
+<li>O movimento horizontal é semelhante ao da parte 1, pois cada caixa tem altura de apenas um caractere, como na parte 1.</li>
+<li>Cada movimento na parte 2 pode mover até 2 caixas ao mesmo tempo. Mas esse movimento é feito em cascata, logo estas duas caixas podem mover outras 2, 3, ou mais caixas. Cada movimento de caixa pode movimentar a cada passo, zero, uma ou duas caixas, mas pode ocasionar o movimento de muitas outras.</li>
+<li>Na parte 2, você precisa verificar se o movimento inteiro é válido. Por exemplo, quando as caixas sendo empurradas formam um V ou ficam presas em uma parede, todas tem que parar de se mover. Se você implementar a função de movimento como na parte 1, verá que as caixas vão se mexer em vários pedaços, o que leva a resposta errada.</li>
+</ul>
+<h1 id="quanto-de-python-você-precisa-saber">Quanto de Python você precisa saber?</h1>
+<p>Os capítulos se referem ao meu livro <a href="https://python.nilo.pro.br/">Introdução à Programação com Python</a>.</p>
+<ul>
+<li>Listas - Capítulo 6</li>
+<li>Strings - Capítulo 7</li>
+<li>Funções recursivas - Capítulo 8</li>
+<li>Classes, herança - Capítulo 10</li>
+</ul>
+<h1 id="resposta">Resposta</h1>
+<p>Aqui o código que resolve o problema. Você verá as cicatrizes no código da troca entre a parte 1 e a parte 2, mas o código funciona relativamente bem.</p>
+<p>Você pode de usar as classes Armazém, Caixas, Parede, Robô, Problema e Problema2 para depurar sua solução. Os métodos <code>desenha</code>e <code>prompt</code> podem ajudar a interpretar e desenhar os comandos passo a passo. Embora não sejam chamados na resposta, eles estão no código caso você precise.</p>
+<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> sys
+</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> auxiliares <span style="color:#f92672">import</span> le_arquivo_sem_linhas_em_branco, Vector2d, Labirinto
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>ENTRADA_TESTE <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;&#34;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O.O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##@.O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#...O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.#.O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#...O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#......#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;^^&gt;&gt;&gt;vv&lt;v&gt;&gt;v&lt;&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>ENTRADA <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;&#34;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O..O.O#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#......O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.OO..O.O#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O@..O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#O#..O...#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#O..O..O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.OO.O.OO#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#....O...#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;vv&gt;^&lt;v^&gt;v&gt;^vv^v&gt;v&lt;&gt;v^v&lt;v&lt;^vv&lt;&lt;&lt;^&gt;&lt;&lt;&gt;&lt;&gt;&gt;v&lt;vvv&lt;&gt;^v^&gt;^&lt;&lt;&lt;&gt;&lt;&lt;v&lt;&lt;&lt;v^vv^v&gt;^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">vvv&lt;&lt;^&gt;^v^^&gt;&lt;&lt;&gt;&gt;&gt;&lt;&gt;^&lt;&lt;&gt;&lt;^vv^^&lt;&gt;vvv&lt;&gt;&gt;&lt;^^v&gt;^&gt;vv&lt;&gt;v&lt;&lt;&lt;&lt;v&lt;^v&gt;^&lt;^^&gt;&gt;&gt;^&lt;v&lt;v
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&gt;&lt;&gt;vv&gt;v^v^&lt;&gt;&gt;&lt;&gt;&gt;&gt;&gt;&lt;^^&gt;vv&gt;v&lt;^^^&gt;&gt;v^v^&lt;^^&gt;v^^&gt;v^&lt;^v&gt;v&lt;&gt;&gt;v^v^&lt;v&gt;v^^&lt;^^vv&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;&lt;v&lt;^&gt;&gt;^^^^&gt;&gt;&gt;v^&lt;&gt;vvv^&gt;&lt;v&lt;&lt;&lt;&gt;^^^vv^&lt;vvv&gt;^&gt;v&lt;^^^^v&lt;&gt;^&gt;vvvv&gt;&lt;&gt;&gt;v^&lt;&lt;^^^^^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^&gt;&lt;^&gt;&lt;&gt;&gt;&gt;&lt;&gt;^^&lt;&lt;^^v&gt;&gt;&gt;&lt;^&lt;v&gt;^&lt;vv&gt;&gt;v&gt;&gt;&gt;^v&gt;&lt;&gt;^v&gt;&lt;&lt;&lt;&lt;v&gt;&gt;v&lt;v&lt;v&gt;vvv&gt;^&lt;&gt;&lt;&lt;&gt;^&gt;&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^&gt;&gt;&lt;&gt;^v&lt;&gt;&lt;^vvv&lt;^^&lt;&gt;&lt;v&lt;&lt;&lt;&lt;&lt;&gt;&lt;^v&lt;&lt;&lt;&gt;&lt;&lt;&lt;^^&lt;v&lt;^^^&gt;&lt;^&gt;&gt;^&lt;v^&gt;&lt;&lt;&lt;^&gt;&gt;^v&lt;v^v&lt;v^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&gt;^&gt;&gt;^v&gt;vv&gt;^&lt;&lt;^v&lt;&gt;&gt;&lt;&lt;&gt;&lt;&lt;v&lt;&lt;v&gt;&lt;&gt;v&lt;^vv&lt;&lt;&lt;&gt;^^v^&gt;^^&gt;&gt;&gt;&lt;&lt;^v&gt;&gt;v^v&gt;&lt;^^&gt;&gt;^&lt;&gt;vv^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;&gt;&lt;^^&gt;^^^&lt;&gt;&lt;vvvvv^v&lt;v&lt;&lt;&gt;^v&lt;v&gt;v&lt;&lt;^&gt;&lt;&lt;&gt;&lt;&lt;&gt;&lt;&lt;&lt;^^&lt;&lt;&lt;^&lt;&lt;&gt;&gt;&lt;&lt;&gt;&lt;^^^&gt;^^&lt;&gt;^&gt;v&lt;&gt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^^&gt;vv&lt;^v^v&lt;vv&gt;^&lt;&gt;&lt;v&lt;^v&gt;^^^&gt;&gt;&gt;^^vvv^&gt;vvv&lt;&gt;&gt;&gt;^&lt;^&gt;&gt;&gt;&gt;&gt;^&lt;&lt;^v&gt;^vvv&lt;&gt;^&lt;&gt;&lt;&lt;v&gt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">v^^&gt;&gt;&gt;&lt;&lt;^^&lt;&gt;&gt;^v^&lt;v^vv&lt;&gt;v^&lt;&lt;&gt;^&lt;^v^v&gt;&lt;^&lt;&lt;&lt;&gt;&lt;&lt;^&lt;v&gt;&lt;v&lt;&gt;vv&gt;&gt;v&gt;&lt;v^&lt;vv&lt;&gt;v^&lt;&lt;^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">le_entrada</span>(entrada: str) <span style="color:#f92672">-&gt;</span> tuple[str, str]:
+</span></span><span style="display:flex;"><span> labirinto <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> comandos <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> linha <span style="color:#f92672">in</span> entrada<span style="color:#f92672">.</span>splitlines():
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> linha<span style="color:#f92672">.</span>strip():
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> linha<span style="color:#f92672">.</span>startswith(<span style="color:#e6db74">&#34;#&#34;</span>):
+</span></span><span style="display:flex;"><span> labirinto<span style="color:#f92672">.</span>append(linha)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>:
+</span></span><span style="display:flex;"><span> comandos<span style="color:#f92672">.</span>append(linha)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> labirinto, comandos
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Vector2dComSimbolo</span>(Vector2d):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x, símbolo):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>símbolo <span style="color:#f92672">=</span> símbolo
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __add__(self, other):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> Vector2dComSimbolo(self<span style="color:#f92672">.</span>y <span style="color:#f92672">+</span> other<span style="color:#f92672">.</span>y, self<span style="color:#f92672">.</span>x <span style="color:#f92672">+</span> other<span style="color:#f92672">.</span>x, self<span style="color:#f92672">.</span>símbolo)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __sub__(self, other):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> Vector2dComSimbolo(self<span style="color:#f92672">.</span>y <span style="color:#f92672">-</span> other<span style="color:#f92672">.</span>y, self<span style="color:#f92672">.</span>x <span style="color:#f92672">-</span> other<span style="color:#f92672">.</span>x, self<span style="color:#f92672">.</span>símbolo)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Caixas</span>(Vector2dComSimbolo):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x, símbolo<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;O&#34;</span>):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, símbolo)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Parede</span>(Vector2dComSimbolo):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, <span style="color:#e6db74">&#34;#&#34;</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Robô</span>(Vector2dComSimbolo):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, <span style="color:#e6db74">&#34;@&#34;</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Armazém</span>(Labirinto):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">inicie_desenho</span>(self, fundo<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;.&#34;</span>):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>fundo <span style="color:#f92672">=</span> fundo
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>desenho <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> _ <span style="color:#f92672">in</span> range(self<span style="color:#f92672">.</span>max_y):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>desenho<span style="color:#f92672">.</span>append([fundo] <span style="color:#f92672">*</span> self<span style="color:#f92672">.</span>max_x)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">desenhe</span>(self, objetos: list[Vector2dComSimbolo]):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> objeto <span style="color:#f92672">in</span> objetos:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>desenho[objeto<span style="color:#f92672">.</span>y][objeto<span style="color:#f92672">.</span>x] <span style="color:#f92672">=</span> objeto<span style="color:#f92672">.</span>símbolo
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">dump_desenho</span>(self, output<span style="color:#f92672">=</span>sys<span style="color:#f92672">.</span>stdout):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> linha <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>desenho:
+</span></span><span style="display:flex;"><span> print(<span style="color:#e6db74">&#34;&#34;</span><span style="color:#f92672">.</span>join(linha), file<span style="color:#f92672">=</span>output)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __iter__(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> iter(self<span style="color:#f92672">.</span>labirinto)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Problema</span>:
+</span></span><span style="display:flex;"><span> DIREÇÕES <span style="color:#f92672">=</span> {
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;&lt;&#34;</span>: Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;^&#34;</span>: Vector2d(<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;&gt;&#34;</span>: Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;v&#34;</span>: Vector2d(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>),
+</span></span><span style="display:flex;"><span> }
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, labirinto: list[str], comandos: list[str]):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>comandos <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;</span><span style="color:#f92672">.</span>join(comandos)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém <span style="color:#f92672">=</span> Armazém(labirinto)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>monta()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">monta</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>paredes <span style="color:#f92672">=</span> set()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas <span style="color:#f92672">=</span> dict()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> y, linha <span style="color:#f92672">in</span> enumerate(self<span style="color:#f92672">.</span>armazém):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> x, char <span style="color:#f92672">in</span> enumerate(linha):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;#&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>paredes<span style="color:#f92672">.</span>add(Parede(y, x))
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;O&#34;</span>:
+</span></span><span style="display:flex;"><span> caixa <span style="color:#f92672">=</span> Caixas(y, x)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas[caixa] <span style="color:#f92672">=</span> caixa
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;@&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">=</span> Robô(y, x)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">desenha</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>inicie_desenho()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>desenhe(self<span style="color:#f92672">.</span>paredes)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>desenhe(self<span style="color:#f92672">.</span>caixas)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>desenhe([self<span style="color:#f92672">.</span>robô])
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>dump_desenho()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">executa_comandos</span>(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> comando <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>comandos:
+</span></span><span style="display:flex;"><span> direção <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>DIREÇÕES[comando]
+</span></span><span style="display:flex;"><span> nova_posição <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">+</span> direção
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> nova_posição <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> self<span style="color:#f92672">.</span>empurra(nova_posição, direção):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">elif</span> nova_posição <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>paredes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">+=</span> direção
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">empurra</span>(self, posição: Vector2d, direção: Vector2d, executa: bool <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>):
+</span></span><span style="display:flex;"><span> nova_posição <span style="color:#f92672">=</span> posição <span style="color:#f92672">+</span> direção
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> nova_posição <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>paredes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> nova_posição <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> pode_empurrar <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>empurra(nova_posição, direção, executa)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> pode_empurrar:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> executa:
+</span></span><span style="display:flex;"><span> caixa <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>caixas<span style="color:#f92672">.</span>pop(posição)
+</span></span><span style="display:flex;"><span> caixa<span style="color:#f92672">.</span>y <span style="color:#f92672">=</span> nova_posição<span style="color:#f92672">.</span>y
+</span></span><span style="display:flex;"><span> caixa<span style="color:#f92672">.</span>x <span style="color:#f92672">=</span> nova_posição<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas[caixa] <span style="color:#f92672">=</span> caixa
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">gps_caixas</span>(self):
+</span></span><span style="display:flex;"><span> soma <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> caixas <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> soma <span style="color:#f92672">+=</span> caixas<span style="color:#f92672">.</span>y <span style="color:#f92672">*</span> <span style="color:#ae81ff">100</span> <span style="color:#f92672">+</span> caixas<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> soma
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">prompt</span>(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">while</span> <span style="color:#66d9ef">True</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>desenha()
+</span></span><span style="display:flex;"><span> comandos <span style="color:#f92672">=</span> input(<span style="color:#e6db74">&#34;Comandos: &#34;</span>)<span style="color:#f92672">.</span>lower()<span style="color:#f92672">.</span>strip()
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> comandos:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>comandos <span style="color:#f92672">=</span> comandos
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Problema2</span>(Problema):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, labirinto: list[str], comandos: list[str]):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(labirinto, comandos)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>max_x <span style="color:#f92672">*=</span> <span style="color:#ae81ff">2</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">monta</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>paredes <span style="color:#f92672">=</span> set()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas <span style="color:#f92672">=</span> dict()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> y, linha <span style="color:#f92672">in</span> enumerate(self<span style="color:#f92672">.</span>armazém):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> x, char <span style="color:#f92672">in</span> enumerate(linha):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;#&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>paredes<span style="color:#f92672">.</span>add(Parede(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>))
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>paredes<span style="color:#f92672">.</span>add(Parede(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>))
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;O&#34;</span>:
+</span></span><span style="display:flex;"><span> caixa1 <span style="color:#f92672">=</span> Caixas(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>, <span style="color:#e6db74">&#34;[&#34;</span>)
+</span></span><span style="display:flex;"><span> caixa2 <span style="color:#f92672">=</span> Caixas(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>, <span style="color:#e6db74">&#34;]&#34;</span>)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas[caixa1] <span style="color:#f92672">=</span> caixa1
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas[caixa2] <span style="color:#f92672">=</span> caixa2
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;@&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">=</span> Robô(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">empurra</span>(self, posição: Vector2d, direção: Vector2d, executa: bool <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>):
+</span></span><span style="display:flex;"><span> nova_posição <span style="color:#f92672">=</span> posição <span style="color:#f92672">+</span> direção
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> direção <span style="color:#f92672">in</span> [Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>), Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>)] <span style="color:#f92672">or</span> nova_posição <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>paredes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> super()<span style="color:#f92672">.</span>empurra(posição, direção, executa)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> posição <span style="color:#f92672">not</span> <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> caixa1, caixa2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>ache_caixa(posição)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> pode_empurrar1 <span style="color:#f92672">=</span> super()<span style="color:#f92672">.</span>empurra(caixa1, direção, <span style="color:#66d9ef">False</span>)
+</span></span><span style="display:flex;"><span> pode_empurrar2 <span style="color:#f92672">=</span> super()<span style="color:#f92672">.</span>empurra(caixa2, direção, <span style="color:#66d9ef">False</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> pode_empurrar1 <span style="color:#f92672">and</span> pode_empurrar2:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> executa:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>empurra_v(caixa1, caixa2, direção)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">ache_caixa</span>(self, posição):
+</span></span><span style="display:flex;"><span> caixa1 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>caixas[posição]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> caixa1<span style="color:#f92672">.</span>símbolo <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;[&#34;</span>:
+</span></span><span style="display:flex;"><span> caixa2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>caixas[posição <span style="color:#f92672">+</span> Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>)]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>:
+</span></span><span style="display:flex;"><span> caixa2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>caixas[posição <span style="color:#f92672">+</span> Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> caixa1, caixa2
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">empurra_v</span>(self, posição1, posição2, direção):
+</span></span><span style="display:flex;"><span> np1 <span style="color:#f92672">=</span> posição1 <span style="color:#f92672">+</span> direção
+</span></span><span style="display:flex;"><span> np2 <span style="color:#f92672">=</span> posição2 <span style="color:#f92672">+</span> direção
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> np1 <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>empurra_v(<span style="color:#f92672">*</span>self<span style="color:#f92672">.</span>ache_caixa(np1), direção)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> np2 <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>empurra_v(<span style="color:#f92672">*</span>self<span style="color:#f92672">.</span>ache_caixa(np2), direção)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> posição, nova_posição <span style="color:#f92672">in</span> zip([posição1, posição2], [np1, np2]):
+</span></span><span style="display:flex;"><span> caixa <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>caixas<span style="color:#f92672">.</span>pop(posição)
+</span></span><span style="display:flex;"><span> caixa<span style="color:#f92672">.</span>y <span style="color:#f92672">=</span> nova_posição<span style="color:#f92672">.</span>y
+</span></span><span style="display:flex;"><span> caixa<span style="color:#f92672">.</span>x <span style="color:#f92672">=</span> nova_posição<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas[caixa] <span style="color:#f92672">=</span> caixa
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">gps_caixas</span>(self):
+</span></span><span style="display:flex;"><span> soma <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> caixa <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> caixa<span style="color:#f92672">.</span>símbolo <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;[&#34;</span>:
+</span></span><span style="display:flex;"><span> soma <span style="color:#f92672">+=</span> caixa<span style="color:#f92672">.</span>y <span style="color:#f92672">*</span> <span style="color:#ae81ff">100</span> <span style="color:#f92672">+</span> caixa<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> soma
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problema <span style="color:#f92672">=</span> Problema(<span style="color:#f92672">*</span>le_entrada(ENTRADA_TESTE))
+</span></span><span style="display:flex;"><span>problema<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problema<span style="color:#f92672">.</span>gps_caixas()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Parte 1 - teste pequena entrada:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">2028</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problema <span style="color:#f92672">=</span> Problema(<span style="color:#f92672">*</span>le_entrada(ENTRADA))
+</span></span><span style="display:flex;"><span>problema<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problema<span style="color:#f92672">.</span>gps_caixas()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Parte 1 - teste entrada grande:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">10092</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problema <span style="color:#f92672">=</span> Problema(<span style="color:#f92672">*</span>le_entrada(le_arquivo_sem_linhas_em_branco(<span style="color:#e6db74">&#34;15/input.txt&#34;</span>)))
+</span></span><span style="display:flex;"><span>problema<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problema<span style="color:#f92672">.</span>gps_caixas()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Parte 1 - entrada grande:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">1552463</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#75715e"># Parte 2</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problema <span style="color:#f92672">=</span> Problema2(<span style="color:#f92672">*</span>le_entrada(ENTRADA))
+</span></span><span style="display:flex;"><span>problema<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problema<span style="color:#f92672">.</span>gps_caixas()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Parte 2 - teste entrada grande:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">9021</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problema <span style="color:#f92672">=</span> Problema2(<span style="color:#f92672">*</span>le_entrada(le_arquivo_sem_linhas_em_branco(<span style="color:#e6db74">&#34;15/input.txt&#34;</span>)))
+</span></span><span style="display:flex;"><span>problema<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problema<span style="color:#f92672">.</span>gps_caixas()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Parte 2 - entrada grande:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">1554058</span>
+</span></span></code></pre></div>
+ 2024-12-16T00:49:05+00:00
+
+
+ Rodrigo Delduca: My First Game with Carimbo, My Homemade Engine, For my Son
+ https://nullonerror.org/2024/10/08/my-first-game-with-carimbo/
+ <p><a href="https://youtu.be/nVRzCstyspQ"><img src="https://nullonerror.org/public/2024-10-08-my-first-game-with-carimbo/megarick.webp" alt="MegaRick Game" class="center" /></a>
+<a href="https://youtu.be/nVRzCstyspQ">https://youtu.be/nVRzCstyspQ</a></p>
+
+<h3 id="tldr">TL;DR</h3>
+
+<p>After a while, I decided to resume work on <a href="https://github.com/carimbolabs/carimbo">my game engine</a>.</p>
+
+<p>I made a game for my son. I could have used an existing engine, but I chose to write everything from scratch because code is like pasta—it’s much better and more enjoyable when homemade.</p>
+
+<p>This actually reminds me of when my father used to build my toys, from kites to wooden slides. Good memories. I have decided to do the same using what I know: programming.</p>
+
+<p>You can watch it here or <a href="https://carimbo.run">play it here</a>, (runs in the browser thanks to WebAssembly), use <code class="language-plaintext highlighter-rouge">A</code> and <code class="language-plaintext highlighter-rouge">D</code> for moving around and <code class="language-plaintext highlighter-rouge">space</code> to shoot.</p>
+
+<p>The engine was written in C++17, and the games are in Lua. The engine exposes some primitives to the Lua VM, which in turn coordinates the entire game.</p>
+
+<p><a href="https://github.com/willtobyte/megarick">Game source code</a> and <a href="https://github.com/willtobyte/carimbo">engine source code</a>.</p>
+
+<p>Artwork by <a href="https://www.fiverr.com/yuugenpixie">Aline Cardoso @yuugenpixie</a>.</p>
+
+<h3 id="result">Result</h3>
+
+<p><img src="https://nullonerror.org/public/2024-10-08-my-first-game-with-carimbo/play.jpeg" alt="Henrique Playing" class="center" /></p>
+
+<h3 id="going-deep">Going Deep</h3>
+
+<p>The entire game is scripted in <a href="https://www.lua.org">Lua</a>.</p>
+
+<p>First, we create the engine itself.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">local</span> <span class="n">engine</span> <span class="o">=</span> <span class="n">EngineFactory</span><span class="p">.</span><span class="n">new</span><span class="p">()</span>
+ <span class="p">:</span><span class="n">set_title</span><span class="p">(</span><span class="s2">"Mega Rick"</span><span class="p">)</span>
+ <span class="p">:</span><span class="n">set_width</span><span class="p">(</span><span class="mi">1920</span><span class="p">)</span>
+ <span class="p">:</span><span class="n">set_height</span><span class="p">(</span><span class="mi">1080</span><span class="p">)</span>
+ <span class="p">:</span><span class="n">set_fullscreen</span><span class="p">(</span><span class="kc">false</span><span class="p">)</span>
+ <span class="p">:</span><span class="n">create</span><span class="p">()</span>
+</code></pre></div></div>
+
+<p>Then we point to some resources for asset prefetching; they are loaded lazily to avoid impacting the <em>game loop</em>.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">engine</span><span class="p">:</span><span class="n">prefetch</span><span class="p">({</span>
+ <span class="s2">"blobs/bomb1.ogg"</span><span class="p">,</span>
+ <span class="s2">"blobs/bomb2.ogg"</span><span class="p">,</span>
+ <span class="s2">"blobs/bullet.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/candle.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/explosion.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/octopus.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/player.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/princess.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/ship.png"</span>
+<span class="p">})</span>
+</code></pre></div></div>
+
+<p>We created the postal service, which is something I borrowed from languages like <em>Erlang</em>, where an entity can send messages to any other. As we’ll see below, the bullet sends a hit message when it collides with the octopus, and the octopus, upon receiving the hit, decrements its own health.</p>
+
+<p>We also obtain the SoundManager to play sounds and spawn the main entities.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">local</span> <span class="n">postal</span> <span class="o">=</span> <span class="n">PostalService</span><span class="p">.</span><span class="n">new</span><span class="p">()</span>
+<span class="kd">local</span> <span class="n">soundmanager</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">soundmanager</span><span class="p">()</span>
+<span class="kd">local</span> <span class="n">octopus</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"octopus"</span><span class="p">)</span>
+<span class="kd">local</span> <span class="n">player</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"player"</span><span class="p">)</span>
+<span class="kd">local</span> <span class="n">princess</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"princess"</span><span class="p">)</span>
+<span class="kd">local</span> <span class="n">candle1</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"candle"</span><span class="p">)</span>
+<span class="kd">local</span> <span class="n">candle2</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"candle"</span><span class="p">)</span>
+</code></pre></div></div>
+
+<p>It’s not a triple-A title, but I’m very careful with resource management. A widely known technique is to create an object pool that you can reuse. In the code below, we limit it to a maximum of 3 bullets present on the screen.</p>
+
+<p>The <code class="language-plaintext highlighter-rouge">on_update</code> method is a callback called each loop in the engine. In the case of the bullet, I check if it has approached the green octopus. If so, I send a message to it indicating that it received a hit.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">_</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">3</span> <span class="k">do</span>
+ <span class="kd">local</span> <span class="n">bullet</span> <span class="o">=</span> <span class="n">entitymanager</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"bullet"</span><span class="p">)</span>
+ <span class="n">bullet</span><span class="p">.</span><span class="n">placement</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="o">-</span><span class="mi">128</span><span class="p">,</span> <span class="o">-</span><span class="mi">128</span><span class="p">)</span>
+ <span class="n">bullet</span><span class="p">:</span><span class="n">on_collision</span><span class="p">(</span><span class="s2">"octopus"</span><span class="p">,</span> <span class="k">function</span><span class="p">(</span><span class="n">self</span><span class="p">)</span>
+ <span class="n">self</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">unset</span><span class="p">()</span>
+ <span class="n">self</span><span class="p">.</span><span class="n">placement</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="o">-</span><span class="mi">128</span><span class="p">,</span> <span class="o">-</span><span class="mi">128</span><span class="p">)</span>
+ <span class="n">postalservice</span><span class="p">:</span><span class="n">post</span><span class="p">(</span><span class="n">Mail</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="n">octopus</span><span class="p">,</span> <span class="s2">"bullet"</span><span class="p">,</span> <span class="s2">"hit"</span><span class="p">))</span>
+ <span class="nb">table.insert</span><span class="p">(</span><span class="n">bullet_pool</span><span class="p">,</span> <span class="n">self</span><span class="p">)</span>
+ <span class="k">end</span><span class="p">)</span>
+ <span class="nb">table.insert</span><span class="p">(</span><span class="n">bullet_pool</span><span class="p">,</span> <span class="n">bullet</span><span class="p">)</span>
+ <span class="k">end</span>
+</code></pre></div></div>
+
+<p>On the octopus’s side, when it receives a “hit” message, the entity triggers an explosion animation, switches to attack mode, and decreases its health by 1. If it reaches 0, it changes the animation to “dead”.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">octopus</span> <span class="o">=</span> <span class="n">entitymanager</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"octopus"</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">.</span><span class="n">kv</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"life"</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">.</span><span class="n">placement</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="mi">1200</span><span class="p">,</span> <span class="mi">622</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"idle"</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">:</span><span class="n">on_mail</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="kd">local</span> <span class="n">behavior</span> <span class="o">=</span> <span class="n">behaviors</span><span class="p">[</span><span class="n">message</span><span class="p">]</span>
+ <span class="k">if</span> <span class="n">behavior</span> <span class="k">then</span>
+ <span class="n">behavior</span><span class="p">(</span><span class="n">self</span><span class="p">)</span>
+ <span class="k">end</span>
+<span class="k">end</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">.</span><span class="n">kv</span><span class="p">:</span><span class="n">subscribe</span><span class="p">(</span><span class="s2">"life"</span><span class="p">,</span> <span class="k">function</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
+ <span class="n">vitality</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="nb">string.format</span><span class="p">(</span><span class="s2">"%02d-"</span><span class="p">,</span> <span class="nb">math.max</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="mi">0</span><span class="p">)))</span>
+
+ <span class="k">if</span> <span class="n">value</span> <span class="o"><=</span> <span class="mi">0</span> <span class="k">then</span>
+ <span class="n">octopus</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"dead"</span><span class="p">)</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">timer</span> <span class="k">then</span>
+ <span class="n">timemanager</span><span class="p">:</span><span class="n">singleshot</span><span class="p">(</span><span class="mi">3000</span><span class="p">,</span> <span class="k">function</span><span class="p">()</span>
+ <span class="kd">local</span> <span class="k">function</span> <span class="nf">destroy</span><span class="p">(</span><span class="n">pool</span><span class="p">)</span>
+ <span class="k">for</span> <span class="n">i</span> <span class="o">=</span> <span class="o">#</span><span class="n">pool</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="k">do</span>
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">pool</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
+ <span class="nb">table.remove</span><span class="p">(</span><span class="n">pool</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
+ <span class="n">pool</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="kc">nil</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+
+ <span class="n">destroy</span><span class="p">(</span><span class="n">bullet_pool</span><span class="p">)</span>
+ <span class="n">destroy</span><span class="p">(</span><span class="n">explosion_pool</span><span class="p">)</span>
+ <span class="n">destroy</span><span class="p">(</span><span class="n">jet_pool</span><span class="p">)</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">octopus</span><span class="p">)</span>
+ <span class="n">octopus</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">player</span><span class="p">)</span>
+ <span class="n">player</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">princess</span><span class="p">)</span>
+ <span class="n">princess</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">candle1</span><span class="p">)</span>
+ <span class="n">candle1</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">candle2</span><span class="p">)</span>
+ <span class="n">candle2</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">floor</span><span class="p">)</span>
+ <span class="n">floor</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">overlay</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">vitality</span><span class="p">)</span>
+ <span class="n">vitality</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">overlay</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">online</span><span class="p">)</span>
+ <span class="n">online</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">scenemanager</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"gameover"</span><span class="p">)</span>
+
+ <span class="nb">collectgarbage</span><span class="p">(</span><span class="s2">"collect"</span><span class="p">)</span>
+
+ <span class="n">resourcemanager</span><span class="p">:</span><span class="n">flush</span><span class="p">()</span>
+ <span class="k">end</span><span class="p">)</span>
+ <span class="n">timer</span> <span class="o">=</span> <span class="kc">true</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+<span class="k">end</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">:</span><span class="n">on_animationfinished</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="n">self</span><span class="p">)</span>
+ <span class="n">self</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"idle"</span><span class="p">)</span>
+<span class="k">end</span><span class="p">)</span>
+</code></pre></div></div>
+
+<p>And finally, the player’s <code class="language-plaintext highlighter-rouge">on_update</code>, where I apply the velocity if any movement keys are pressed, or set the animation to idle if none are pressed.</p>
+
+<p>We also have the projectile firing, where the fire function is called, taking an instance of a bullet from the pool, placing it in a semi-random position, and applying velocity.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">function</span> <span class="nf">loop</span><span class="p">()</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">player</span> <span class="k">then</span>
+ <span class="k">return</span>
+ <span class="k">end</span>
+
+ <span class="n">player</span><span class="p">.</span><span class="n">velocity</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">0</span>
+
+ <span class="k">if</span> <span class="n">statemanager</span><span class="p">:</span><span class="n">is_keydown</span><span class="p">(</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">left</span><span class="p">)</span> <span class="k">then</span>
+ <span class="n">player</span><span class="p">.</span><span class="n">reflection</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="n">Reflection</span><span class="p">.</span><span class="n">horizontal</span><span class="p">)</span>
+ <span class="n">player</span><span class="p">.</span><span class="n">velocity</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="o">-</span><span class="mi">360</span>
+ <span class="k">elseif</span> <span class="n">statemanager</span><span class="p">:</span><span class="n">is_keydown</span><span class="p">(</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">right</span><span class="p">)</span> <span class="k">then</span>
+ <span class="n">player</span><span class="p">.</span><span class="n">reflection</span><span class="p">:</span><span class="n">unset</span><span class="p">()</span>
+ <span class="n">player</span><span class="p">.</span><span class="n">velocity</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">360</span>
+ <span class="k">end</span>
+
+ <span class="n">player</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="n">player</span><span class="p">.</span><span class="n">velocity</span><span class="p">.</span><span class="n">x</span> <span class="o">~=</span> <span class="mi">0</span> <span class="ow">and</span> <span class="s2">"run"</span> <span class="ow">or</span> <span class="s2">"idle"</span><span class="p">)</span>
+
+ <span class="k">if</span> <span class="n">statemanager</span><span class="p">:</span><span class="n">is_keydown</span><span class="p">(</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">space</span><span class="p">)</span> <span class="k">then</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">key_states</span><span class="p">[</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">space</span><span class="p">]</span> <span class="k">then</span>
+ <span class="n">key_states</span><span class="p">[</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">space</span><span class="p">]</span> <span class="o">=</span> <span class="kc">true</span>
+
+ <span class="c1">-- player.velocity.y = -360</span>
+
+ <span class="k">if</span> <span class="n">octopus</span><span class="p">.</span><span class="n">kv</span><span class="p">:</span><span class="n">get</span><span class="p">(</span><span class="s2">"life"</span><span class="p">)</span> <span class="o"><=</span> <span class="mi">0</span> <span class="k">then</span>
+ <span class="k">return</span>
+ <span class="k">end</span>
+
+ <span class="k">if</span> <span class="o">#</span><span class="n">bullet_pool</span> <span class="o">></span> <span class="mi">0</span> <span class="k">then</span>
+ <span class="kd">local</span> <span class="n">bullet</span> <span class="o">=</span> <span class="nb">table.remove</span><span class="p">(</span><span class="n">bullet_pool</span><span class="p">)</span>
+ <span class="kd">local</span> <span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="n">player</span><span class="p">.</span><span class="n">x</span> <span class="o">+</span> <span class="n">player</span><span class="p">.</span><span class="n">size</span><span class="p">.</span><span class="n">width</span><span class="p">)</span> <span class="o">+</span> <span class="mi">100</span>
+ <span class="kd">local</span> <span class="n">y</span> <span class="o">=</span> <span class="n">player</span><span class="p">.</span><span class="n">y</span> <span class="o">+</span> <span class="mi">10</span>
+ <span class="kd">local</span> <span class="n">offset_y</span> <span class="o">=</span> <span class="p">(</span><span class="nb">math.random</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span> <span class="o">*</span> <span class="mi">30</span>
+
+ <span class="n">bullet</span><span class="p">.</span><span class="n">placement</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="n">offset_y</span><span class="p">)</span>
+ <span class="n">bullet</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"default"</span><span class="p">)</span>
+ <span class="n">bullet</span><span class="p">.</span><span class="n">velocity</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">800</span>
+
+ <span class="kd">local</span> <span class="n">sound</span> <span class="o">=</span> <span class="s2">"bomb"</span> <span class="o">..</span> <span class="nb">math.random</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
+ <span class="n">soundmanager</span><span class="p">:</span><span class="n">play</span><span class="p">(</span><span class="n">sound</span><span class="p">)</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+ <span class="k">else</span>
+ <span class="n">key_states</span><span class="p">[</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">space</span><span class="p">]</span> <span class="o">=</span> <span class="kc">false</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</code></pre></div></div>
+
+<h3 id="in-the-end">In The End</h3>
+
+<p>In the end, he requested some <em>minor</em> tweaks, such as the projectile being the princess sprite, the target being the player, and the entity that shoots being the green octopus. 🤷♂️</p>
+
+<p><img src="https://nullonerror.org/public/2024-10-08-my-first-game-with-carimbo/tweaks.jpeg" alt="Tweaks" class="center" /></p>
+ 2024-10-08T00:00:00+00:00
+ Rodrigo Delduca
+
+
+ Rodrigo Delduca: How I saved a few dozen dollars by migrating my personal projects from the cloud to a Raspberry Pi
+ https://nullonerror.org/2024/06/05/how-i-saved-a-few-dozen-dollars-by-migrating-my-personal-projects-from-the-cloud-to-a-raspberry-pi/
+ <h3 id="intro">Intro</h3>
+
+<p>Since the first URL shorteners emerged, that’s always drawn me in for some reason, perhaps because I figured out on my own that they encoded the primary key in base36 (maybe).</p>
+
+<p>So around ~2010, I decided to make my own. It was called “encurta.ae” (make it short there in Portuguese), and it was based on AppEngine. Even back then, I was quite fond of serverless and such.</p>
+
+<p>It worked great locally, but when I deployed it, the datastore IDs were too large, causing the URLs to be too long.</p>
+
+<p>Fast-forward to nowadays, I’ve decided to bring this project to life again. This time with a twist: when shortening the URL, the shortener would take a screenshot of the website and embed Open Graph tags in the redirect URL. This way, when shared on a social network, the link would display a title, description, and thumbnail that accurately represent what the user is about to open.</p>
+
+<h3 id="stack">Stack</h3>
+
+<p>Typically, I use serverless for my personal projects because they scale to zero, thus eliminating costs when not in use. However, after working with serverless for many years, I’ve been wanting to experiment with a more grounded and down-to-earth approach to development and deployment.</p>
+
+<ul>
+ <li>I opted to use <a href="https://go.dev/">Go</a>, with several <a href="https://en.wikipedia.org/wiki/Coroutine">goroutines</a> performing tasks in parallel, and a purely written work queue mechanism.</li>
+ <li><a href="https://playwright.dev/">Playwright</a> & Chromium for automation and screenshot.</li>
+ <li><a href="https://sqlite.org/">SQLite</a> for the database (it’s simple)</li>
+ <li><a href="https://www.backblaze.com/">Backblaze</a> for storage</li>
+ <li><a href="https://bunny.net/">BunnyCDN</a> for cotent delivery</li>
+ <li><a href="https://www.papertrail.com/">Papertrail</a> for logging</li>
+</ul>
+
+<p>Deployment is done using Docker Compose, via SSH using the DOCKER_HOST environment variable pointing directly to a <a href="https://www.raspberrypi.com/">Raspberry Pi</a> that I had bought and never used before. Now it saves me $5 per month, and I can keep a limited number of projects running on it.</p>
+
+<p>And then you might ask: How do you expose it to the internet? I use <a href="https://www.cloudflare.com/products/tunnel/">Cloudflare Tunnel</a>; the setup is simple and creates a direct connection between the Raspberry Pi and the nearest <a href="https://en.wikipedia.org/wiki/Point_of_presence">Point of Presence</a>.</p>
+
+<p>This type of hosting is extremely advantageous because the server’s IP and/or ports are never revealed; it stays behind my firewall. Everything goes through Cloudflare.</p>
+
+<p>I have more than one Docker Compose file, and that’s what’s coolest. Locally, I run one, for deployment I instruct the Compose to read two others for logging and tunneling.</p>
+
+<p><code class="language-plaintext highlighter-rouge">docker-compose.yaml</code></p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span>
+ <span class="na">app</span><span class="pi">:</span>
+ <span class="na">build</span><span class="pi">:</span> <span class="s">.</span>
+ <span class="na">env_file</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">.env</span>
+ <span class="na">ports</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s2">"</span><span class="s">8000:8000"</span>
+ <span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span>
+ <span class="na">volumes</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">data:/data</span>
+ <span class="na">tmpfs</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">/tmp</span>
+<span class="na">volumes</span><span class="pi">:</span>
+ <span class="na">data</span><span class="pi">:</span>
+</code></pre></div></div>
+
+<p><code class="language-plaintext highlighter-rouge">docker-compose.logging.yaml</code></p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span>
+ <span class="na">app</span><span class="pi">:</span>
+ <span class="na">logging</span><span class="pi">:</span>
+ <span class="na">driver</span><span class="pi">:</span> <span class="s">syslog</span>
+ <span class="na">options</span><span class="pi">:</span>
+ <span class="na">syslog-address</span><span class="pi">:</span> <span class="s2">"</span><span class="s">udp://logs2.papertrailapp.com:XXX"</span>
+</code></pre></div></div>
+
+<p><code class="language-plaintext highlighter-rouge">docker-compose.cloudflare.yaml</code></p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span>
+ <span class="na">tunnel</span><span class="pi">:</span>
+ <span class="na">image</span><span class="pi">:</span> <span class="s">cloudflare/cloudflared</span>
+ <span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span>
+ <span class="na">command</span><span class="pi">:</span> <span class="s">tunnel run</span>
+ <span class="na">environment</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">TUNNEL_TOKEN=yourtoken</span>
+</code></pre></div></div>
+
+<p>Then for deploying</p>
+
+<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">DOCKER_HOST</span><span class="o">=</span>ssh://pi@192.168.0.10 docker compose <span class="nt">--file</span> docker-compose.yaml <span class="nt">--file</span> docker-compose.logging.yaml <span class="nt">--file</span> docker-compose.cloudflare.yaml up <span class="nt">--build</span> <span class="nt">--detach</span>
+</code></pre></div></div>
+
+<p>Example of the “worker queue”</p>
+
+<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">functions</span>
+
+<span class="k">import</span> <span class="p">(</span>
+ <span class="s">"context"</span>
+ <span class="s">"database/sql"</span>
+ <span class="s">"fmt"</span>
+ <span class="s">"os"</span>
+ <span class="s">"os/exec"</span>
+ <span class="s">"path/filepath"</span>
+ <span class="s">"sync"</span>
+ <span class="s">"time"</span>
+
+ <span class="n">google</span> <span class="s">"cloud.google.com/go/vision/apiv1"</span>
+ <span class="n">visionpb</span> <span class="s">"cloud.google.com/go/vision/v2/apiv1/visionpb"</span>
+ <span class="s">"github.com/martinlindhe/base36"</span>
+ <span class="s">"github.com/minio/minio-go/v7"</span>
+ <span class="s">"github.com/minio/minio-go/v7/pkg/credentials"</span>
+ <span class="s">"github.com/playwright-community/playwright-go"</span>
+ <span class="s">"go.uber.org/zap"</span>
+ <span class="s">"google.golang.org/api/option"</span>
+ <span class="n">log</span> <span class="s">"skhaz.dev/urlshortnen/logging"</span>
+<span class="p">)</span>
+
+<span class="k">var</span> <span class="p">(</span>
+ <span class="n">accessKeyID</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"BACKBLAZE_ACCESS_ID"</span><span class="p">)</span>
+ <span class="n">secretAccessKey</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"BACKBLAZE_APPLICATION_KEY"</span><span class="p">)</span>
+ <span class="n">bucket</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"BACKBLAZE_BUCKET"</span><span class="p">)</span>
+ <span class="n">endpoint</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"BACKBLAZE_ENDPOINT"</span><span class="p">)</span>
+ <span class="n">useSSL</span> <span class="o">=</span> <span class="no">true</span>
+ <span class="n">extension</span> <span class="o">=</span> <span class="s">"webp"</span>
+ <span class="n">mimetype</span> <span class="o">=</span> <span class="s">"image/webp"</span>
+ <span class="n">quality</span> <span class="o">=</span> <span class="s">"50"</span>
+<span class="p">)</span>
+
+<span class="k">type</span> <span class="n">WorkerFunctions</span> <span class="k">struct</span> <span class="p">{</span>
+ <span class="n">db</span> <span class="o">*</span><span class="n">sql</span><span class="o">.</span><span class="n">DB</span>
+ <span class="n">vision</span> <span class="o">*</span><span class="n">google</span><span class="o">.</span><span class="n">ImageAnnotatorClient</span>
+ <span class="n">mc</span> <span class="o">*</span><span class="n">minio</span><span class="o">.</span><span class="n">Client</span>
+ <span class="n">browser</span> <span class="n">playwright</span><span class="o">.</span><span class="n">BrowserContext</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="n">Worker</span><span class="p">(</span><span class="n">db</span> <span class="o">*</span><span class="n">sql</span><span class="o">.</span><span class="n">DB</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">defer</span> <span class="k">func</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">if</span> <span class="n">r</span> <span class="o">:=</span> <span class="nb">recover</span><span class="p">();</span> <span class="n">r</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"worker panic"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Any</span><span class="p">(</span><span class="s">"error"</span><span class="p">,</span> <span class="n">r</span><span class="p">))</span>
+ <span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span> <span class="o">*</span> <span class="m">10</span><span class="p">)</span>
+ <span class="k">go</span> <span class="n">Worker</span><span class="p">(</span><span class="n">db</span><span class="p">)</span>
+ <span class="p">}</span>
+ <span class="p">}()</span>
+
+ <span class="n">ctx</span> <span class="o">:=</span> <span class="n">context</span><span class="o">.</span><span class="n">Background</span><span class="p">()</span>
+
+ <span class="n">vision</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">google</span><span class="o">.</span><span class="n">NewImageAnnotatorClient</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">option</span><span class="o">.</span><span class="n">WithCredentialsJSON</span><span class="p">([]</span><span class="kt">byte</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"GOOGLE_CREDENTIALS"</span><span class="p">))))</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to create vision client"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">vision</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="n">mc</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">minio</span><span class="o">.</span><span class="n">New</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="o">&</span><span class="n">minio</span><span class="o">.</span><span class="n">Options</span><span class="p">{</span>
+ <span class="n">Creds</span><span class="o">:</span> <span class="n">credentials</span><span class="o">.</span><span class="n">NewStaticV4</span><span class="p">(</span><span class="n">accessKeyID</span><span class="p">,</span> <span class="n">secretAccessKey</span><span class="p">,</span> <span class="s">""</span><span class="p">),</span>
+ <span class="n">Secure</span><span class="o">:</span> <span class="n">useSSL</span><span class="p">,</span>
+ <span class="p">})</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to create minio client"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">pw</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">playwright</span><span class="o">.</span><span class="n">Run</span><span class="p">()</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to launch playwright"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="c">//nolint:golint,errcheck</span>
+ <span class="k">defer</span> <span class="n">pw</span><span class="o">.</span><span class="n">Stop</span><span class="p">()</span>
+
+ <span class="n">userDataDir</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">MkdirTemp</span><span class="p">(</span><span class="s">""</span><span class="p">,</span> <span class="s">"chromium"</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to create temporary directory"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">os</span><span class="o">.</span><span class="n">RemoveAll</span><span class="p">(</span><span class="n">userDataDir</span><span class="p">)</span>
+
+ <span class="n">browser</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">pw</span><span class="o">.</span><span class="n">Chromium</span><span class="o">.</span><span class="n">LaunchPersistentContext</span><span class="p">(</span><span class="n">userDataDir</span><span class="p">,</span> <span class="n">playwright</span><span class="o">.</span><span class="n">BrowserTypeLaunchPersistentContextOptions</span><span class="p">{</span>
+ <span class="n">Args</span><span class="o">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span>
+ <span class="s">"--headless=new"</span><span class="p">,</span>
+ <span class="s">"--no-zygote"</span><span class="p">,</span>
+ <span class="s">"--no-sandbox"</span><span class="p">,</span>
+ <span class="s">"--disable-gpu"</span><span class="p">,</span>
+ <span class="s">"--hide-scrollbars"</span><span class="p">,</span>
+ <span class="s">"--disable-setuid-sandbox"</span><span class="p">,</span>
+ <span class="s">"--disable-dev-shm-usage"</span><span class="p">,</span>
+ <span class="s">"--disable-extensions-except=/opt/extensions/ublock,/opt/extensions/isdncac"</span><span class="p">,</span>
+ <span class="s">"--load-extension=/opt/extensions/ublock,/opt/extensions/isdncac"</span><span class="p">,</span>
+ <span class="p">},</span>
+ <span class="n">DeviceScaleFactor</span><span class="o">:</span> <span class="n">playwright</span><span class="o">.</span><span class="n">Float</span><span class="p">(</span><span class="m">4.0</span><span class="p">),</span>
+ <span class="n">Headless</span><span class="o">:</span> <span class="n">playwright</span><span class="o">.</span><span class="n">Bool</span><span class="p">(</span><span class="no">false</span><span class="p">),</span>
+ <span class="n">Viewport</span><span class="o">:</span> <span class="o">&</span><span class="n">playwright</span><span class="o">.</span><span class="n">Size</span><span class="p">{</span>
+ <span class="n">Width</span><span class="o">:</span> <span class="m">1200</span><span class="p">,</span>
+ <span class="n">Height</span><span class="o">:</span> <span class="m">630</span><span class="p">,</span>
+ <span class="p">},</span>
+ <span class="n">UserAgent</span><span class="o">:</span> <span class="n">playwright</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="s">"Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Googlebot/2.1; +http://www.google.com/bot.html) Chrome/125.0.0.0 Safari/537.36"</span><span class="p">),</span>
+ <span class="p">})</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to launch chromium browser"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">browser</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="n">wf</span> <span class="o">:=</span> <span class="n">WorkerFunctions</span><span class="p">{</span>
+ <span class="n">db</span><span class="o">:</span> <span class="n">db</span><span class="p">,</span>
+ <span class="n">vision</span><span class="o">:</span> <span class="n">vision</span><span class="p">,</span>
+ <span class="n">mc</span><span class="o">:</span> <span class="n">mc</span><span class="p">,</span>
+ <span class="n">browser</span><span class="o">:</span> <span class="n">browser</span><span class="p">,</span>
+ <span class="p">}</span>
+
+ <span class="k">for</span> <span class="p">{</span>
+ <span class="n">start</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">Now</span><span class="p">()</span>
+
+ <span class="k">func</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">wg</span> <span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span>
+ <span class="n">rows</span> <span class="o">*</span><span class="n">sql</span><span class="o">.</span><span class="n">Rows</span>
+ <span class="n">err</span> <span class="kt">error</span>
+ <span class="p">)</span>
+
+ <span class="n">rows</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Query</span><span class="p">(</span><span class="s">"SELECT id, url FROM data WHERE ready = 0 ORDER BY created_at LIMIT 6"</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"error executing query"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">rows</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="k">for</span> <span class="n">rows</span><span class="o">.</span><span class="n">Next</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">var</span> <span class="n">id</span> <span class="kt">int64</span>
+ <span class="k">var</span> <span class="n">url</span> <span class="kt">string</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">=</span> <span class="n">rows</span><span class="o">.</span><span class="n">Scan</span><span class="p">(</span><span class="o">&</span><span class="n">id</span><span class="p">,</span> <span class="o">&</span><span class="n">url</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"error scanning row"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">wg</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
+ <span class="k">go</span> <span class="n">wf</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="o">&</span><span class="n">wg</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">id</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">rows</span><span class="o">.</span><span class="n">Err</span><span class="p">();</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"error during rows iteration"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">wg</span><span class="o">.</span><span class="n">Wait</span><span class="p">()</span>
+ <span class="p">}()</span>
+
+ <span class="n">elapsed</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">Since</span><span class="p">(</span><span class="n">start</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">remaining</span> <span class="o">:=</span> <span class="m">5</span><span class="o">*</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span> <span class="o">-</span> <span class="n">elapsed</span><span class="p">;</span> <span class="n">remaining</span> <span class="o">></span> <span class="m">0</span> <span class="p">{</span>
+ <span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">remaining</span><span class="p">)</span>
+ <span class="p">}</span>
+ <span class="p">}</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="p">(</span><span class="n">wf</span> <span class="o">*</span><span class="n">WorkerFunctions</span><span class="p">)</span> <span class="n">run</span><span class="p">(</span><span class="n">wg</span> <span class="o">*</span><span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span><span class="p">,</span> <span class="n">url</span> <span class="kt">string</span><span class="p">,</span> <span class="n">id</span> <span class="kt">int64</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">defer</span> <span class="n">wg</span><span class="o">.</span><span class="n">Done</span><span class="p">()</span>
+
+ <span class="k">var</span> <span class="n">message</span> <span class="kt">string</span>
+
+ <span class="k">if</span> <span class="n">id</span> <span class="o"><</span> <span class="m">0</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="s">"invalid id: id must be non-negative"</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">ctx</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="n">Background</span><span class="p">()</span>
+ <span class="n">short</span> <span class="o">=</span> <span class="n">base36</span><span class="o">.</span><span class="n">Encode</span><span class="p">(</span><span class="kt">uint64</span><span class="p">(</span><span class="n">id</span><span class="p">))</span>
+ <span class="p">)</span>
+
+ <span class="n">dir</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">MkdirTemp</span><span class="p">(</span><span class="s">""</span><span class="p">,</span> <span class="s">"screenshot"</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to create temporary directory: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">os</span><span class="o">.</span><span class="n">RemoveAll</span><span class="p">(</span><span class="n">dir</span><span class="p">)</span>
+
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">fileName</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"%s.%s"</span><span class="p">,</span> <span class="n">short</span><span class="p">,</span> <span class="n">extension</span><span class="p">)</span>
+ <span class="n">filePath</span> <span class="o">=</span> <span class="n">filepath</span><span class="o">.</span><span class="n">Join</span><span class="p">(</span><span class="n">dir</span><span class="p">,</span> <span class="n">fileName</span><span class="p">)</span>
+ <span class="p">)</span>
+
+ <span class="n">page</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">wf</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">NewPage</span><span class="p">()</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to create new page: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">page</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">page</span><span class="o">.</span><span class="n">Goto</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">playwright</span><span class="o">.</span><span class="n">PageGotoOptions</span><span class="p">{</span>
+ <span class="n">WaitUntil</span><span class="o">:</span> <span class="n">playwright</span><span class="o">.</span><span class="n">WaitUntilStateDomcontentloaded</span><span class="p">,</span>
+ <span class="p">});</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to navigate to url: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span> <span class="o">*</span> <span class="m">5</span><span class="p">)</span>
+
+ <span class="n">title</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">page</span><span class="o">.</span><span class="n">Title</span><span class="p">()</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"could not get title: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">description</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">page</span><span class="o">.</span><span class="n">Locator</span><span class="p">(</span><span class="s">`meta[name="description"]`</span><span class="p">)</span><span class="o">.</span><span class="n">GetAttribute</span><span class="p">(</span><span class="s">"content"</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Info</span><span class="p">(</span><span class="s">"could not get meta description"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="n">description</span> <span class="o">=</span> <span class="s">""</span>
+ <span class="p">}</span>
+
+ <span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">page</span><span class="o">.</span><span class="n">Screenshot</span><span class="p">(</span><span class="n">playwright</span><span class="o">.</span><span class="n">PageScreenshotOptions</span><span class="p">{</span>
+ <span class="n">Path</span><span class="o">:</span> <span class="n">playwright</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="n">filePath</span><span class="p">),</span>
+ <span class="p">});</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to create screenshot: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">fp</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">Open</span><span class="p">(</span><span class="n">filePath</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to open screenshot: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">fp</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="n">image</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">google</span><span class="o">.</span><span class="n">NewImageFromReader</span><span class="p">(</span><span class="n">fp</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to load screenshot: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">annotations</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">wf</span><span class="o">.</span><span class="n">vision</span><span class="o">.</span><span class="n">DetectSafeSearch</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">image</span><span class="p">,</span> <span class="no">nil</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to detect labels: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="k">if</span> <span class="n">annotations</span><span class="o">.</span><span class="n">Adult</span> <span class="o">>=</span> <span class="n">visionpb</span><span class="o">.</span><span class="n">Likelihood_POSSIBLE</span> <span class="o">||</span> <span class="n">annotations</span><span class="o">.</span><span class="n">Violence</span> <span class="o">>=</span> <span class="n">visionpb</span><span class="o">.</span><span class="n">Likelihood_POSSIBLE</span> <span class="o">||</span> <span class="n">annotations</span><span class="o">.</span><span class="n">Racy</span> <span class="o">>=</span> <span class="n">visionpb</span><span class="o">.</span><span class="n">Likelihood_POSSIBLE</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"site is not safe %v"</span><span class="p">,</span> <span class="n">annotations</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">cmd</span> <span class="o">:=</span> <span class="n">exec</span><span class="o">.</span><span class="n">Command</span><span class="p">(</span><span class="s">"convert"</span><span class="p">,</span> <span class="n">filePath</span><span class="p">,</span> <span class="s">"-resize"</span><span class="p">,</span> <span class="s">"50%"</span><span class="p">,</span> <span class="s">"-filter"</span><span class="p">,</span> <span class="s">"Lanczos"</span><span class="p">,</span> <span class="s">"-quality"</span><span class="p">,</span> <span class="n">quality</span><span class="p">,</span> <span class="n">filePath</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">cmd</span><span class="o">.</span><span class="n">Run</span><span class="p">();</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"error during image conversion: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">wf</span><span class="o">.</span><span class="n">mc</span><span class="o">.</span><span class="n">FPutObject</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">bucket</span><span class="p">,</span> <span class="n">fileName</span><span class="p">,</span> <span class="n">filePath</span><span class="p">,</span> <span class="n">minio</span><span class="o">.</span><span class="n">PutObjectOptions</span><span class="p">{</span><span class="n">ContentType</span><span class="o">:</span> <span class="n">mimetype</span><span class="p">})</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to upload file to minio: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">Exec</span><span class="p">(</span><span class="s">"UPDATE data SET ready = 1, title = ?, description = ? WHERE url = ?"</span><span class="p">,</span> <span class="n">title</span><span class="p">,</span> <span class="n">description</span><span class="p">,</span> <span class="n">url</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to update database record: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="k">go</span> <span class="n">warmup</span><span class="p">(</span><span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"%s/%s"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"DOMAIN"</span><span class="p">),</span> <span class="n">short</span><span class="p">))</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="n">setError</span><span class="p">(</span><span class="n">db</span> <span class="o">*</span><span class="n">sql</span><span class="o">.</span><span class="n">DB</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">db</span><span class="o">.</span><span class="n">Exec</span><span class="p">(</span><span class="s">"UPDATE data SET ready = 1, error = ? WHERE url = ?"</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">url</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to update database error record"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="p">}</span>
+<span class="p">}</span>
+</code></pre></div></div>
+
+<p>It’s noticeable that it took a bit more effort than if I had used a task queue. However, it turned out to be quite robust and easy to debug, and for that reason alone, it was worth it.</p>
+
+<h3 id="conclusion">Conclusion</h3>
+
+<p>In sharing the shortened link (<a href="https://takealook.pro/4MP">https://takealook.pro/4MP</a>) for my company <a href="https://ultratech.software/">Ultratech Software</a> on social networks, you can have a really nice preview, as seen below.</p>
+
+<p><img src="https://nullonerror.org/public/2024-06-05-how-i-saved-a-few-dozen-dollars-by-migrating-my-personal-projects-from-the-cloud-to-a-raspberry-pi/takealook.pro.avif" alt="takealook.pro" class="center" /></p>
+
+<p><a href="https://takealook.pro/">Take A Look</a></p>
+ 2024-06-05T00:00:00+00:00
+ Rodrigo Delduca
+
+
+ Rodrigo Delduca: My 2024 Setup
+ https://nullonerror.org/2024/03/20/my-2024-setup/
+ <h3 id="software--productivity">Software & Productivity</h3>
+
+<ol>
+ <li><a href="https://brew.sh/">brew</a> as package manager</li>
+ <li><a href="https://asdf-vm.com/">asdf</a> as language & tools management</li>
+ <li><a href="https://zed.dev/">Zed</a> as code editor</li>
+ <li><a href="https://bear.app/">Bear</a> as notes keeping</li>
+ <li><a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> as browser</li>
+ <li><a href="https://kagi.com/">Kagi</a> as search engine</li>
+ <li><a href="https://chat.openai.com/">ChatGPT</a> as AI assistant</li>
+ <li><a href="https://www.usebruno.com/">Bruno</a> as API client</li>
+ <li><a href="https://orbstack.dev/">OrbStack</a> as Docker desktop replacement, plus virtual machines</li>
+ <li>Apple ecosystem for mail, calendar, reminders, music, messaging, and more.</li>
+</ol>
+
+<h3 id="degoogling">Degoogling</h3>
+
+<p>As can be seen, I am using Kagi, which delivers excellent results without any garbage or ads, instead of Google. I also use email with my own domain through Apple’s iCloud email system, which allows for custom domains, as well as calendar and other features, all outside Google’s domain. Unfortunately, I have to maintain my Google account to use YouTube and occasionally for sites that do not allow creating accounts with a username and password, but only through Single Sign-On (SSO).</p>
+ 2024-03-20T00:00:00+00:00
+ Rodrigo Delduca
+
+
+ Rodrigo Delduca: Mastering Atomic Complex Operations in Redis: Unlocking High-Performance Data Handling With Lua
+ https://nullonerror.org/2024/03/06/mastering-atomic-complex-operations-in-redis-unlocking-high-performance-data-handling-with-lua/
+ <p>I have been using Redis for its most basic operations such as caching with expiration and counters, as well as slightly more complex operations like <a href="https://redis.io/docs/data-types/sorted-sets/">zsets</a>, <a href="https://redis.io/commands/zrangebyscore/">zrangebyscore</a>, and <a href="https://redis.io/commands/scan/">scan</a>, or as a <a href="https://redis.io/commands/rpoplpush/">queue</a> for probably more than 6 years on a daily basis.</p>
+
+<p>I won’t discuss Postgres today, as we can achieve similar results. Today, I will introduce something different: the possibility of scripting Redis with <a href="https://en.wikipedia.org/wiki/Lua_%28programming_language%29">Lua</a>.</p>
+
+<p>Months ago, I found myself facing the following problem: I needed to retrieve a JSON string from Redis using a specific key, merge it with the local JSON, and then submit it back to Redis as a string.</p>
+
+<p>This would have been a trivial task if not for the following scenario: On the “other” side, there was a process that consumed the same JSON and then, in an atomic pipeline operation, deleted it.</p>
+
+<p>Given this context, I could not perform the JSON merge on the client side, as it would not be atomic. This is where my native language, Lua (incidentally, I have a dog with the same name, and another dog named Python, yes, I’m a nerd, how did you guess?), comes into play.</p>
+
+<p>With Lua, operations are atomic and accelerated with JIT, making it possible to do a myriad of things, one very classic example being a rate limiter by IP address.</p>
+
+<p>Let’s see how my solution turned out:</p>
+
+<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">script</span> <span class="o">=</span> <span class="s2">`
+ local key = KEYS[1]
+ local input = ARGV[1]
+ local existing = redis.call('GET', key)
+
+ if existing then
+ local existingJson = cjson.decode(existing)
+ local inputJson = cjson.decode(input)
+ for _, v in ipairs(inputJson) do
+ table.insert(existingJson, v)
+ end
+ input = cjson.encode(existingJson)
+ end
+
+ redis.call('SET', key, input)
+ return input
+ `</span><span class="p">;</span>
+
+<span class="kd">const</span> <span class="nx">key</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">...</span><span class="dl">"</span><span class="p">;</span>
+
+<span class="k">await</span> <span class="nx">redis</span><span class="p">.</span><span class="nb">eval</span><span class="p">(</span><span class="nx">script</span><span class="p">,</span> <span class="p">{</span>
+ <span class="na">keys</span><span class="p">:</span> <span class="p">[</span><span class="nx">key</span><span class="p">],</span>
+ <span class="na">arguments</span><span class="p">:</span> <span class="p">[</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">schema</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">data</span><span class="p">))],</span>
+<span class="p">});</span>
+</code></pre></div></div>
+
+<p>And on the other side, the consumer can retrieve the JSON and delete it, atomically within a pipeline:</p>
+
+<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">pipeline</span> <span class="o">=</span> <span class="nx">redis</span><span class="p">.</span><span class="nx">multi</span><span class="p">();</span>
+<span class="nx">pipeline</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">...</span><span class="dl">"</span><span class="p">);</span>
+<span class="nx">pipeline</span><span class="p">.</span><span class="nx">del</span><span class="p">(</span><span class="dl">"</span><span class="s2">...</span><span class="dl">"</span><span class="p">);</span>
+<span class="kd">const</span> <span class="p">[</span><span class="nx">jsonStr</span><span class="p">]</span> <span class="o">=</span> <span class="nx">pipeline</span><span class="p">.</span><span class="nx">exec</span><span class="p">();</span>
+</code></pre></div></div>
+
+<p>In this way, all operations are atomic.</p>
+
+<p><img src="https://nullonerror.org/public/2024-03-06-mastering-atomic-complex-operations-in-redis-unlocking-high-performance-data-handling-with-lua/elmo.jpg" alt="Elmo reacting to the atomicity of combining Lua with Redis" class="center" /></p>
+ 2024-03-06T00:00:00+00:00
+ Rodrigo Delduca
+
+
+ Rodrigo Delduca: 2023 was a productive year
+ https://nullonerror.org/2023/12/18/2023-was-a-productive-year/
+ <p>The year started and I joined <a href="https://fueled.com/">Fueled</a>, an agency specializing in mobile apps and web systems, as a lead engineer.</p>
+
+<p>At Fueled, so far I’ve had the opportunity to work with three of their biggest clients to date. One of them is the world’s most famous lingerie brand, for which I developed a ‘social network’ that currently has about 5 million users. Then, I worked on a credit platform for elders in the United States that garnered 500,000 users at launch. Currently, I am working on a personal time management project for one of the top coaches in the field.</p>
+
+<p>In parallel, I wrote five articles for Fueled’s technical blog. They are:</p>
+
+<ul>
+ <li><a href="https://fueled.com/the-cache/posts/backend/working-with-compressed-binary-data-on-aws-iot-core/">Working With Compressed Binary Data on AWS Iot Core</a></li>
+ <li><a href="https://fueled.com/the-cache/posts/backend/combating-telegram-spam-with-a-serverless-bot/">Combating Telegram Spam With a Serverless Bot</a></li>
+ <li><a href="https://fueled.com/the-cache/posts/backend/clean-architecture-with-fastapi/">Clean Architecture with FastAPI</a></li>
+ <li><a href="https://fueled.com/the-cache/posts/backend/devops/cloud-workspace/">Secure Cloud Workspace with VSCode and Tailscale</a></li>
+ <li><a href="https://fueled.com/the-cache/posts/backend/golang/lint-all-the-things-with-golangci-lint/">Lint all the things with golangci-lint!</a></li>
+</ul>
+
+<p>I also wrote another five for my personal blog (this one). They are:</p>
+
+<ul>
+ <li><a href="https://nullonerror.org/2023/01/19/gotta-go-fast-with-docker-on-macos/">Gotta go fast with Docker on macOS</a></li>
+ <li><a href="https://nullonerror.org/2023/10/17/working-with-compressed-binary-data-on-aws-iot-core/">Working with compressed binary data on AWS IoT Core</a></li>
+ <li><a href="https://nullonerror.org/2023/11/01/what-i-ve-been-automating-with-github-actions-an-automated-life/">What I’ve been automating with GitHub Actions, an automated life</a></li>
+ <li><a href="https://nullonerror.org/2023/11/12/flipping-bits-whilst-updating-pixels/">Flipping Bits Whilst Updating Pixels</a></li>
+ <li><a href="https://nullonerror.org/2023/12/01/executing-untrusted-code-in-serverless-environments-a-telegram-bot-for-running-c-and-c++-code-on-cloud-run/">Executing Untrusted Code in Serverless Environments: A Telegram Bot for Running C and C++ Code on Cloud Run</a></li>
+</ul>
+
+<p>As if that wasn’t enough, I also developed and maintained personal projects, one of which, <a href="https://github.com/flippingpixels/carimbo">Carimbo</a>, was the one I devoted most of my time to. They are:</p>
+
+<ul>
+ <li><a href="https://github.com/flippingpixels/carimbo">Carimbo</a> - A 2D engine written in C++ using SDL and Lua as a scripting language.</li>
+ <li><a href="https://github.com/flippingpixels/play">Carimbo Play</a> - A web server to serve a combination of Carimbo releases and games.</li>
+ <li><a href="https://github.com/skhaz/grammateus">Grammateus</a> - An assistant that summarizes Twitch streams using OpenAI, written in Go.</li>
+ <li><a href="https://github.com/skhaz/neo-compiler-and-runner">Neo Compiler Bot</a> - A Telegram bot capable of compiling and executing unsafe code in the cloud.</li>
+ <li><a href="https://github.com/skhaz/freudian-slip">Freudian Slip Bot</a> - A Telegram bot designed to detect slip-ups between the lines.</li>
+</ul>
+
+<p>And many other small projects.</p>
+
+<p>As a result.</p>
+
+<p><img src="https://nullonerror.org/public/2023-12-18-2023-was-a-productive-year/github.png" alt="GitHub Stats" /></p>
+
+<p>Let’s see if 2024 will surpass it. Happy new year everyone!</p>
+ 2023-12-18T00:00:00+00:00
+ Rodrigo Delduca
+
+
+ Rodrigo Delduca: Executing Untrusted Code in Serverless Environments: A Telegram Bot for Running C and C++ Code on Cloud Run
+ https://nullonerror.org/2023/12/01/executing-untrusted-code-in-serverless-environments-a-telegram-bot-for-running-c-and-c++-code-on-cloud-run/
+ <h2 id="intro">Intro</h2>
+
+<p>I enjoy experimenting and writing Telegram bots for programming groups I participate in. In two groups, people frequently ask about C or C++ code, seeking help, examples, and more. Instead of using online tools like <a href="https://godbolt.org/">Godbolt</a> (Compiler Explorer), they prefer sending their code directly in messages.</p>
+
+<p>I had previously created such a bot using a Flask webserver, which communicated with another container through <a href="https://www.jsonrpc.org/">JSON-RPC</a>. It worked well but occasionally had issues.</p>
+
+<p>With the rise of <a href="https://en.wikipedia.org/wiki/Large_language_model">LLM</a>, I switched to using OpenAI, but many users complained about the unconventional results, which was amusing.</p>
+
+<p>Recently, while working on a project named <a href="https://github.com/flippingpixels/carimbo">Carimbo</a>, I started exploring <a href="https://webassembly.org/">WebAssembly</a>. I realized it could be ideal for running untrusted code. Initially, I considered using <code class="language-plaintext highlighter-rouge">isolated-vm</code> with WebAssembly, but I was quite satisfied with <a href="https://wasmtime.dev/">Wasmtime</a>. It offered options to limit CPU time and RAM usage, among other features.</p>
+
+<h2 id="cgroups">Cgroups</h2>
+
+<p>Any experienced developer would likely suggest using <a href="https://en.wikipedia.org/wiki/Cgroups">cgroups</a> and <a href="https://en.wikipedia.org/wiki/Linux_namespaces">namespaces</a>, which are indeed superior options. However, I prefer not to incur the costs of VMs or keep a machine running 24/7 at my home. This is primarily because <a href="https://cloud.google.com/run">Cloud Run</a>, based on <a href="https://www.docker.com/">Docker</a>, already utilizes cgroups, and to my knowledge, nested cgroups aren’t possible.</p>
+
+<p>Cloud Run offers me several advantages. Without delving into too much detail, it’s a serverless platform built on top of <a href="https://kubernetes.io/">Kubernetes</a>, employing <a href="https://gvisor.dev/">gVisor</a> for an added security layer. You don’t need to handle Kubernetes directly, but the option for fine-tuning is available, which I will discuss in this article.</p>
+
+<h2 id="the-bot">The Bot</h2>
+
+<p>Unlike in my previous work <a href="https://nullonerror.org/2021/01/08/hosting-telegram-bots-on-google-cloud-run/">Hosting Telegram bots on Cloud Run for free</a>, this time I will not use <a href="https://flask.palletsprojects.com">Flask</a>, but instead, I will directly employ <a href="https://www.starlette.io/">Starlette</a>. Starlette is an asynchronous framework for Python. One of the main reasons for this migration is to utilize <a href="https://docs.python.org/3/library/asyncio.html">asyncio</a>, which will enable handling more requests. Additionally, the python-telegram-robot library has shifted to this asynchronous model, aligning with this change.</p>
+
+<p>Let’s start with the Dockerfile.</p>
+
+<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="w"> </span><span class="s">python:3.12-slim-bookworm</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">base</span>
+
+<span class="k">ENV</span><span class="s"> PIP_DISABLE_PIP_VERSION_CHECK 1</span>
+<span class="k">ENV</span><span class="s"> PYTHONUNBUFFERED 1</span>
+<span class="k">ENV</span><span class="s"> PYTHONDONTWRITEBYTECODE 1</span>
+<span class="k">ENV</span><span class="s"> EMSDK=/emsdk</span>
+<span class="k">ENV</span><span class="s"> PATH=/emsdk:/emsdk/upstream/emscripten:/opt/venv/bin:$PATH</span>
+
+<span class="k">FROM</span><span class="w"> </span><span class="s">base</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">builder</span>
+<span class="k">RUN </span>python <span class="nt">-m</span> venv /opt/venv
+<span class="k">COPY</span><span class="s"> requirements.txt .</span>
+<span class="k">RUN </span>pip <span class="nb">install</span> <span class="nt">--no-cache-dir</span> <span class="nt">--requirement</span> requirements.txt
+
+<span class="k">FROM</span><span class="s"> base</span>
+<span class="k">WORKDIR</span><span class="s"> /opt/app</span>
+
+<span class="c"># Let's steal this entire directory from the official Emscripten image.</span>
+<span class="k">COPY</span><span class="s"> --from=emscripten/emsdk:3.1.49 /emsdk /emsdk</span>
+<span class="k">COPY</span><span class="s"> --from=builder /opt/venv /opt/venv</span>
+<span class="k">COPY</span><span class="s"> . .</span>
+
+<span class="k">RUN </span>useradd <span class="nt">-r</span> user
+<span class="k">USER</span><span class="s"> user</span>
+
+<span class="c"># Instead of Gunicorn, we will use Uvicorn, which is an ASGI web server implementation for Python.</span>
+<span class="k">CMD</span><span class="s"> exec uvicorn main:app --host 0.0.0.0 --port $PORT --workers 8 --timeout-keep-alive 600 --timeout-graceful-shutdown 600</span>
+</code></pre></div></div>
+
+<p>The main differences are that we steal an entire directory from the Emscripten Docker image, which saves us from having to build in the image, which is excellent. We also use Uvicorn, an ASGI web server that allows direct use of asyncio.</p>
+
+<p>Now let’s see how it goes with handling the incoming requests.</p>
+
+<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">equals</span><span class="p">(</span><span class="n">left</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="bp">None</span><span class="p">,</span> <span class="n">right</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="bp">None</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
+ <span class="s">"""
+ Compare two strings using a consistent amount of time to avoid timing attacks.
+ """</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">left</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">right</span><span class="p">:</span>
+ <span class="k">return</span> <span class="bp">False</span>
+
+ <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">left</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">right</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">False</span>
+
+ <span class="k">for</span> <span class="n">c1</span><span class="p">,</span> <span class="n">c2</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">left</span><span class="p">,</span> <span class="n">right</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">c1</span> <span class="o">!=</span> <span class="n">c2</span><span class="p">:</span>
+ <span class="k">return</span> <span class="bp">False</span>
+
+ <span class="k">return</span> <span class="bp">True</span>
+
+
+<span class="k">async</span> <span class="k">def</span> <span class="nf">webhook</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">Request</span><span class="p">):</span>
+ <span class="s">"""
+ Entry point for requests coming from Telegram.
+ """</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">equals</span><span class="p">(</span>
+ <span class="n">request</span><span class="p">.</span><span class="n">headers</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"X-Telegram-Bot-Api-Secret-Token"</span><span class="p">),</span>
+ <span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">[</span><span class="s">"SECRET"</span><span class="p">],</span>
+ <span class="p">):</span>
+ <span class="c1"># This section prevents false calls, only this application and Telegram know the secret.
+</span> <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">401</span><span class="p">)</span>
+
+ <span class="n">payload</span> <span class="o">=</span> <span class="k">await</span> <span class="n">request</span><span class="p">.</span><span class="n">json</span><span class="p">()</span>
+
+ <span class="c1"># Where the bot becomes operational, the JSON is passed to the application, which in turn processes the request.
+</span> <span class="k">async</span> <span class="k">with</span> <span class="n">application</span><span class="p">:</span>
+ <span class="k">await</span> <span class="n">application</span><span class="p">.</span><span class="n">process_update</span><span class="p">(</span><span class="n">Update</span><span class="p">.</span><span class="n">de_json</span><span class="p">(</span><span class="n">payload</span><span class="p">,</span> <span class="n">application</span><span class="p">.</span><span class="n">bot</span><span class="p">))</span>
+
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">200</span><span class="p">)</span>
+
+
+<span class="n">app</span> <span class="o">=</span> <span class="n">Starlette</span><span class="p">(</span>
+ <span class="n">routes</span><span class="o">=</span><span class="p">[</span>
+ <span class="n">Route</span><span class="p">(</span><span class="s">"/"</span><span class="p">,</span> <span class="n">webhook</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">"POST"</span><span class="p">]),</span>
+ <span class="p">],</span>
+<span class="p">)</span>
+</code></pre></div></div>
+
+<p>Finally, we have the handler for messages that start with <code class="language-plaintext highlighter-rouge">/run</code>.</p>
+
+<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="k">def</span> <span class="nf">on_run</span><span class="p">(</span><span class="n">update</span><span class="p">:</span> <span class="n">Update</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="n">ContextTypes</span><span class="p">.</span><span class="n">DEFAULT_TYPE</span><span class="p">)</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">update</span><span class="p">.</span><span class="n">message</span><span class="p">.</span><span class="n">reply_to_message</span> <span class="ow">or</span> <span class="n">update</span><span class="p">.</span><span class="n">message</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">message</span><span class="p">:</span>
+ <span class="k">return</span>
+
+ <span class="n">text</span> <span class="o">=</span> <span class="n">message</span><span class="p">.</span><span class="n">text</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">text</span><span class="p">:</span>
+ <span class="k">return</span>
+
+ <span class="n">text</span> <span class="o">=</span> <span class="n">text</span><span class="p">.</span><span class="n">lstrip</span><span class="p">(</span><span class="s">"/run"</span><span class="p">)</span>
+
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">text</span><span class="p">:</span>
+ <span class="k">await</span> <span class="n">message</span><span class="p">.</span><span class="n">reply_text</span><span class="p">(</span><span class="s">"Luke, I need the code for the Death Star's system."</span><span class="p">)</span>
+ <span class="k">return</span>
+
+ <span class="k">try</span><span class="p">:</span>
+ <span class="c1"># All the code is asynchronous, while the 'run' function is not. Therefore, we execute it in a thread.
+</span> <span class="n">coro</span> <span class="o">=</span> <span class="n">asyncio</span><span class="p">.</span><span class="n">to_thread</span><span class="p">(</span><span class="n">run</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span>
+
+ <span class="c1"># We execute the thread as a coroutine and limit its execution to 30 seconds.
+</span> <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">asyncio</span><span class="p">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">coro</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span>
+
+ <span class="c1"># Below, we prevent flooding in groups by placing very long messages into a bucket and returning the public URL.
+</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">></span> <span class="mi">64</span><span class="p">:</span>
+ <span class="n">blob</span> <span class="o">=</span> <span class="n">bucket</span><span class="p">.</span><span class="n">blob</span><span class="p">(</span><span class="n">hashlib</span><span class="p">.</span><span class="n">sha256</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">text</span><span class="p">).</span><span class="n">encode</span><span class="p">()).</span><span class="n">hexdigest</span><span class="p">())</span>
+ <span class="n">blob</span><span class="p">.</span><span class="n">upload_from_string</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
+ <span class="n">blob</span><span class="p">.</span><span class="n">make_public</span><span class="p">()</span>
+
+ <span class="n">result</span> <span class="o">=</span> <span class="n">blob</span><span class="p">.</span><span class="n">public_url</span>
+
+ <span class="c1"># Respond to the message with the result, which can be either an error or a success.
+</span> <span class="k">await</span> <span class="n">message</span><span class="p">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">asyncio</span><span class="p">.</span><span class="nb">TimeoutError</span><span class="p">:</span>
+ <span class="c1"># If the code exceeds the time limit or takes too long to compile, we return some emojis.
+</span> <span class="k">await</span> <span class="n">message</span><span class="p">.</span><span class="n">reply_text</span><span class="p">(</span><span class="s">"⏰😮💨"</span><span class="p">)</span>
+</code></pre></div></div>
+
+<h2 id="running-untrusted-code">Running Untrusted Code</h2>
+
+<p>Each request to execute code is compiled using em++, an ‘alias’ for clang++, targeting WebAssembly, and then executed with the <a href="https://wasi.dev/">WASI</a> runtime. Each execution runs separately and in a thread-safe manner in its own directory. While I could limit CPU usage (fuel) and memory usage, as indicated by the commented lines, in my case I opted for a container with 4GB of RAM and 4 vCPUs, which is more than sufficient given that I configured Run to accept only 8 connections per instance.</p>
+
+<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="n">source</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
+ <span class="k">with</span> <span class="n">TemporaryDirectory</span><span class="p">()</span> <span class="k">as</span> <span class="n">path</span><span class="p">:</span>
+ <span class="n">os</span><span class="p">.</span><span class="n">chdir</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
+
+ <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"main.cpp"</span><span class="p">,</span> <span class="s">"w+t"</span><span class="p">)</span> <span class="k">as</span> <span class="n">main</span><span class="p">:</span>
+ <span class="n">main</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">source</span><span class="p">)</span>
+ <span class="n">main</span><span class="p">.</span><span class="n">flush</span><span class="p">()</span>
+
+ <span class="k">try</span><span class="p">:</span>
+ <span class="c1"># Compile it.
+</span> <span class="n">result</span> <span class="o">=</span> <span class="n">subprocess</span><span class="p">.</span><span class="n">run</span><span class="p">(</span>
+ <span class="p">[</span>
+ <span class="s">"em++"</span><span class="p">,</span>
+ <span class="s">"-s"</span><span class="p">,</span>
+ <span class="s">"ENVIRONMENT=node"</span><span class="p">,</span>
+ <span class="s">"-s"</span><span class="p">,</span>
+ <span class="s">"WASM=1"</span><span class="p">,</span>
+ <span class="s">"-s"</span><span class="p">,</span>
+ <span class="s">"PURE_WASI=1"</span><span class="p">,</span>
+ <span class="s">"main.cpp"</span><span class="p">,</span>
+ <span class="p">],</span>
+ <span class="n">capture_output</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
+ <span class="n">text</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
+ <span class="n">check</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
+ <span class="p">)</span>
+
+ <span class="k">if</span> <span class="n">result</span><span class="p">.</span><span class="n">returncode</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">result</span><span class="p">.</span><span class="n">stderr</span>
+
+ <span class="c1"># Run it.
+</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"a.out.wasm"</span><span class="p">,</span> <span class="s">"rb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">binary</span><span class="p">:</span>
+ <span class="n">wasi</span> <span class="o">=</span> <span class="n">WasiConfig</span><span class="p">()</span>
+ <span class="c1"># Store the output in a file.
+</span> <span class="n">wasi</span><span class="p">.</span><span class="n">stdout_file</span> <span class="o">=</span> <span class="s">"a.out.stdout"</span>
+ <span class="c1"># Store the errors in a file.
+</span> <span class="n">wasi</span><span class="p">.</span><span class="n">stderr_file</span> <span class="o">=</span> <span class="s">"a.out.stderr"</span>
+
+ <span class="n">config</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
+ <span class="c1"># config.consume_fuel = True
+</span> <span class="n">engine</span> <span class="o">=</span> <span class="n">Engine</span><span class="p">(</span><span class="n">config</span><span class="p">)</span>
+ <span class="n">store</span> <span class="o">=</span> <span class="n">Store</span><span class="p">(</span><span class="n">engine</span><span class="p">)</span>
+ <span class="n">store</span><span class="p">.</span><span class="n">set_wasi</span><span class="p">(</span><span class="n">wasi</span><span class="p">)</span>
+ <span class="c1"># Limits the RAM.
+</span> <span class="c1"># store.set_limits(16 * 1024 * 1024)
+</span> <span class="c1"># Limits the CPU.
+</span> <span class="c1"># store.set_fuel(10_000_000_000)
+</span>
+ <span class="n">linker</span> <span class="o">=</span> <span class="n">Linker</span><span class="p">(</span><span class="n">engine</span><span class="p">)</span>
+ <span class="n">linker</span><span class="p">.</span><span class="n">define_wasi</span><span class="p">()</span>
+ <span class="n">module</span> <span class="o">=</span> <span class="n">Module</span><span class="p">(</span><span class="n">store</span><span class="p">.</span><span class="n">engine</span><span class="p">,</span> <span class="n">binary</span><span class="p">.</span><span class="n">read</span><span class="p">())</span>
+ <span class="n">instance</span> <span class="o">=</span> <span class="n">linker</span><span class="p">.</span><span class="n">instantiate</span><span class="p">(</span><span class="n">store</span><span class="p">,</span> <span class="n">module</span><span class="p">)</span>
+
+ <span class="c1"># `_start` is the binary entrypoint, also known as main.
+</span> <span class="n">start</span> <span class="o">=</span> <span class="n">instance</span><span class="p">.</span><span class="n">exports</span><span class="p">(</span><span class="n">store</span><span class="p">)[</span><span class="s">"_start"</span><span class="p">]</span>
+ <span class="k">assert</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">Func</span><span class="p">)</span>
+
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">start</span><span class="p">(</span><span class="n">store</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">ExitTrap</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
+ <span class="c1"># If exit code is not 0, we return the errors.
+</span> <span class="k">if</span> <span class="n">e</span><span class="p">.</span><span class="n">code</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
+ <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"a.out.stderr"</span><span class="p">,</span> <span class="s">"rt"</span><span class="p">)</span> <span class="k">as</span> <span class="n">stderr</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">stderr</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
+
+ <span class="c1"># If no errors, we return the output.
+</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"a.out.stdout"</span><span class="p">,</span> <span class="s">"rt"</span><span class="p">)</span> <span class="k">as</span> <span class="n">stdout</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">stdout</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
+ <span class="k">except</span> <span class="n">subprocess</span><span class="p">.</span><span class="n">CalledProcessError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">e</span><span class="p">.</span><span class="n">stderr</span>
+ <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span> <span class="c1"># noqa
+</span> <span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
+</code></pre></div></div>
+
+<h2 id="deploy">Deploy</h2>
+
+<p>In the past, I always used Google’s tools for deployment, but this time I tried building the Docker image in GitHub Action, which gave me two huge advantages.</p>
+
+<ol>
+ <li>Cache: I don’t know why, but I never got the cache to work in Cloud Build. With GitHub, it’s just a matter of using a flag.</li>
+ <li>Modern Docker syntax usage: In Cloud Build, it’s not possible to use <a href="https://www.docker.com/blog/introduction-to-heredocs-in-dockerfiles/">heredoc</a>, for example.</li>
+ <li>Speed: I know it’s possible to upgrade the Cloud Build machine, but that costs money, and on GitHub, I have a quite generous free quota.</li>
+</ol>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Deploy on Google Cloud Platform</span>
+
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">push</span><span class="pi">:</span>
+ <span class="na">branches</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">main</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">deploy</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Authenticate to Google Cloud</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">google-github-actions/auth@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">credentials_json</span><span class="pi">:</span> <span class="s">$</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Google Cloud SDK</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">google-github-actions/setup-gcloud@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">project_id</span><span class="pi">:</span> <span class="s">$</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Docker Buildx</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">docker/setup-buildx-action@v3</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Authenticate Docker</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">gcloud auth configure-docker --quiet $-docker.pkg.dev</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build And Push Telegram Service</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">docker/build-push-action@v5</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">context</span><span class="pi">:</span> <span class="s">.</span>
+ <span class="na">push</span><span class="pi">:</span> <span class="no">true</span>
+ <span class="na">tags</span><span class="pi">:</span> <span class="s">$/$:$</span>
+ <span class="na">cache-from</span><span class="pi">:</span> <span class="s">type=gha</span>
+ <span class="na">cache-to</span><span class="pi">:</span> <span class="s">type=gha,mode=max</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy Telegram Service to Cloud Run</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">TELEGRAM_SERVICE_NAME</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">REGION</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">REGISTRY</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">GITHUB_SHA</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">TELEGRAM_TOKEN</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">SECRET</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">BUCKET</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">cat <<EOF | envsubst > service.yaml</span>
+ <span class="s">apiVersion: serving.knative.dev/v1</span>
+ <span class="s">kind: Service</span>
+ <span class="s">metadata:</span>
+ <span class="s">name: "$TELEGRAM_SERVICE_NAME"</span>
+ <span class="s">labels:</span>
+ <span class="s">cloud.googleapis.com/location: "$REGION"</span>
+ <span class="s">spec:</span>
+ <span class="s">template:</span>
+ <span class="s">metadata:</span>
+ <span class="s">annotations:</span>
+ <span class="s">run.googleapis.com/execution-environment: "gen2"</span>
+ <span class="s">run.googleapis.com/startup-cpu-boost: "true"</span>
+ <span class="s">run.googleapis.com/cpu-throttling: "true"</span>
+ <span class="s">autoscaling.knative.dev/maxScale: "16"</span>
+ <span class="s">spec:</span>
+ <span class="s">containerConcurrency: "1"</span>
+ <span class="s">timeoutSeconds: "60"</span>
+ <span class="s">containers:</span>
+ <span class="s">- image: "$REGISTRY/$TELEGRAM_SERVICE_NAME:$GITHUB_SHA"</span>
+ <span class="s">name: "$TELEGRAM_SERVICE_NAME"</span>
+ <span class="s">resources:</span>
+ <span class="s">limits:</span>
+ <span class="s">cpu: "4000m"</span>
+ <span class="s">memory: "4Gi"</span>
+ <span class="s">env:</span>
+ <span class="s">- name: TELEGRAM_TOKEN</span>
+ <span class="s">value: "$TELEGRAM_TOKEN"</span>
+ <span class="s">- name: SECRET</span>
+ <span class="s">value: "$SECRET"</span>
+ <span class="s">- name: BUCKET</span>
+ <span class="s">value: "$BUCKET"</span>
+ <span class="s">EOF</span>
+
+ <span class="s">gcloud run services replace service.yaml</span>
+ <span class="s">rm -f service.yaml</span>
+</code></pre></div></div>
+
+<h2 id="conclusion">Conclusion</h2>
+
+<p>Try here: <a href="https://t.me/neo_compiler_bot">https://t.me/neo_compiler_bot</a> or <code class="language-plaintext highlighter-rouge">@neo_compiler_bot</code> on Telegram.</p>
+
+<p>Source code: <a href="https://github.com/skhaz/neo-compiler-and-runner">https://github.com/skhaz/neo-compiler-and-runner</a>.</p>
+
+<p><img src="https://nullonerror.org/public/2023-12-01-executing-untrusted-code-in-serverless-environments-a-telegram-bot-for-running-c-and-c++-code-on-cloud-run/telegram.png" alt="Telegram" class="center" /></p>
+ 2023-12-01T00:00:00+00:00
+ Rodrigo Delduca
+
+
+ Rodrigo Delduca: Flipping Bits Whilst Updating Pixels
+ https://nullonerror.org/2023/11/12/flipping-bits-whilst-updating-pixels/
+ <blockquote>
+ <p>I have less than 16 milliseconds to do more than thousands of operations.</p>
+</blockquote>
+
+<p>Roughly 15 years ago, during an extended summer holiday, in a shared room of a student residence, I found myself endeavoring to port <a href="https://github.com/skhaz/wintermoon">my 2D game</a> engine built on top of <a href="https://en.wikipedia.org/wiki/Simple_DirectMedia_Layer">SDL</a> to the <a href="https://en.wikipedia.org/wiki/Google_Native_Client">Google Native Client (NaCl)</a>. NaCl served as a sandboxing mechanism for Chrome, enabling the execution of native code within the browser, specifically within the Chrome browser. It’s safe to assert that NaCl can be considered the progenitor of <a href="https://webassembly.org">WebAssembly</a>.</p>
+
+<p>A considerable amount of time has elapsed, and many changes have transpired. I transitioned from game development to web development, yet low-level programming has always coursed through my veins. Consequently, I resolved to revive the dream of crafting my own game engine running on the web. Today, with the advent of WebAssembly, achieving this goal is significantly more feasible and portable.</p>
+
+<p>Therefore, I created Carimbo 🇧🇷, meaning “stamp” in English. The name encapsulates the notion that 2D engines are continually stamping sprites onto the screen.</p>
+
+
+ <img src="https://nullonerror.org/public/2023-11-12-flipping-bits-whilst-updating-pixels/carimbo.webp" alt="Carimbo" class="center" />
+ Artwork by <a href="https://www.fiverr.com/yuugenpixie">@yuugenpixie</a>
+
+
+<p>This engine shares the foundational principles of <em>Wintermoon</em>; it is built upon the SDL library, employs Lua for scripting, and consolidates all assets within a compressed LZMA file, which is <em>mounted</em> when the engine initializes.</p>
+
+<p>In essence, it operates as follows: video, audio, joystick, network, and file system components are initialized. Then, the <code class="language-plaintext highlighter-rouge">bundle.zip</code> file is mounted. When I say ‘mounted,’ I employ <a href="https://icculus.org/physfs/">a library</a> that makes read and write operations within the compressed file entirely transparent, eliminating the need for decompression, which is excellent. Subsequently, the ‘main.lua’ file is executed. This file should utilize the factory to construct the engine, which is the cornerstone. Following this, the script must spawn entities and other objects to be used within the game. Finally, with the game defined, the script should invoke the ‘run’ method of the engine, which will block and initiate the event loop.</p>
+
+<p>However, this time around, the tooling for C++ has significantly improved compared to that era. Numerous package managers now exist, and compilers, along with the standard library, have matured considerably. Notably, there’s no longer a necessity to employ Boost for advanced features; many functionalities, including smart pointers and others formerly associated with Boost, are now integral parts of the standard library.</p>
+
+<p>Speaking of package managers, in Carimbo, I opted for <a href="https://conan.io/">Conan</a>, which, in my opinion, is an excellent package manager.</p>
+
+<p>It was during this exploration that I discovered Conan’s support for various toolings, including <a href="https://github.com/emscripten-core/emsdk">emsdk</a>—the Software Development Kit (SDK) and compiler for the Emscripten project. Emscripten is an LLVM/Clang-based compiler designed to translate C and C++ source code into WebAssembly.</p>
+
+<p>With the <code class="language-plaintext highlighter-rouge">emsdk</code>, I could finally fulfill my long-held aspiration.</p>
+
+<p><img src="https://nullonerror.org/public/2023-11-12-flipping-bits-whilst-updating-pixels/blank.jpeg" alt="" class="center" /></p>
+
+<p>Yes, a black screen and an overwhelming sense of victory. I had successfully ported my code to run in the browser. Now, all that remained was to figure out how to load the assets (at that time). However, the event loop was running just a tad below 60 frames per second due to a bug in the counter.</p>
+
+<p>And you might be wondering, ‘How do you debug this?’ Firstly, for various reasons that can burden the final binary, a lot is stripped away. Therefore, we need to recompile everything and all dependencies with sanitizers and debugging symbols. Secondly, WebAssembly should be linked with <strong>-sASSERTIONS=2</strong>, <strong>-sRUNTIME_DEBUG</strong>, and <strong>–profiling</strong>. This way, it’s possible to see the stack trace in the browser console as if by magic. Additionally, <a href="https://developer.chrome.com/blog/wasm-debugging-2020/">Chrome has a debugger</a> that allows you to insert breakpoints within your source code and inspect step by step.</p>
+
+<p>By the way, a binary and all its dependencies compiled with all sanitizers and debugging symbols can easily surpass 300 megabytes! So, I recommend compiling with -Os or -O1.</p>
+
+<p><a href="https://github.com/carimbolabs/carimbo">The source code for the Carimbo</a></p>
+
+<p>Check out the engine running below; this is just a concept without audio and joystick support.</p>
+ 2023-11-12T00:00:00+00:00
+ Rodrigo Delduca
+
+
+ Rodrigo Delduca: What I’ve been automating with GitHub Actions, an automated life
+ https://nullonerror.org/2023/11/01/what-i-ve-been-automating-with-github-actions-an-automated-life/
+ <h1 id="automate-all-the-things">Automate all the things!</h1>
+
+<p>Programmers, like no other beings on the planet, are completely obsessed with automating things, from the simplest to the most complex, and I’m no different.</p>
+
+<p>I have automated several things using GitHub Actions, and today I will show some of the things I’ve done.</p>
+
+<h2 id="readmemd">README.md</h2>
+
+<p>In my GitHub README, I periodically fetch the RSS feed from my blog (the one you are currently reading) and populate it with the latest articles, like this:</p>
+
+<p>Ah, you can use any source of RSS, like your YouTube channel!</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Latest blog post workflow</span>
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">schedule</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0</span><span class="nv"> </span><span class="s">*/6</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*"</span>
+ <span class="na">workflow_dispatch</span><span class="pi">:</span> <span class="c1"># Run workflow manually</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">update-readme-with-blog</span><span class="pi">:</span>
+ <span class="na">name</span><span class="pi">:</span> <span class="s">Update this repo's README with latest blog posts</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v2</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Pull NULL on Error posts</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">gautamkrishnar/blog-post-workflow@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">comment_tag_name</span><span class="pi">:</span> <span class="s">BLOG</span>
+ <span class="na">commit_message</span><span class="pi">:</span> <span class="s">Update with the latest blog posts</span>
+ <span class="na">committer_username</span><span class="pi">:</span> <span class="s">Rodrigo Delduca</span>
+ <span class="na">committer_email</span><span class="pi">:</span> <span class="s">46259+skhaz@users.noreply.github.com</span>
+ <span class="na">max_post_count</span><span class="pi">:</span> <span class="m">6</span>
+ <span class="na">feed_list</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://nullonerror.org/feed"</span>
+</code></pre></div></div>
+
+<h2 id="resume">Resume</h2>
+
+<p>My resume is public. I have an action in the repository that compiles and uploads it to a Google Cloud bucket and sets the object as public, like this:</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">on</span><span class="pi">:</span> <span class="s">push</span>
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">build</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">container</span><span class="pi">:</span> <span class="s">texlive/texlive</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">xelatex resume.tex</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Authenticate to Google Cloud</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">google-github-actions/auth@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">credentials_json</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Upload to Google Cloud Storage</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">google-github-actions/upload-cloud-storage@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">path</span><span class="pi">:</span> <span class="s">resume.pdf</span>
+ <span class="na">destination</span><span class="pi">:</span> <span class="s">gcs.skhaz.dev</span>
+ <span class="na">predefinedAcl</span><span class="pi">:</span> <span class="s">publicRead</span>
+</code></pre></div></div>
+
+<p>You can check it out at <a href="https://gcs.skhaz.dev/resume.pdf">https://gcs.skhaz.dev/resume.pdf</a>.</p>
+
+<h2 id="github-stars">GitHub Stars</h2>
+
+<p>I believe everyone enjoys starring repositories, but GitHub’s interface doesn’t help much when it comes to finding or organizing them.</p>
+
+<p>To do this, I use <a href="https://github.com/maguowei/starred">starred</a>, which generates a README file categorizing by language. You can check it out at the following address: <a href="https://github.com/skhaz/stars">https://github.com/skhaz/stars</a></p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Update Stars</span>
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">workflow_dispatch</span><span class="pi">:</span>
+ <span class="na">schedule</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s">0 0 * * *</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">stars</span><span class="pi">:</span>
+ <span class="na">name</span><span class="pi">:</span> <span class="s">Update stars</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v3</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Python</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-python@v3</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">python-version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3.10"</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install dependencies</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">python -m pip install --upgrade pip</span>
+ <span class="s">pip install starred</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Get repository name</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">echo "REPOSITORY_NAME=${GITHUB_REPOSITORY#*/}" >> $GITHUB_ENV</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Update repository category by language</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">GITHUB_TOKEN</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">REPOSITORY</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">USERNAME</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">starred --username ${USERNAME} --repository ${REPOSITORY} --sort --token ${GITHUB_TOKEN} --message 'awesome-stars category by language update by github actions cron, created by starred'</span>
+</code></pre></div></div>
+
+<h2 id="healthchecks">Healthchecks</h2>
+
+<p>I imagine that, just like me, you also have several websites. In my case, all of them are simple and do not generate any income. However, I want to make sure that everything is in order, so I use https://healthchecks.io in conjunction with GitHub Actions. Healthchecks.io operates passively; it does not make requests to your site. On the contrary, you must make a request to it, and then it marks it as healthy. If there are no pings for a certain amount of time (configurable), it will notify you through various means that the site or application is not functioning as it should. Think of it as a Kubernetes probe.</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Health Check</span>
+
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">workflow_dispatch</span><span class="pi">:</span>
+ <span class="na">schedule</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*"</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">health</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Check health</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">STATUSCODE=$(curl -s -o /dev/null --write-out "%{http_code}" "${SITE_URL}")</span>
+
+ <span class="s">if test $STATUSCODE -ne 200; then</span>
+ <span class="s">exit 1</span>
+ <span class="s">fi</span>
+
+ <span class="s">curl -fsS -m 10 --retry 5 -o /dev/null "https://hc-ping.com/${HEALTH_UUID}"</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">HEALTH_UUID</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">SITE_URL</span><span class="pi">:</span> <span class="s">$</span>
+</code></pre></div></div>
+
+<h2 id="salary">Salary</h2>
+
+<p>This one is a bit more complex, as it goes beyond just an action. I have a repository called ‘salary’, where there’s an action that runs every hour. What this action does is essentially run a Go code, and the result updates the README file. This way, I can simply access the URL and get an estimate of how much I’ll receive.</p>
+
+<p>In the program, I have two goroutines running in parallel. In one of them, I query the number of hours on Toggl, multiply it by my rate, and return it through a channel. The other does the same, but it uses an API to convert dollars to Brazilian reais, and in the end, the values are summed up.</p>
+
+<p><code class="language-plaintext highlighter-rouge">main.go</code></p>
+
+<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>
+
+<span class="k">import</span> <span class="p">(</span>
+ <span class="s">"bytes"</span>
+ <span class="s">"encoding/json"</span>
+ <span class="s">"fmt"</span>
+ <span class="s">"io"</span>
+ <span class="s">"log"</span>
+ <span class="s">"math"</span>
+ <span class="s">"net/http"</span>
+ <span class="s">"os"</span>
+ <span class="s">"strconv"</span>
+ <span class="s">"sync"</span>
+ <span class="s">"time"</span>
+<span class="p">)</span>
+
+<span class="k">type</span> <span class="n">DateRange</span> <span class="k">struct</span> <span class="p">{</span>
+ <span class="n">StartDate</span> <span class="kt">string</span> <span class="s">`json:"start_date"`</span>
+ <span class="n">EndDate</span> <span class="kt">string</span> <span class="s">`json:"end_date"`</span>
+<span class="p">}</span>
+
+<span class="k">type</span> <span class="n">Summary</span> <span class="k">struct</span> <span class="p">{</span>
+ <span class="n">TrackedSecond</span> <span class="kt">int</span> <span class="s">`json:"tracked_seconds"`</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="n">toggl</span><span class="p">(</span><span class="n">result</span> <span class="k">chan</span><span class="o"><-</span> <span class="kt">int</span><span class="p">,</span> <span class="n">wg</span> <span class="o">*</span><span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">defer</span> <span class="n">wg</span><span class="o">.</span><span class="n">Done</span><span class="p">()</span>
+
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">now</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">Now</span><span class="p">()</span>
+ <span class="n">firstDay</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">Date</span><span class="p">(</span><span class="n">now</span><span class="o">.</span><span class="n">Year</span><span class="p">(),</span> <span class="n">now</span><span class="o">.</span><span class="n">Month</span><span class="p">(),</span> <span class="m">1</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">time</span><span class="o">.</span><span class="n">UTC</span><span class="p">)</span>
+ <span class="n">lastDay</span> <span class="o">=</span> <span class="n">firstDay</span><span class="o">.</span><span class="n">AddDate</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="o">-</span><span class="m">1</span><span class="p">)</span>
+
+ <span class="n">url</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"https://api.track.toggl.com/reports/api/v3/workspace/%s/projects/summary"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"TOGGL_WORKSPACE_ID"</span><span class="p">))</span>
+ <span class="n">dataRange</span> <span class="o">=</span> <span class="n">DateRange</span><span class="p">{</span>
+ <span class="n">StartDate</span><span class="o">:</span> <span class="n">firstDay</span><span class="o">.</span><span class="n">Format</span><span class="p">(</span><span class="s">"2006-01-02"</span><span class="p">),</span>
+ <span class="n">EndDate</span><span class="o">:</span> <span class="n">lastDay</span><span class="o">.</span><span class="n">Format</span><span class="p">(</span><span class="s">"2006-01-02"</span><span class="p">),</span>
+ <span class="p">}</span>
+ <span class="p">)</span>
+
+ <span class="n">payload</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">json</span><span class="o">.</span><span class="n">Marshal</span><span class="p">(</span><span class="n">dataRange</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">req</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">http</span><span class="o">.</span><span class="n">NewRequest</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">MethodPost</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">bytes</span><span class="o">.</span><span class="n">NewBuffer</span><span class="p">(</span><span class="n">payload</span><span class="p">))</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">req</span><span class="o">.</span><span class="n">Header</span><span class="o">.</span><span class="n">Set</span><span class="p">(</span><span class="s">"Content-Type"</span><span class="p">,</span> <span class="s">"application/json"</span><span class="p">)</span>
+ <span class="n">req</span><span class="o">.</span><span class="n">SetBasicAuth</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"TOGGL_EMAIL"</span><span class="p">),</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"TOGGL_PASSWORD"</span><span class="p">))</span>
+
+ <span class="n">client</span> <span class="o">:=</span> <span class="o">&</span><span class="n">http</span><span class="o">.</span><span class="n">Client</span><span class="p">{}</span>
+ <span class="n">resp</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">client</span><span class="o">.</span><span class="n">Do</span><span class="p">(</span><span class="n">req</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">resp</span><span class="o">.</span><span class="n">Body</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="n">body</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">io</span><span class="o">.</span><span class="n">ReadAll</span><span class="p">(</span><span class="n">resp</span><span class="o">.</span><span class="n">Body</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="k">var</span> <span class="n">summaries</span> <span class="p">[]</span><span class="n">Summary</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">Unmarshal</span><span class="p">(</span><span class="n">body</span><span class="p">,</span> <span class="o">&</span><span class="n">summaries</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">total</span> <span class="o">:=</span> <span class="m">0</span>
+ <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">summary</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">summaries</span> <span class="p">{</span>
+ <span class="n">total</span> <span class="o">+=</span> <span class="n">summary</span><span class="o">.</span><span class="n">TrackedSecond</span>
+ <span class="p">}</span>
+
+ <span class="n">hourlyRate</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">strconv</span><span class="o">.</span><span class="n">Atoi</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"TOGGL_HOURLY_RATE"</span><span class="p">))</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">result</span> <span class="o"><-</span> <span class="p">(</span><span class="n">total</span> <span class="o">/</span> <span class="m">3600</span><span class="p">)</span> <span class="o">*</span> <span class="n">hourlyRate</span>
+<span class="p">}</span>
+
+<span class="k">type</span> <span class="n">CurrencyData</span> <span class="k">struct</span> <span class="p">{</span>
+ <span class="n">Quotes</span> <span class="k">struct</span> <span class="p">{</span>
+ <span class="n">USDBRL</span> <span class="kt">float64</span> <span class="s">`json:"USDBRL"`</span>
+ <span class="p">}</span> <span class="s">`json:"quotes"`</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="n">husky</span><span class="p">(</span><span class="n">result</span> <span class="k">chan</span><span class="o"><-</span> <span class="kt">int</span><span class="p">,</span> <span class="n">wg</span> <span class="o">*</span><span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">defer</span> <span class="n">wg</span><span class="o">.</span><span class="n">Done</span><span class="p">()</span>
+
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">currency</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"HUSKY_CURRENCY"</span><span class="p">)</span>
+ <span class="n">url</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"https://api.apilayer.com/currency_data/live?base=USD&symbols=%s&currencies=%s"</span><span class="p">,</span> <span class="n">currency</span><span class="p">,</span> <span class="n">currency</span><span class="p">)</span>
+ <span class="p">)</span>
+
+ <span class="n">req</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">http</span><span class="o">.</span><span class="n">NewRequest</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">MethodGet</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="no">nil</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">req</span><span class="o">.</span><span class="n">Header</span><span class="o">.</span><span class="n">Set</span><span class="p">(</span><span class="s">"apikey"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"APILAYER_APIKEY"</span><span class="p">))</span>
+
+ <span class="n">client</span> <span class="o">:=</span> <span class="o">&</span><span class="n">http</span><span class="o">.</span><span class="n">Client</span><span class="p">{}</span>
+ <span class="n">resp</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">client</span><span class="o">.</span><span class="n">Do</span><span class="p">(</span><span class="n">req</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">resp</span><span class="o">.</span><span class="n">Body</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="n">body</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">io</span><span class="o">.</span><span class="n">ReadAll</span><span class="p">(</span><span class="n">resp</span><span class="o">.</span><span class="n">Body</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="k">var</span> <span class="n">data</span> <span class="n">CurrencyData</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">Unmarshal</span><span class="p">(</span><span class="n">body</span><span class="p">,</span> <span class="o">&</span><span class="n">data</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">monthlySalary</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">strconv</span><span class="o">.</span><span class="n">ParseFloat</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"HUSKY_MONTHLY_SALARY"</span><span class="p">),</span> <span class="m">64</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">gross</span> <span class="o">:=</span> <span class="kt">int</span><span class="p">(</span><span class="n">math</span><span class="o">.</span><span class="n">Floor</span><span class="p">(</span><span class="n">monthlySalary</span> <span class="o">*</span> <span class="n">data</span><span class="o">.</span><span class="n">Quotes</span><span class="o">.</span><span class="n">USDBRL</span><span class="p">))</span>
+ <span class="n">deduction</span> <span class="o">:=</span> <span class="n">gross</span> <span class="o">*</span> <span class="m">1</span> <span class="o">/</span> <span class="m">100</span>
+ <span class="n">result</span> <span class="o"><-</span> <span class="n">gross</span> <span class="o">-</span> <span class="n">deduction</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">ch</span> <span class="o">=</span> <span class="nb">make</span><span class="p">(</span><span class="k">chan</span> <span class="kt">int</span><span class="p">)</span>
+ <span class="n">wg</span> <span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span>
+ <span class="n">funcs</span> <span class="o">=</span> <span class="p">[]</span><span class="k">func</span><span class="p">(</span><span class="k">chan</span><span class="o"><-</span> <span class="kt">int</span><span class="p">,</span> <span class="o">*</span><span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span><span class="p">){</span><span class="n">toggl</span><span class="p">,</span> <span class="n">husky</span><span class="p">}</span>
+ <span class="p">)</span>
+
+ <span class="n">wg</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">funcs</span><span class="p">))</span>
+
+ <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">fun</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">funcs</span> <span class="p">{</span>
+ <span class="k">go</span> <span class="n">fun</span><span class="p">(</span><span class="n">ch</span><span class="p">,</span> <span class="o">&</span><span class="n">wg</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="k">go</span> <span class="k">func</span><span class="p">()</span> <span class="p">{</span>
+ <span class="n">wg</span><span class="o">.</span><span class="n">Wait</span><span class="p">()</span>
+ <span class="nb">close</span><span class="p">(</span><span class="n">ch</span><span class="p">)</span>
+ <span class="p">}()</span>
+
+ <span class="k">var</span> <span class="n">sum</span> <span class="kt">int</span>
+ <span class="k">for</span> <span class="n">result</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">ch</span> <span class="p">{</span>
+ <span class="n">sum</span> <span class="o">+=</span> <span class="n">result</span>
+ <span class="p">}</span>
+
+ <span class="n">fmt</span><span class="o">.</span><span class="n">Print</span><span class="p">(</span><span class="n">sum</span><span class="p">)</span>
+<span class="p">}</span>
+</code></pre></div></div>
+
+<p>So in the action, all you have to do is run and update the README periodically.</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Run</span>
+
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">workflow_dispatch</span><span class="pi">:</span>
+ <span class="na">schedule</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*"</span>
+
+<span class="na">permissions</span><span class="pi">:</span>
+ <span class="na">contents</span><span class="pi">:</span> <span class="s">write</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">run</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup Go</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-go@v4</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Update Markdown</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">salary=$(make run)</span>
+
+ <span class="s">cat > README.md <<EOF</span>
+ <span class="s">### Salary</span>
+
+ <span class="s">EOF</span>
+
+ <span class="s">printf "%s\n" "$salary" >> README.md</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">APILAYER_APIKEY</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">HUSKY_MONTHLY_SALARY</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">HUSKY_CURRENCY</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">TOGGL_WORKSPACE_ID</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">TOGGL_EMAIL</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">TOGGL_PASSWORD</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">TOGGL_HOURLY_RATE</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Commit report</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">git config --global user.name 'github-actions[bot]'</span>
+ <span class="s">git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com'</span>
+ <span class="s">git add README.md</span>
+ <span class="s">git commit -am "Automated Salary Update" || true</span>
+ <span class="s">git push</span>
+</code></pre></div></div>
+
+<h2 id="blog">Blog</h2>
+
+<p>The next step is to automate the publishing of new blog posts using ChatGPT ;-).</p>
+ 2023-11-01T00:00:00+00:00
+ Rodrigo Delduca
+
+
+ Rodrigo Delduca: Working with compressed binary data on AWS IoT Core
+ https://nullonerror.org/2023/10/17/working-with-compressed-binary-data-on-aws-iot-core/
+ <h3 id="objective">Objective</h3>
+
+<p>Today we will see how to send compressed CBOR with ZSTD from an ESP32 microcontroller through an MQTT topic, passing through AWS IoT Core, and finally being decoded in a TypeScript-written Lambda function.</p>
+
+<h3 id="intro">Intro</h3>
+
+<p>Firstly, what is a microcontroller (MCU)? In this article, we will be using the ESP32 microcontroller from Espressif. It is a highly affordable and inexpensive microcontroller that comes with built-in WiFi and Bluetooth, making it ideal for IoT projects as we will see today. It also boasts a generous amount of flash memory and RAM, as well as a powerful dual-core 32-bit CPU.</p>
+
+<p>Another tool we will be using is PlatformIO, a development framework that functions as a series of plugins and command-line tools for VSCode. With PlatformIO, we have everything we need, from project setup, board selection, serial port speed, compilation flags (yes, we’ll be compiling code in C and C++), and more. Installing it is quite simple; just visit https://platformio.org/ and follow the instructions.</p>
+
+<p>Lastly, to streamline our project and infrastructure, we will be using the Serverless Framework, a framework designed for developing serverless solutions. In our case, we will be using a Lambda function to receive messages sent to the topic. The Serverless Framework is a perfect fit for this scenario and works seamlessly with AWS Amazon.</p>
+
+<h3 id="compressed-binary-data">Compressed Binary Data</h3>
+
+<p>We could certainly use JSON. In fact, with JSON, it is possible to perform queries using the WHERE clause (Yes, IoT Core supports SQL, and we will delve into that later). However, our objective here is to save as many bytes as possible. Imagine that our application will send and receive data via a costly and unreliable telephone connection. Therefore, we need to compress the data as much as possible.</p>
+
+<p>Firstly, let’s construct the raw payload in CBOR. CBOR is an interchangeable binary format specified in an RFC and supported by various programming languages (it is derived from MsgPack, in case you’ve heard of it before).</p>
+
+<p>Since this part will be done on the microcontroller side, we will be using the C++ language with the <code class="language-plaintext highlighter-rouge">ssilverman/libCBOR</code> library. To do this, we open the <code class="language-plaintext highlighter-rouge">platformio.ini</code> file and add the dependency under <code class="language-plaintext highlighter-rouge">lib_deps</code>.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf"><CBOR.h></span><span class="cp">
+#include</span> <span class="cpf"><CBOR_parsing.h></span><span class="cp">
+#include</span> <span class="cpf"><CBOR_streams.h></span><span class="cp">
+</span>
+<span class="k">namespace</span> <span class="n">cbor</span> <span class="o">=</span> <span class="o">::</span><span class="n">qindesign</span><span class="o">::</span><span class="n">cbor</span><span class="p">;</span>
+</code></pre></div></div>
+
+<p>Since we are developing for a microcontroller, memory allocation is a critical concern as it can lead to fragmentation and other issues. Therefore, we will define a buffer of 256 bytes for reading and writing CBOR messages, which is more than sufficient for our current application.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">constexpr</span> <span class="kt">size_t</span> <span class="n">kBytesSize</span> <span class="o">=</span> <span class="mi">256</span><span class="p">;</span>
+<span class="kt">uint8_t</span> <span class="n">bytes</span><span class="p">[</span><span class="n">kBytesSize</span><span class="p">]{</span><span class="mi">0</span><span class="p">};</span>
+<span class="n">cbor</span><span class="o">::</span><span class="n">BytesStream</span> <span class="n">bs</span><span class="p">{</span><span class="n">bytes</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">bytes</span><span class="p">)};</span>
+<span class="n">cbor</span><span class="o">::</span><span class="n">BytesPrint</span> <span class="n">bp</span><span class="p">{</span><span class="n">bytes</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">bytes</span><span class="p">)};</span>
+</code></pre></div></div>
+
+<p>Next, let’s prepare to use ZSTD. The steps are the same as with CBOR.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf"><zstd.h></span><span class="cp">
+</span>
+<span class="k">constexpr</span> <span class="kt">size_t</span> <span class="n">kZBytesSize</span> <span class="o">=</span> <span class="mi">256</span><span class="p">;</span>
+<span class="kt">uint8_t</span> <span class="n">zbytes</span><span class="p">[</span><span class="n">kZBytesSize</span><span class="p">]{</span><span class="mi">0</span><span class="p">};</span>
+</code></pre></div></div>
+
+<p>Finally, and not least importantly, let’s prepare to send messages to a topic on IoT Core. The setup is a bit complex and requires attention. Firstly, we need to add the <code class="language-plaintext highlighter-rouge">knolleary/PubSubClient</code> library.</p>
+
+<p>We will need three things from AWS IoT Core: the Certificate Authority (CA) certificate, the Thing certificate, and the private key certificate of the Thing. We will see how to create rules in IoT Core to allow things to subscribe and publish only to specific topics for security reasons. One more thing, in our project, we will hardcode these values in the flash memory of the ESP32 using the <em>PROGMEM</em> directive.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf"><WiFiClientSecure.h></span><span class="cp">
+#include</span> <span class="cpf"><PubSubClient.h></span><span class="cp">
+</span>
+<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">AWS_IOT_ENDPOINT</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"....amazonaws.com"</span><span class="p">;</span>
+<span class="k">static</span> <span class="k">const</span> <span class="kt">uint16_t</span> <span class="n">AWS_IOT_PORT</span> <span class="o">=</span> <span class="mi">8883</span><span class="p">;</span>
+<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">THINGNAME</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"My ESP32"</span><span class="p">;</span>
+
+<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">AWS_CERT_CA</span><span class="p">[]</span> <span class="n">PROGMEM</span> <span class="o">=</span> <span class="s">R"EOF(
+-----BEGIN CERTIFICATE-----
+... AWS Amazon Certificate Authority (CA)
+-----END CERTIFICATE-----
+)EOF"</span><span class="p">;</span>
+
+<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">AWS_CERT_CRT</span><span class="p">[]</span> <span class="n">PROGMEM</span> <span class="o">=</span> <span class="s">R"KEY(
+-----BEGIN CERTIFICATE-----
+... Thing's certificate
+-----END CERTIFICATE-----
+)KEY"</span><span class="p">;</span>
+
+<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">AWS_CERT_PRIVATE</span><span class="p">[]</span> <span class="n">PROGMEM</span> <span class="o">=</span> <span class="s">R"KEY(
+-----BEGIN RSA PRIVATE KEY-----
+... Thing's private certificate
+-----END RSA PRIVATE KEY-----
+)KEY"</span><span class="p">;</span>
+</code></pre></div></div>
+
+<p>We need an instance of PubSub to publish and subscribe to topics, as well as a WiFi client to configure the aforementioned keys.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">WiFiClientSecure</span> <span class="n">net</span><span class="p">;</span>
+<span class="n">PubSubClient</span> <span class="nf">pubsub</span><span class="p">(</span><span class="n">AWS_IOT_ENDPOINT</span><span class="p">,</span> <span class="n">AWS_IOT_PORT</span><span class="p">,</span> <span class="n">net</span><span class="p">);</span>
+</code></pre></div></div>
+
+<p>Now let’s proceed with a typical Arduino program structure, with the setup and loop functions. In the setup function, we will connect to the WiFi network, and then we will configure the keys in the WiFiClientSecure object so that the PubSubClient can use them to connect to IoT Core. Without these keys, the connection will be rejected by the AWS Amazon servers.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span>
+<span class="p">{</span>
+ <span class="c1">// For debugging.</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span>
+
+ <span class="c1">// Connect to WiFi.</span>
+ <span class="n">WiFi</span><span class="p">.</span><span class="n">mode</span><span class="p">(</span><span class="n">WIFI_STA</span><span class="p">);</span>
+ <span class="n">WiFi</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="n">WIFI_SSID</span><span class="p">,</span> <span class="n">WIFI_PASSWORD</span><span class="p">);</span>
+
+ <span class="c1">// Wait for the connection.</span>
+ <span class="k">while</span> <span class="p">(</span><span class="n">WiFi</span><span class="p">.</span><span class="n">status</span><span class="p">()</span> <span class="o">!=</span> <span class="n">WL_CONNECTED</span><span class="p">)</span>
+ <span class="p">{</span>
+ <span class="n">delay</span><span class="p">(</span><span class="mi">500</span><span class="p">);</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">"."</span><span class="p">);</span>
+ <span class="p">}</span>
+
+ <span class="c1">// Show WiFi information.</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">();</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">"Connected to "</span><span class="p">);</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="n">WIFI_SSID</span><span class="p">);</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">"IP address: "</span><span class="p">);</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="n">WiFi</span><span class="p">.</span><span class="n">localIP</span><span class="p">());</span>
+
+ <span class="c1">// Setup the certificates.</span>
+ <span class="n">net</span><span class="p">.</span><span class="n">setCACert</span><span class="p">(</span><span class="n">AWS_CERT_CA</span><span class="p">);</span>
+ <span class="n">net</span><span class="p">.</span><span class="n">setCertificate</span><span class="p">(</span><span class="n">AWS_CERT_CRT</span><span class="p">);</span>
+ <span class="n">net</span><span class="p">.</span><span class="n">setPrivateKey</span><span class="p">(</span><span class="n">AWS_CERT_PRIVATE</span><span class="p">);</span>
+
+ <span class="c1">// Connect to IoT Core.</span>
+ <span class="n">pubsub</span><span class="p">.</span><span class="n">connect</span><span class="p">(</span><span class="n">THINGNAME</span><span class="p">);</span>
+<span class="p">}</span>
+</code></pre></div></div>
+
+<p>If everything goes well, the <code class="language-plaintext highlighter-rouge">connect</code> method of PubSubClient will return <em>true</em>. We won’t perform that check; instead, we’ll attempt to publish directly and monitor the results on the IoT Core dashboard.</p>
+
+<p>Now comes the interesting part. We will assemble our payload in CBOR format, compress it using ZSTD, and publish it to a topic.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">on_sensor</span><span class="p">(</span><span class="k">const</span> <span class="kt">uint64_t</span> <span class="o">*</span><span class="n">sensor_data</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">sensor_data_size</span><span class="p">)</span> <span class="p">{</span>
+ <span class="c1">// Create a new instance of the CBOR writer.</span>
+ <span class="n">cbor</span><span class="o">::</span><span class="n">Writer</span> <span class="n">cbor</span><span class="p">{</span><span class="n">bp</span><span class="p">};</span>
+
+ <span class="c1">// Reset BytesPrint instance.</span>
+ <span class="n">bp</span><span class="p">.</span><span class="n">reset</span><span class="p">();</span>
+
+ <span class="c1">// Indicates that what follows is an array of a certain size.</span>
+ <span class="n">cbor</span><span class="p">.</span><span class="n">beginArray</span><span class="p">(</span><span class="n">sensor_data_size</span><span class="p">);</span>
+ <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">sensor_data_size</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
+ <span class="p">{</span>
+ <span class="c1">// For each item in the array, write it to CBOR.</span>
+ <span class="n">cbor</span><span class="p">.</span><span class="n">writeUnsignedInt</span><span class="p">(</span><span class="n">sensor_data</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
+ <span class="p">}</span>
+
+ <span class="c1">// Get the final CBOR size.</span>
+ <span class="k">const</span> <span class="kt">size_t</span> <span class="n">lenght</span> <span class="o">=</span> <span class="n">cbor</span><span class="p">.</span><span class="n">getWriteSize</span><span class="p">();</span>
+
+ <span class="c1">// Compress the CBOR buffer using ZSTD.</span>
+ <span class="kt">size_t</span> <span class="n">compressedSize</span> <span class="o">=</span> <span class="n">ZSTD_compress</span><span class="p">(</span><span class="n">zbytes</span><span class="p">,</span> <span class="n">kZBytesSize</span><span class="p">,</span> <span class="n">bytes</span><span class="p">,</span> <span class="n">lenght</span><span class="p">,</span> <span class="n">ZSTD_CLEVEL_DEFAULT</span><span class="p">);</span>
+
+ <span class="c1">// Publish the binary compressed data onto the topic.</span>
+ <span class="kt">char</span> <span class="n">topic</span><span class="p">[</span><span class="mi">128</span><span class="p">];</span>
+ <span class="n">sprintf</span><span class="p">(</span><span class="n">topic</span><span class="p">,</span> <span class="s">"sensors/%s/v1"</span><span class="p">,</span> <span class="n">THINGNAME</span><span class="p">);</span>
+ <span class="n">pubsub</span><span class="p">.</span><span class="n">publish</span><span class="p">(</span><span class="n">topic</span><span class="p">,</span> <span class="n">zbytes</span><span class="p">,</span> <span class="n">compressedSize</span><span class="p">,</span> <span class="nb">false</span><span class="p">);</span>
+<span class="p">}</span>
+</code></pre></div></div>
+
+<h3 id="on-the-cloud-side">On the cloud side</h3>
+
+<p>As I mentioned before, we will be using the Serverless Framework, which follows the Infrastructure as Code (IaC) approach. So what we’ll do is create the rules for the Things, create a lambda function, and define the trigger for that lambda as IoT Core. Since the data is in binary format, we will encode it in Base64 in the IoT Core SQL.</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">service</span><span class="pi">:</span> <span class="s">myiot</span>
+
+<span class="na">configValidationMode</span><span class="pi">:</span> <span class="s">error</span>
+
+<span class="na">frameworkVersion</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3"</span>
+
+<span class="na">provider</span><span class="pi">:</span>
+ <span class="na">name</span><span class="pi">:</span> <span class="s">aws</span>
+ <span class="na">runtime</span><span class="pi">:</span> <span class="s">nodejs18.x</span>
+ <span class="na">architecture</span><span class="pi">:</span> <span class="s">arm64</span>
+ <span class="na">stage</span><span class="pi">:</span> <span class="s">development</span>
+
+<span class="na">resources</span><span class="pi">:</span>
+ <span class="na">Resources</span><span class="pi">:</span>
+ <span class="na">IoTPolicy</span><span class="pi">:</span>
+ <span class="na">Type</span><span class="pi">:</span> <span class="s">AWS::IoT::Policy</span>
+ <span class="na">Properties</span><span class="pi">:</span>
+ <span class="na">PolicyName</span><span class="pi">:</span> <span class="s">IoTPolicy</span>
+ <span class="na">PolicyDocument</span><span class="pi">:</span>
+ <span class="na">Version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2012-10-17"</span>
+ <span class="na">Statement</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">Effect</span><span class="pi">:</span> <span class="s">Allow</span>
+ <span class="na">Action</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">iot:Connect</span>
+ <span class="na">Resource</span><span class="pi">:</span> <span class="s">arn:aws:iot:*:*:client/\${iot:Connection.Thing.ThingName}</span>
+ <span class="pi">-</span> <span class="na">Effect</span><span class="pi">:</span> <span class="s">Allow</span>
+ <span class="na">Action</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">iot:Publish</span>
+ <span class="pi">-</span> <span class="s">iot:Receive</span>
+ <span class="na">Resource</span><span class="pi">:</span> <span class="s">arn:aws:iot:*:*:topic/*/\${iot:Connection.Thing.ThingName}/*</span>
+ <span class="pi">-</span> <span class="na">Effect</span><span class="pi">:</span> <span class="s">Allow</span>
+ <span class="na">Action</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">iot:Subscribe</span>
+ <span class="na">Resource</span><span class="pi">:</span> <span class="s">arn:aws:iot:*:*:topicfilter/*/\${iot:Connection.Thing.ThingName}/*</span>
+
+<span class="na">functions</span><span class="pi">:</span>
+ <span class="na">tracker</span><span class="pi">:</span>
+ <span class="na">handler</span><span class="pi">:</span> <span class="s">app/mylambda.handler</span>
+ <span class="na">events</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">iot</span><span class="pi">:</span>
+ <span class="na">sql</span><span class="pi">:</span> <span class="s2">"</span><span class="s">SELECT</span><span class="nv"> </span><span class="s">timestamp()</span><span class="nv"> </span><span class="s">AS</span><span class="nv"> </span><span class="s">timestamp,</span><span class="nv"> </span><span class="s">topic()</span><span class="nv"> </span><span class="s">AS</span><span class="nv"> </span><span class="s">topic,</span><span class="nv"> </span><span class="s">encode(*,</span><span class="nv"> </span><span class="s">'base64')</span><span class="nv"> </span><span class="s">AS</span><span class="nv"> </span><span class="s">data</span><span class="nv"> </span><span class="s">FROM</span><span class="nv"> </span><span class="s">'sensors/+/v1'"</span>
+ <span class="na">sqlVersion</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2016-03-23"</span>
+
+<span class="na">plugins</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">serverless-plugin-typescript</span>
+</code></pre></div></div>
+
+<p>The YAML file for serverless may seem a bit intimidating at first, but it’s simple. It essentially does two things. Firstly, it creates an IoTPolicy for the things. A Thing can only publish or subscribe to its own topic with IoTPolicies, allowing for a granular level of security.</p>
+
+<p>Also, it’s worth noting that we are using ARM64 architecture. Lambdas running on the ARM architecture are not only more cost-effective but also more efficient compared to x86_64.</p>
+
+<p>The second part is the definition of the lambda function and its trigger. It uses a query in IoT Core, which is the key to working with binary data in IoT Core. You need to encode the data in <strong>base64</strong> before sending it to the lambda function; otherwise, it won’t work. This lambda function is “listening” to the sensors topic from any Thing, hence the plus symbol in the topic. By default, I prefer to version APIs, and a topic shouldn’t be an exception. Therefore, this is version 1 of my project.</p>
+
+<p>Now let’s take a look at the lambda function itself. For this project, I chose to use TypeScript, but AWS Lambda and the Serverless Framework support various programming languages.</p>
+
+<p>The code is quite straightforward. It receives the binary payload in the <code class="language-plaintext highlighter-rouge">data</code> parameter (as defined in the SQL statement above). First, it decodes the payload from base64 to binary. Then, it decompresses it using ZSTD and, finally, utilizes the CBOR library to parse it into a JavaScript object, ready to be used.</p>
+
+<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">decompressSync</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@skhaz/zstd</span><span class="dl">"</span><span class="p">;</span>
+<span class="k">import</span> <span class="p">{</span> <span class="nx">decodeFirstSync</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">cbor</span><span class="dl">"</span><span class="p">;</span>
+
+<span class="k">export</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">handler</span><span class="p">(</span><span class="nx">event</span><span class="p">:</span> <span class="p">{</span> <span class="nl">timestamp</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span> <span class="nl">topic</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="nl">data</span><span class="p">:</span> <span class="kr">string</span> <span class="p">})</span> <span class="p">{</span>
+ <span class="c1">// Extract from event some variables.</span>
+ <span class="kd">const</span> <span class="p">{</span> <span class="nx">timestamp</span><span class="p">,</span> <span class="nx">topic</span><span class="p">,</span> <span class="nx">data</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">event</span><span class="p">;</span>
+
+ <span class="c1">// Decode from base64 to binary.</span>
+ <span class="kd">const</span> <span class="nx">buffer</span> <span class="o">=</span> <span class="nx">Buffer</span><span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="dl">"</span><span class="s2">base64</span><span class="dl">"</span><span class="p">);</span>
+ <span class="c1">// Decompress using ZSTD algorithm.</span>
+ <span class="kd">const</span> <span class="nx">cbor</span> <span class="o">=</span> <span class="nx">decompressSync</span><span class="p">(</span><span class="nx">buffer</span><span class="p">);</span>
+ <span class="c1">// Parse the binary CBOR to JavaScript object.</span>
+ <span class="kd">const</span> <span class="nx">payload</span> <span class="o">=</span> <span class="nx">decodeFirstSync</span><span class="p">(</span><span class="nx">cbor</span><span class="p">);</span>
+
+ <span class="c1">// Print the sensor array data.</span>
+ <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">payload</span><span class="p">);</span>
+<span class="p">}</span>
+</code></pre></div></div>
+
+<h3 id="conclusion">Conclusion</h3>
+
+<p>In conclusion, it is possible to save a few bytes or even more with these techniques while utilizing cloud solutions like IoT Core in your projects. It allows for efficient data handling and enables the integration of cloud services into your applications.</p>
+
+<p>The compression ratio can vary dramatically, depending solely on the input. In my experiments with numerical data, it ranged from around 30% to 40%.</p>
+ 2023-10-17T00:00:00+00:00
+ Rodrigo Delduca
+
+
+ Thiago Avelino: Gentlemen’s Club vs Strip Club: Is There Really a Difference?
+ https://podcasters.spotify.com/pod/show/strip-clubs-las-vegas/episodes/Gentlemens-Club-vs-Strip-Club-Is-There-Really-a-Difference-e27337q
+ <p>Confused about the distinctions between Strip Clubs and Gentlemen&#39;s Clubs in Las Vegas? In this episode, we&#39;re going to dissect what sets these venues apart and what you can anticipate from the very best of both. Be sure to listen in as we demystify the Las Vegas club scene.</p>
+<p><br></p>
+<p><a href="https://www.stripclubconcierge.com/" target="_blank" rel="noopener noreferer">https://www.stripclubconcierge.com/</a></p>
+<p><br></p>
+<p><a href="https://www.stripclubconcierge.com/gentlemens-club-vs-strip-club/" target="_blank" rel="noopener noreferer">https://www.stripclubconcierge.com/gentlemens-club-vs-strip-club/</a></p>
+<p><br></p>
+<p>Tags: Gentlemens Clubs Las Vegas, Best Gentlemens Clubs Las Vegas, Treasures Gentlemen’s Club, Crazy Horse III Gentlemen’s Club</p>
+ 2023-08-15T13:21:00+00:00
+
+
+ Thiago Avelino: Las Vegas Strippers 101: Your 2023 Guide to "Strippers to the Room" (Private Bachelor Party Bookings)
+ https://podcasters.spotify.com/pod/show/strip-clubs-las-vegas/episodes/Las-Vegas-Strippers-101-Your-2023-Guide-to-Strippers-to-the-Room-Private-Bachelor-Party-Bookings-e273351
+ <p>Are you mulling over hiring Las Vegas strippers for a bachelor party? In this episode, we help you weigh up the pros and cons between opting for private in-room entertainers and taking the gang to a strip club. Make sure you tune in to get all the information you need for planning the ultimate bachelor party.</p>
+<p><br></p>
+<p><a href="https://www.stripclubconcierge.com/" target="_blank" rel="noopener noreferer">https://www.stripclubconcierge.com/</a></p>
+<p><br></p>
+<p><a href="https://www.stripclubconcierge.com/las-vegas-strippers/" target="_blank" rel="noopener noreferer">https://www.stripclubconcierge.com/las-vegas-strippers/</a></p>
+<p><br></p>
+<p>Tags: Las Vegas Bachelor Party, Las Vegas Guides, Las Vegas Strippers, Outcall Strippers, Private Strippers</p>
+ 2023-08-12T13:18:00+00:00
+
+
+ Ellison Leão: Gerando documentação vimdoc de plugins Neovim com Github Actions
+ https://dev.to/ellisonleao/gerando-documentacao-vimdoc-de-plugins-neovim-com-github-actions-22if
+ <p><em>Dificuldade: intermediário</em></p>
+
+<p>Escrever documentação nem sempre é uma das tarefas mais prazerosas, mas ter uma boa documentação nos nossos projetos é o que realmente faz diferença. Ainda mais quando usamos ferramentas que nos auxiliam nessa escrita.</p>
+
+<p>Nesse post vamos ensinar como automatizar a geração da documentação específica para plugins <strong>Neovim</strong> utilizando <strong>Github Actions</strong></p>
+
+<h2>
+
+
+ Entendendo o fluxo
+</h2>
+
+<p>Assumindo que entendemos o fluxo básico das <strong>Github Actions</strong>, temos o seguinte fluxo para a geração da doc:</p>
+
+<ol>
+<li>Fazemos um push na branch principal (geralmente chamada de <code>main</code>)</li>
+<li>Um job chamado <code>docs</code> é iniciado com os seguintes passos:
+
+<ul>
+<li>Conversão do <code>README.md</code> do seu projeto em um arquivo .txt no formato <a href="https://vimdoc.sourceforge.net/htmldoc/helphelp.html#help-writing" rel="noopener noreferrer">vimdoc</a>
+</li>
+<li>Criamos um commit e fazemos o push na mesma branch com o resultado da conversão</li>
+</ul>
+</li>
+</ol>
+
+<p>Convertendo isso para o <strong>Github Actions</strong>, temos:</p>
+
+<ol>
+<li>Crie um arquivo <code>.github/workflows/docs.yml</code> com o seguinte conteúdo:
+</li>
+</ol>
+
+<div class="highlight js-code-highlight">
+<pre class="highlight yaml"><code><span class="na">on</span><span class="pi">:</span>
+ <span class="na">push</span><span class="pi">:</span>
+ <span class="na">branches</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">main</span> <span class="c1"># aqui nossa branch principal se chama `main`</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">docs</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">needs</span><span class="pi">:</span> <span class="s">test</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v3</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">panvimdoc</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">kdheepak/panvimdoc@main</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">vimdoc</span><span class="pi">:</span> <span class="s">nome-do-seu-projeto</span> <span class="c1"># sem extensão .txt</span>
+ <span class="na">version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Neovim</span><span class="nv"> </span><span class="s">>=</span><span class="nv"> </span><span class="s">0.8.0"</span>
+ <span class="na">demojify</span><span class="pi">:</span> <span class="kc">true</span> <span class="c1"># coloque false caso queira manter os emojis</span>
+ <span class="na">treesitter</span><span class="pi">:</span> <span class="kc">true</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Push changes</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">stefanzweifel/git-auto-commit-action@v4</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">commit_message</span><span class="pi">:</span> <span class="s2">"</span><span class="s">auto-generate</span><span class="nv"> </span><span class="s">vimdoc"</span>
+ <span class="na">commit_user_name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">github-actions[bot]"</span>
+ <span class="na">commit_user_email</span><span class="pi">:</span> <span class="s2">"</span><span class="s">github-actions[bot]@users.noreply.github.com"</span>
+ <span class="na">commit_author</span><span class="pi">:</span> <span class="s2">"</span><span class="s">github-actions[bot]</span><span class="nv"> </span><span class="s"><github-actions[bot]@users.noreply.github.com>"</span>
+</code></pre>
+
+</div>
+
+
+
+<h2>
+
+
+ Preparando seu README
+</h2>
+
+<p>Para um bom resultado de uma documentação vimdoc, recomendamos que seu README tenha pelo menos:</p>
+
+<ul>
+<li>Uma seção <code>Sobre</code>
+</li>
+<li>Uma seção <code>Instalação</code>
+</li>
+<li>Uma seção <code>Como usar</code>
+</li>
+</ul>
+
+<p>O uso de tabelas, bullet points também é suportado e recomendado. Quanto mais detalhes conseguir colocar na documentação, melhor ficará o resultado (até emojis são suportados!)</p>
+
+<h2>
+
+
+ Colocando tudo para funcionar
+</h2>
+
+<p>Antes de commitar e rodar a action pela primeira vez, crie o arquivo vazio da doc:<br />
+</p>
+
+<div class="highlight js-code-highlight">
+<pre class="highlight shell"><code><span class="nv">$ </span><span class="nb">touch </span>doc/nome-do-seu-projeto.txt
+<span class="nv">$ </span>git add doc/nome-do-seu-projeto.txt
+<span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s1">'adicionando arquivo vimdoc'</span>
+<span class="nv">$ </span>git push
+</code></pre>
+
+</div>
+
+
+
+<p>Agora envie o novo worflow:<br />
+</p>
+
+<div class="highlight js-code-highlight">
+<pre class="highlight shell"><code><span class="nv">$ </span>git add .github/workflows/docs.yml
+<span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s1">'adicionando docs workflow'</span>
+<span class="nv">$ </span>git push
+</code></pre>
+
+</div>
+
+
+
+<p>Se tudo deu certo, você já poderá ver o resultado do workflow com seu vimdoc gerado na branch principal. Exemplo do resultado na imagem abaixo</p>
+
+<p><a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pre86sg64pzp5y69bdg.png" class="article-body-image-wrapper"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pre86sg64pzp5y69bdg.png" alt="resultado do job" width="800" height="377" /></a></p>
+
+<p>Exemplo pode ser visto <a href="https://github.com/ellisonleao/carbon-now.nvim/actions/runs/5673377787" rel="noopener noreferrer">aqui</a></p>
+
+<h2>
+
+
+ Links úteis
+</h2>
+
+<ul>
+<li>Documentação da <a href="https://kdheepak.com/panvimdoc/" rel="noopener noreferrer">github action</a>
+</li>
+<li>Exemplo de um bom <a href="https://github.com/folke/lazy.nvim/blob/main/README.md" rel="noopener noreferrer">README</a> vs o <a href="https://github.com/folke/lazy.nvim/blob/main/doc/lazy.nvim.txt" rel="noopener noreferrer">resultado</a> em vimdoc</li>
+</ul>
+
+<p>Se gostou do artigo, não esqueça de compartilhar em outras redes e aproveita e me segue também! <a href="https://linktr.ee/ellisonleao" rel="noopener noreferrer">https://linktr.ee/ellisonleao</a></p>
+ 2023-07-26T20:40:59+00:00
+
+
+ Blog do PauloHRPinheiro: Debugando em Python
+ https://paulohrpinheiro.xyz/texts/python/2022-12-17-debugando-em-python.html
+ Iniciando a usar o módulo pdb do Python para debugar com maestria, na linha de comando
+ 2022-12-17T00:00:00+00:00
+
+
+ PythonClub: Questões para estudo de algoritmos
+ https://pythonclub.com.br/questoes-para-estudo-de-algoritmos.html
+ <p>Recentemente li o texto do Maycon Alves, <a href="https://mayconbalves.com.br/3-algoritmos-para-voc%C3%AA-sua-l%C3%B3gica/">"3 algoritmos para você sua lógica"</a>, onde são apresentados 3 problemas para treinar a lógica e escrita de algoritmos: <a href="https://pt.wikipedia.org/wiki/Fatorial">cálculo de fatorial</a>, <a href="https://pt.wikipedia.org/wiki/N%C3%BAmero_primo">identificar se um número é primo</a>, e <a href="https://pt.wikipedia.org/wiki/Sequ%C3%AAncia_de_Fibonacci">calcular os valores da sequência de Fibonacci</a>. São problemas interessantes, e após resolvê-los, pode-se fazer outras perguntas que levam a um entendimento mais profundo desses algoritmos. Eu recomendo que leiam o texto do Maycon primeiro e tentem implementar uma solução para esses problemas propostos, e com isso feito, vamos discutir um pouco sobre eles.</p>
+<h2>Analisando as soluções</h2>
+<p>No texto do Maycon, tem uma dica sobre o problema da sequência de Fibonacci, onde é dito que ele pode ser resolvido usando recursividade ou <em>loops</em>. Vamos analisar essas opções.</p>
+<p>Uma solução recursiva pode ser implementada como a baixo. O código dessa solução é simples e se aproxima bastante da descrição matemática do problema.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">n</span> <span class="o"><=</span> <span class="mi">1</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">n</span>
+ <span class="k">return</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Enquanto uma solução iterativa (com <em>loop</em>) pode ser um pouco mais complicada de se ler:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
+ <span class="n">a</span> <span class="o">=</span> <span class="mi">0</span>
+ <span class="n">b</span> <span class="o">=</span> <span class="mi">1</span>
+ <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
+ <span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> <span class="n">b</span><span class="p">,</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span>
+ <span class="k">return</span> <span class="n">b</span>
+</pre></div>
+
+
+<p>Essas mesmas técnicas podem ser utilizadas para resolver o cálculo do fatorial. Onde uma implementação recursiva e outra iterativa podem ser vistas a baixo:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">fatorial</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">n</span> <span class="o"><=</span> <span class="mi">1</span><span class="p">:</span>
+ <span class="k">return</span> <span class="mi">1</span>
+ <span class="k">return</span> <span class="n">n</span> <span class="o">*</span> <span class="n">fatorial</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">fatorial</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="mi">0</span>
+ <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
+ <span class="n">valor</span> <span class="o">*=</span> <span class="n">i</span>
+ <span class="k">return</span> <span class="n">valor</span>
+</pre></div>
+
+
+<p>Com essas soluções implementadas, vem uma pergunta: Existe alguma diferença entre elas, além da diferença de como isso está expresso no código? Um primeiro teste que pode ser feito é de desempenho visando observar quanto tempo cada implementação leva para calcular a resposta. Os testes foram executados em um notebook com processador Intel Core i7-6500U CPU @ 2.50GHz, memória RAM DDR4 2133MHz, no Python 3.9.2 do Debian 11, desativamente o <em>garbage collector</em> do Python durante a execução das funções para ter um resultado com menos variação, apresentados como uma média de 10 execuções (<a href="https://github.com/eduardoklosowski/blog/tree/main/content/2022-10-17-questoes-para-estudo-de-algoritmos">código utilizado</a>).</p>
+<p>O gráfico a baixo mostra o tempo de execução das implementações que calculam os valores da sequência de Fibonacci, onde é possível observar que a implementação iterativa mantém uma linearidade do tempo conforme vai se pedindo números maiores da sequência, diferente da implementação recursiva, que a cada valor quase dobra o tempo de execução.</p>
+<p><img alt="Gráfico do tempo de execução Fibonacci" src="https://pythonclub.com.br/feeds/images/eduardoklosowski/questoes-para-estudo-de-algoritmos/fibonacci.png" /></p>
+<p>E a baixo pode-se observar o gráfico para as implementações que calculam o fatorial, que devido aos tempos serem mais baixos, possui uma variação um pouco maior, e é possível observar uma tendência de reta para as duas implementações, com a implementação recursiva tendo um ângulo um pouco mais íngreme, implicando em um algoritmo mais lento.</p>
+<p><img alt="Gráfico do tempo de execução fatorial" src="https://pythonclub.com.br/feeds/images/eduardoklosowski/questoes-para-estudo-de-algoritmos/fatorial.png" /></p>
+<p>A partir desses dois gráficos algumas perguntas podem ser feitas: Por que a implementação recursiva do Fibonacci apresentou uma curva que se aproxima de uma exponencial e não de uma reta como as demais? Qual a diferença para a implementação recursiva do fatorial que explicar isso? Implementações recursivas são sempre piores que as implementações iterativas, ou existem casos em elas superem ou se equivalem as iterativas?</p>
+<p>Saindo um pouco desses gráficos, outras questões podem ser levantadas, como: Existe mais algum aspecto ou característica além do tempo de execução (e facilidade de leitura do código) para a escolha entre uma implementação ou outra? Considerando que essas funções vão rodar em um servidor, existe alguma coisa que possa ser feita para melhorar a performance dessas funções, como reaproveitar resultados já calculados para se calcular novos resultados? Como isso poderia ser feito? Quais são as desvantagens dessa abordagem?</p>
+<p>Olhando para o problema de verificar se um número é primo, existe o <a href="https://pt.wikipedia.org/wiki/Crivo_de_Erat%C3%B3stenes">crivo de Eratóstenes</a>, ele é uma implementação eficiente? Existem casos em que ele pode ser uma boa solução ou não? O exemplo a baixo (retirado da Wikipédia) mostra o processo para encontrar todos os números primos até 120, existe alguma forma de adaptá-lo para executar diversas vezes reaproveitando o que já foi calculado, como sempre retornar o próximo número primo?</p>
+<p><img alt="Exemplo do crivo de Eratóstenes" src="https://upload.wikimedia.org/wikipedia/commons/8/8c/New_Animation_Sieve_of_Eratosthenes.gif" /></p>
+<h2>Considerações</h2>
+<p>Se você nunca se deparou com perguntas desse tipo, seja bem-vindo a área de análise de algoritmos, onde após se ter uma solução, busca-se analisar e descrever o comportamento do algoritmo, e até a busca de algoritmos mais eficientes. E trazendo para o dia a dia de um desenvolvedor, essas questões podem ser a resposta do motivo do código funcionar muito bem no computador de quem desenvolveu, mas demorar muito ou apresentar problemas para rodar no servidor de produção, ou com o tempo (e crescimento do volume de dados) começar a dar problemas.</p>
+<p>Nesse artigo eu apenas levantei as perguntas, deixo que cada um busque as respostas, que existem. Sintam-se livres para me procurar para discutir alguma questão ou orientações para encontrar as respostas, seja nos comentários do texto no <a href="https://dev.to/eduardoklosowski/questoes-para-estudo-de-algoritmos-5dab">dev.to</a> ou no <a href="https://twitter.com/eduklosowski">Twitter</a>.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ 2022-10-17T18:00:00+00:00
+ Eduardo Klosowski
+
+
+ Ellison Leão: Meus 10 plugins favoritos para o Neovim em 2022
+ https://dev.to/ellisonleao/meus-10-plugins-favoritos-para-o-neovim-em-2022-43g2
+ <blockquote>
+<p>Uma breve lista dos melhores plugins pra usar agora e aumentar sua produtividade</p>
+</blockquote>
+
+<p>Quando comecei a usar o Neovim, lá por 2017, depois de passar 6 anos usando Vim, não tinha sentido uma diferença tão grande porque primeiro a API lua era praticamente inexistente e minha configuração ainda era puramente vimscript e meus plugins também. </p>
+
+<p>Mas tudo mudou quando fiz o upgrade pras versões 0.4.x e gradualmente comecei a me aventurar no mundo do Lua, no final de 2019. De lá pra ca já consegui migrar minhas configurações 100% para Lua e 99% dos plugins que uso hoje também são feitos na linguagem. Aqui vai minha lista dos 10 melhores plugins que acho indispensáveis hoje:</p>
+
+<h2>
+
+
+ #10
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/wbthomason" rel="noopener noreferrer">
+ wbthomason
+ </a> / <a href="https://github.com/wbthomason/packer.nvim" rel="noopener noreferrer">
+ packer.nvim
+ </a>
+ </h2>
+ <h3>
+ A use-package inspired plugin manager for Neovim. Uses native packages, supports Luarocks dependencies, written in Lua, allows for expressive config
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<p><strong>NOTICE:</strong></p>
+<p>This repository is currently unmaintained. For the time being (as of August, 2023), it is recommended to use one of the following plugin managers instead:</p>
+<ul>
+<li>
+<a href="https://github.com/folke/lazy.nvim" rel="noopener noreferrer">lazy.nvim</a>: Most stable and maintained plugin manager for Nvim.</li>
+<li>
+<a href="https://github.com/lewis6991/pckr.nvim" rel="noopener noreferrer">pckr.nvim</a>: Spiritual successor of packer.nvim. Functional but not as stable as lazy.nvim.</li>
+</ul>
+<div class="markdown-heading">
+<h1 class="heading-element">packer.nvim</h1>
+</div>
+<p><a href="https://gitter.im/packer-nvim/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/335c9ee0b540d19a29323d900502be99a012414abdc965f6e4bd7047842f6541/68747470733a2f2f6261646765732e6769747465722e696d2f7061636b65722d6e76696d2f636f6d6d756e6974792e737667" alt="Gitter" /></a></p>
+<p><a href="https://github.com/jwiegley/use-package" rel="noopener noreferrer"><code>use-package</code></a> inspired plugin/package management for
+Neovim.</p>
+<p>Have questions? Start a <a href="https://github.com/wbthomason/packer.nvim/discussions" rel="noopener noreferrer">discussion</a>.</p>
+<p>Have a problem or idea? Make an <a href="https://github.com/wbthomason/packer.nvim/issues" rel="noopener noreferrer">issue</a> or a <a href="https://github.com/wbthomason/packer.nvim/pulls" rel="noopener noreferrer">PR</a>.</p>
+<p><strong>Packer is built on native packages. You may wish to read <code>:h packages</code> before continuing</strong></p>
+<div class="markdown-heading">
+<h2 class="heading-element">Table of Contents</h2>
+</div>
+<ol>
+<li><a href="https://github.com/wbthomason/packer.nvim#features" rel="noopener noreferrer">Features</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#requirements" rel="noopener noreferrer">Requirements</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#quickstart" rel="noopener noreferrer">Quickstart</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#bootstrapping" rel="noopener noreferrer">Bootstrapping</a></li>
+<li>
+<a href="https://github.com/wbthomason/packer.nvim#usage" rel="noopener noreferrer">Usage</a>
+<ol>
+<li><a href="https://github.com/wbthomason/packer.nvim#the-startup-function" rel="noopener noreferrer">The startup function</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#custom-initialization" rel="noopener noreferrer">Custom Initialization</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#specifying-plugins" rel="noopener noreferrer">Specifying Plugins</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#performing-plugin-management-operations" rel="noopener noreferrer">Performing plugin management operations</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#extending-packer" rel="noopener noreferrer">Extending packer</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#compiling-lazy-loaders" rel="noopener noreferrer">Compiling Lazy-Loaders</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#user-autocommands" rel="noopener noreferrer">User autocommands</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#using-a-floating-window" rel="noopener noreferrer">Using a floating window</a></li>
+</ol>
+</li>
+<li><a href="https://github.com/wbthomason/packer.nvim#profiling" rel="noopener noreferrer">Profiling</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#debugging" rel="noopener noreferrer">Debugging</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#compatibility-and-known-issues" rel="noopener noreferrer">Compatibility and known issues</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#contributors" rel="noopener noreferrer">Contributors</a></li>
+</ol>
+<div class="markdown-heading">
+<h2 class="heading-element">Features</h2>
+</div>
+<ul>
+<li>Declarative plugin specification</li>
+<li>Support for dependencies</li>
+<li>Support for Luarocks dependencies</li>
+<li>Expressive configuration and lazy-loading options</li>
+<li>Automatically compiles efficient lazy-loading code to improve startup time</li>
+<li>…</li>
+</ul>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/wbthomason/packer.nvim" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Começando por esse incrível gerenciador de plugins, que vai deixar sua vida bem mais fácil na hora de instalar, atualizar e configurar seus plugins, principalmente se você utiliza lua nas suas configurações. Tem suporte a instalação de pacotes do luarocks, <em>lazy loading</em> e mais.</p>
+
+<h2>
+
+
+ #9
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/numToStr" rel="noopener noreferrer">
+ numToStr
+ </a> / <a href="https://github.com/numToStr/Comment.nvim" rel="noopener noreferrer">
+ Comment.nvim
+ </a>
+ </h2>
+ <h3>
+ 🧠 💪 // Smart and powerful comment plugin for neovim. Supports treesitter, dot repeat, left-right/up-down motions, hooks, and more
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">// Comment.nvim </h1>
+</div>
+<p><sup>⚡ Smart and Powerful commenting plugin for neovim ⚡</sup></p>
+<p><a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/42532967/136532939-926a8350-84b7-4e78-b045-fe21b5947388.gif"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F42532967%2F136532939-926a8350-84b7-4e78-b045-fe21b5947388.gif" alt="Comment.nvim" title="Commenting go brrrr" /></a></p>
+<div class="markdown-heading">
+<h3 class="heading-element">✨ Features</h3>
+</div>
+<ul>
+<li>Supports treesitter. <a href="https://github.com/numToStr/Comment.nvim#treesitter" rel="noopener noreferrer">Read more</a>
+</li>
+<li>Supports <code>commentstring</code>. Read <code>:h comment.commentstring</code>
+</li>
+<li>Supports line (<code>//</code>) and block (<code>/* */</code>) comments</li>
+<li>Dot (<code>.</code>) repeat support for <code>gcc</code>, <code>gbc</code> and friends</li>
+<li>Count support for <code>[count]gcc</code> and <code>[count]gbc</code>
+</li>
+<li>Left-right (<code>gcw</code> <code>gc$</code>) and Up-Down (<code>gc2j</code> <code>gc4k</code>) motions</li>
+<li>Use with text-objects (<code>gci{</code> <code>gbat</code>)</li>
+<li>Supports pre and post hooks</li>
+<li>Ignore certain lines, powered by Lua regex</li>
+</ul>
+<div class="markdown-heading">
+<h3 class="heading-element">🚀 Installation</h3>
+</div>
+<ul>
+<li>With <a href="https://github.com/folke/lazy.nvim" rel="noopener noreferrer">lazy.nvim</a>
+</li>
+</ul>
+<div class="highlight highlight-source-lua notranslate position-relative overflow-auto js-code-highlight">
+<pre><span class="pl-c"><span class="pl-c">--</span> add this to your lua/plugins.lua, lua/plugins/init.lua, or the file you keep your other plugins:</span>
+{
+ <span class="pl-s"><span class="pl-pds">'</span>numToStr/Comment.nvim<span class="pl-pds">'</span></span>,
+ <span class="pl-smi">opts</span> <span class="pl-k">=</span> {
+ <span class="pl-c"><span class="pl-c">--</span> add any options here</span>
+ }
+}
+</pre>
+
+</div>
+<ul>
+<li>With <a href="https://github.com/wbthomason/packer.nvim" rel="noopener noreferrer">packer.nvim</a>
+</li>
+</ul>
+<div class="highlight highlight-source-lua notranslate position-relative overflow-auto js-code-highlight">
+<pre><span class="pl-c1">use</span> {
+ <span class="pl-s"><span class="pl-pds">'</span>numToStr/Comment.nvim<span class="pl-pds">'</span></span>,
+ <span class="pl-en">config</span> <span class="pl-k">=</span> <span class="pl-k">function</span>()
+ <span class="pl-c1">require</span>(<span class="pl-s"><span class="pl-pds">'</span>Comment<span class="pl-pds">'</span></span>).<span class="pl-c1">setup</span>()
+ <span class="pl-k">end</span>
+}</pre>
+
+</div>
+<ul>
+<li>With <a href="https://github.com/junegunn/vim-plug" rel="noopener noreferrer">vim-plug</a>
+</li>
+</ul>
+<div class="highlight highlight-source-viml notranslate position-relative overflow-auto js-code-highlight">
+<pre>Plug <span class="pl-s"><span class="pl-pds">'</span>numToStr/Comment.nvim<span class="pl-pds">'</span></span>
+<span class="pl-c"><span class="pl-c">"</span> Somewhere after plug#end()</span></pre>…
+</div>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/numToStr/Comment.nvim" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Um poderoso plugin para inserir e remover comentários em códigos. Tem suporte ao treesitter, que vamos falar mais pra frente. </p>
+
+<h2>
+
+
+ #8
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/neovim" rel="noopener noreferrer">
+ neovim
+ </a> / <a href="https://github.com/neovim/nvim-lspconfig" rel="noopener noreferrer">
+ nvim-lspconfig
+ </a>
+ </h2>
+ <h3>
+ Quickstart configs for Nvim LSP
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">nvim-lspconfig</h1>
+</div>
+<p>nvim-lspconfig is a "data only" repo, providing basic, default <a href="https://neovim.io/doc/user/lsp.html" rel="nofollow noopener noreferrer">Nvim LSP client</a>
+configurations for various LSP servers.</p>
+<p>View <a href="https://github.com/neovim/nvim-lspconfigdoc/configs.md" rel="noopener noreferrer">all configs</a> or <code>:help lspconfig-all</code> from Nvim.</p>
+<div class="markdown-heading">
+<h2 class="heading-element">Important ⚠️
+</h2>
+</div>
+<ul>
+<li>These configs are <strong>best-effort and supported by the community (you).</strong> See <a href="https://github.com/neovim/nvim-lspconfig#contributions" rel="noopener noreferrer">contributions</a>.</li>
+<li>If you found a bug in Nvim LSP (<code>:help lsp</code>), <a href="https://github.com/neovim/neovim/issues/new?assignees=&labels=bug%2Clsp&template=lsp_bug_report.yml" rel="noopener noreferrer">report it to Neovim core</a>
+<ul>
+<li>
+<strong>Do not</strong> report it here. Only configuration data lives here.</li>
+</ul>
+</li>
+<li>This repo only provides <em>configurations</em>. Its programmatic API is deprecated and must not be used externally.
+<ul>
+<li>The "framework" parts (<em>not</em> the configs) of nvim-lspconfig <a href="https://github.com/neovim/neovim/issues/28479" rel="noopener noreferrer">will be upstreamed to Nvim core</a>.</li>
+</ul>
+</li>
+</ul>
+<div class="markdown-heading">
+<h2 class="heading-element">Install</h2>
+
+</div>
+<p><a href="https://luarocks.org/modules/neovim/nvim-lspconfig" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/57aa64ed10a79399509246a1ae220f761b1c35e4d44c6c07595faf4463019fd8/68747470733a2f2f696d672e736869656c64732e696f2f6c7561726f636b732f762f6e656f76696d2f6e76696d2d6c7370636f6e6669673f6c6f676f3d6c756126636f6c6f723d707572706c65" alt="LuaRocks" /></a></p>
+<ul>
+<li>Requires Nvim 0.10 above. Update Nvim and nvim-lspconfig before reporting an issue.</li>
+<li>Install nvim-lspconfig using Vim's "packages" feature:
+<div class="snippet-clipboard-content notranslate position-relative overflow-auto"><pre class="notranslate"><code>git clone https://github.com/neovim/nvim-lspconfig ~/.config/nvim/pack/nvim/start/nvim-lspconfig
+</code></pre></div>
+</li>
+<li>Or use a 3rd-party plugin manager (consult the documentation for your plugin manager).</li>
+</ul>
+<div class="markdown-heading">
+<h2 class="heading-element">Quickstart</h2>
+
+</div>
+<ol>
+<li>Install a language server, e.g. <a href="https://github.com/neovim/nvim-lspconfigdoc/configs.md#pyright" rel="noopener noreferrer">pyright</a>
+<div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight">
+<pre>npm i -g pyright</pre>
+
+</div>
+</li>
+<li>Add…</li>
+</ol>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/neovim/nvim-lspconfig" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Plugin obrigatório para quem quer utilizar o maravilhoso cliente nativo LSP. Com esse plugin a configuração dos LSP servers das linguagens fica extremamente fácil.</p>
+
+<h2>
+
+
+ #7
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/williamboman" rel="noopener noreferrer">
+ williamboman
+ </a> / <a href="https://github.com/williamboman/nvim-lsp-installer" rel="noopener noreferrer">
+ nvim-lsp-installer
+ </a>
+ </h2>
+ <h3>
+ Further development has moved to https://github.com/williamboman/mason.nvim!
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h2 class="heading-element">ℹ️ Project status</h2>
+</div>
+<blockquote>
+<p>This is an excerpt from the <a href="https://github.com/williamboman/nvim-lsp-installer/discussions/876" rel="noopener noreferrer">announcement here</a>.</p>
+</blockquote>
+<p><em><code>nvim-lsp-installer</code> is no longer maintained.</em></p>
+<p><a href="https://github.com/williamboman/mason.nvim" rel="noopener noreferrer"><code>mason.nvim</code></a> is the next generation version of <code>nvim-lsp-installer</code>. It
+builds on top of the very same foundation as <code>nvim-lsp-installer</code> (which means it's easy to migrate), but with a
+majority of internals refactored to improve extensibility and testability.</p>
+<p>More importantly, the scope of <code>mason.nvim</code> has also been widened to target more than just LSP servers. <code>mason.nvim</code>
+supports DAP servers, linters, formatters, and more. As of writing, <code>mason.nvim</code> provides 150+ packages for 100+
+languages. It can be thought of as a general-purpose package manager, native to Neovim, that runs everywhere Neovim runs
+(Windows, macOS, Linux, etc.).</p>
+<p>Another big change with <code>mason.nvim</code> is that executables are now linked to a single, shared, location, allowing seamless
+access from Neovim builtins (shell, terminal, etc.) as well as other 3rd party plugins.</p>
+<p>There have also been other improvements…</p>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/williamboman/nvim-lsp-installer" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Plugin que fornece todo suporte para uma rápida instalação dos LSP servers com uma excelente UX. É um plugin que funciona em conjunto com o <strong>nvim-lspconfig</strong></p>
+
+<h2>
+
+
+ #6
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/jose-elias-alvarez" rel="noopener noreferrer">
+ jose-elias-alvarez
+ </a> / <a href="https://github.com/jose-elias-alvarez/null-ls.nvim" rel="noopener noreferrer">
+ null-ls.nvim
+ </a>
+ </h2>
+ <h3>
+ Use Neovim as a language server to inject LSP diagnostics, code actions, and more via Lua.
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">ARCHIVED</h1>
+</div>
+<p>null-ls is now archived and will no longer receive updates. Please see
+<a href="https://github.com/jose-elias-alvarez/null-ls.nvim/issues/1621" rel="noopener noreferrer">this issue</a> for
+details.</p>
+<div class="markdown-heading">
+<h1 class="heading-element">null-ls.nvim</h1>
+</div>
+<p>Use Neovim as a language server to inject LSP diagnostics, code actions, and
+more via Lua.</p>
+<div class="markdown-heading">
+<h2 class="heading-element">Motivation</h2>
+</div>
+<p>Neovim's LSP ecosystem is growing, and plugins like
+<a href="https://github.com/nvim-telescope/telescope.nvim" rel="noopener noreferrer">telescope.nvim</a> and
+<a href="https://github.com/folke/trouble.nvim" rel="noopener noreferrer">trouble.nvim</a> make it a joy to work with
+LSP features like code actions and diagnostics.</p>
+<p>Unlike the VS Code and coc.nvim ecosystems, Neovim doesn't provide a way for
+non-LSP sources to hook into its LSP client. null-ls is an attempt to bridge
+that gap and simplify the process of creating, sharing, and setting up LSP
+sources using pure Lua.</p>
+<p>null-ls is also an attempt to reduce the boilerplate required to set up
+general-purpose language servers and improve performance by removing the need
+for external processes.</p>
+<div class="markdown-heading">
+<h2 class="heading-element">Status</h2>
+
+</div>
+<p>null-ls is in <strong>beta status</strong>. Please see below for steps to follow if something
+doesn't work the way…</p>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/jose-elias-alvarez/null-ls.nvim" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Gere diagnósticos, formate, realize ações de código e mais, utilizando esse plugin que utiliza o neovim como um servidor LSP, e faz a ponte entre ferramentas que não fazem uma conexão direta com o cliente nativo LSP. Entre os exemplos podemos citar:</p>
+
+<ul>
+<li>Formatar código ao salvar o buffer com o prettier</li>
+<li>Gerar relatório de erros e warnings com golangci-lint para Go</li>
+<li>Formatar código Lua com stylua</li>
+</ul>
+
+<p>e <a href="https://github.com/jose-elias-alvarez/null-ls.nvim/blob/main/doc/BUILTINS.md" rel="noopener noreferrer">mais</a>! </p>
+
+<h2>
+
+
+ #5
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/nvim-treesitter" rel="noopener noreferrer">
+ nvim-treesitter
+ </a> / <a href="https://github.com/nvim-treesitter/nvim-treesitter" rel="noopener noreferrer">
+ nvim-treesitter
+ </a>
+ </h2>
+ <h3>
+ Nvim Treesitter configurations and abstraction layer
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div>
+ <div class="markdown-heading">
+<h1 class="heading-element">nvim-treesitter</h1>
+</div>
+ <p>
+ <a href="https://matrix.to/#/#nvim-treesitter:matrix.org" rel="nofollow noopener noreferrer">
+ <img alt="Matrix Chat" src="https://camo.githubusercontent.com/954e3fc1a9172d1c08e4e307b5361d27a5a48d45818951fffab09bdc86fa13e9/68747470733a2f2f696d672e736869656c64732e696f2f6d61747269782f6e76696d2d747265657369747465723a6d61747269782e6f7267" />
+ </a>
+ <a href="https://github.com/nvim-treesitter/nvim-treesitter/actions?query=workflow%3A%22Linting+and+style+checking%22+branch%3Amaster" rel="noopener noreferrer">
+ <img alt="Linting and Style" src="https://github.com/nvim-treesitter/nvim-treesitter/workflows/Linting%20and%20style%20checking/badge.svg" />
+ </a>
+ <a href="https://github.com/nvim-treesitter/nvim-treesitter/actions?query=workflow%3A%22Check+loading+of+syntax+files%22+branch%3Amaster" rel="noopener noreferrer">
+ <img alt="Syntax files" src="https://github.com/nvim-treesitter/nvim-treesitter/workflows/Check%20loading%20of%20syntax%20files/badge.svg" />
+ </a>
+ </p>
+</div>
+<div>
+ <p>
+ <a rel="noopener noreferrer" href="https://github.com/nvim-treesitter/nvim-treesitterassets/logo.png"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fnvim-treesitter%2Fnvim-treesitterassets%2Flogo.png" alt="Logo" /></a>
+ </p>
+ <p>
+ <a href="https://github.com/tree-sitter/tree-sitter" rel="noopener noreferrer">Treesitter</a>
+ configurations and abstraction layer for
+ <a href="https://github.com/neovim/neovim/" rel="noopener noreferrer">Neovim</a>
+ </p>
+ <p>
+ <i>
+ Logo by <a href="https://github.com/steelsojka" rel="noopener noreferrer">@steelsojka</a>
+ </i>
+ </p>
+</div>
+<p>The goal of <code>nvim-treesitter</code> is both to provide a simple and easy way to use the interface for <a href="https://github.com/tree-sitter/tree-sitter" rel="noopener noreferrer">tree-sitter</a> in Neovim and to provide some basic functionality such as highlighting based on it:</p>
+<p><a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/2361214/202753610-e923bf4e-e88f-494b-bb1e-d22a7688446f.png"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F2361214%2F202753610-e923bf4e-e88f-494b-bb1e-d22a7688446f.png" alt="example-cpp" /></a></p>
+<p>Traditional highlighting (left) vs Treesitter-based highlighting (right)
+More examples can be found in <a href="https://github.com/nvim-treesitter/nvim-treesitter/wiki/Gallery" rel="noopener noreferrer">our gallery</a>.</p>
+<p><strong>Warning: Treesitter and nvim-treesitter highlighting are an experimental feature of Neovim.
+Please consider the experience with this plug-in as experimental until Tree-Sitter support in Neovim is stable!
+We recommend using the nightly builds of Neovim if possible.
+You can find the current roadmap <a href="https://github.com/nvim-treesitter/nvim-treesitter/issues/4767" rel="noopener noreferrer">here</a>.
+The roadmap and all features of this plugin are open to change, and any suggestion will be highly appreciated!</strong></p>
+<p>Nvim-treesitter is based on three interlocking features: <a href="https://github.com/nvim-treesitter/nvim-treesitter#language-parsers" rel="noopener noreferrer"><strong>language parsers</strong></a>, <a href="https://github.com/nvim-treesitter/nvim-treesitter#adding-queries" rel="noopener noreferrer"><strong>queries</strong></a>, and <a href="https://github.com/nvim-treesitter/nvim-treesitter#available-modules" rel="noopener noreferrer"><strong>modules</strong></a>, where <em>modules</em> provide features – e.g., highlighting – based on…</p>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/nvim-treesitter/nvim-treesitter" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Um plugin que faz interface com o excelente projeto <a href="https://github.com/tree-sitter/tree-sitter" rel="noopener noreferrer">tree-sitter</a> e traz funcionalides como _syntax highlighting _, indentação, seleção incremental, movimentação, folding, entre outros. </p>
+
+<h2>
+
+
+ #4
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/L3MON4D3" rel="noopener noreferrer">
+ L3MON4D3
+ </a> / <a href="https://github.com/L3MON4D3/LuaSnip" rel="noopener noreferrer">
+ LuaSnip
+ </a>
+ </h2>
+ <h3>
+ Snippet Engine for Neovim written in Lua.
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<p><a href="https://matrix.to/#/%23luasnip:matrix.org" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/7e4c0c2d22ff5f4cb470267e74a6e420bc201b085070d0ff43c6626e84dfb09a/68747470733a2f2f696d672e736869656c64732e696f2f6d61747269782f6c7561736e69703a6d61747269782e6f72673f6c6162656c3d4d6174726978266c6f676f3d6d6174726978" alt="LuaSnip" /></a></p>
+<div class="markdown-heading">
+<h1 class="heading-element">LuaSnip</h1>
+</div>
+
+
+
+
+ <span class="m-1">javadoc.mp4</span>
+
+
+
+
+
+
+
+<div class="markdown-heading">
+<h1 class="heading-element">Features</h1>
+
+</div>
+<ul>
+<li>Tabstops</li>
+<li>Text-Transformations using Lua functions</li>
+<li>Conditional Expansion</li>
+<li>Defining nested Snippets</li>
+<li>Filetype-specific Snippets</li>
+<li>Choices</li>
+<li>Dynamic Snippet creation</li>
+<li>Regex-Trigger</li>
+<li>Autotriggered Snippets</li>
+<li>Easy Postfix Snippets</li>
+<li>Fast</li>
+<li>Parse <a href="https://microsoft.github.io/language-server-protocol/specification#snippet_syntax" rel="nofollow noopener noreferrer">LSP-Style</a> Snippets either directly in Lua, as a VSCode package or a SnipMate snippet collection.</li>
+<li>Expand LSP-Snippets with <a href="https://github.com/hrsh7th/nvim-compe" rel="noopener noreferrer">nvim-compe</a> (or its' successor, <a href="https://github.com/hrsh7th/nvim-cmp" rel="noopener noreferrer">nvim-cmp</a> (requires <a href="https://github.com/saadparwaiz1/cmp_luasnip" rel="noopener noreferrer">cmp_luasnip</a>))</li>
+<li>Snippet history (jump back into older snippets)</li>
+<li>Resolve filetype at the cursor using Treesitter</li>
+</ul>
+<div class="markdown-heading">
+<h1 class="heading-element">Drawbacks</h1>
+
+</div>
+<ul>
+<li>Snippets that make use of the entire functionality of this plugin have to be defined in Lua (but 95% of snippets can be written in LSP-syntax).</li>
+</ul>
+<div class="markdown-heading">
+<h1 class="heading-element">Requirements</h1>
+
+</div>
+<p>Neovim >= 0.7 (extmarks)
+<code>jsregexp</code> for <code>lsp-snippet-transformations</code> (see <a href="https://github.com/L3MON4D3/LuaSnip/blob/master/DOC.md#transformations" rel="noopener noreferrer">here</a> for some tips on installing it).</p>
+<div class="markdown-heading">
+<h1 class="heading-element">Setup</h1>
+
+</div>
+<div class="markdown-heading">
+<h2 class="heading-element">Install</h2>
+
+</div>
+<ul>
+<li>
+<p>With your preferred plugin manager i.e. <a href="https://github.com/junegunn/vim-plug" rel="noopener noreferrer">vim-plug</a>, <a href="https://github.com/wbthomason/packer.nvim" rel="noopener noreferrer">Packer</a> or <a href="https://github.com/folke/lazy.nvim" rel="noopener noreferrer">lazy</a><br />
+<strong>Packer</strong>:</p>
+<div class="highlight highlight-source-lua notranslate position-relative overflow-auto js-code-highlight">
+<pre><span class="pl-c1">use</span>({
+ <span class="pl-s"><span class="pl-pds">"</span>L3MON4D3/LuaSnip<span class="pl-pds">"</span></span>
+ <span class="pl-c"><span class="pl-c">--</span> follow latest release.</span>
+ <span class="pl-smi">tag</span> <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span>v2.*<span class="pl-pds">"</span></span>, <span class="pl-c"><span class="pl-c">--</span> Replace <CurrentMajor> by the latest released major (first</span></pre>…
+</div>
+</li>
+</ul>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/L3MON4D3/LuaSnip" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>O melhor script para criação de snippets do Neovim. Simples assim. Tem uma curva de aprendizado um pouco alta, mas quando se pega o jeito, você não vai querer largar nunca. <a href="https://www.youtube.com/watch?v=Dn800rlPIho" rel="noopener noreferrer">Aqui</a> um vídeo explicando mais sobre as funcionalidades dele (Em inglês)</p>
+
+<h2>
+
+
+ #3
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/hrsh7th" rel="noopener noreferrer">
+ hrsh7th
+ </a> / <a href="https://github.com/hrsh7th/nvim-cmp" rel="noopener noreferrer">
+ nvim-cmp
+ </a>
+ </h2>
+ <h3>
+ A completion plugin for neovim coded in Lua.
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">nvim-cmp</h1>
+</div>
+<p>A completion engine plugin for neovim written in Lua
+Completion sources are installed from external repositories and "sourced".</p>
+
+
+
+
+ <span class="m-1">demo.mp4</span>
+
+
+
+
+
+
+
+
+<div class="markdown-heading">
+<h1 class="heading-element">Readme!</h1>
+
+</div>
+<ol>
+<li>There is a GitHub issue that documents <a href="https://github.com/hrsh7th/nvim-cmp/issues/231" rel="noopener noreferrer">breaking changes</a> for nvim-cmp. Subscribe to the issue to be notified of upcoming breaking changes.</li>
+<li>This is my hobby project. You can support me via GitHub sponsors.</li>
+<li>Bug reports are welcome, but don't expect a fix unless you provide minimal configuration and steps to reproduce your issue.</li>
+<li>The <code>cmp.mapping.preset.*</code> is pre-defined configuration that aims to mimic neovim's native like behavior. It can be changed without announcement. Please manage key-mapping by yourself.</li>
+</ol>
+<div class="markdown-heading">
+<h1 class="heading-element">Concept</h1>
+
+</div>
+<ul>
+<li>Full support for LSP completion related capabilities</li>
+<li>Powerful customizability via Lua functions</li>
+<li>Smart handling of key mappings</li>
+<li>No flicker</li>
+</ul>
+<div class="markdown-heading">
+<h1 class="heading-element">Setup</h1>
+
+</div>
+<div class="markdown-heading">
+<h3 class="heading-element">Recommended Configuration</h3>
+
+</div>
+<p>This example configuration uses <code>vim-plug</code> as the plugin manager and <code>vim-vsnip</code> as a snippet plugin.</p>
+<div class="highlight highlight-source-viml notranslate position-relative overflow-auto js-code-highlight">
+<pre><span class="pl-c1">call</span> <span class="pl-en">plug#begin</span>(<span class="pl-smi"><span class="pl-k">s:</span>plug_dir</span>)
+Plug <span class="pl-s"><span class="pl-pds">'</span>neovim/nvim-lspconfig<span class="pl-pds">'</span></span>
+Plug <span class="pl-s"><span class="pl-pds">'</span>hrsh7th/cmp-nvim-lsp</span></pre>…
+</div>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/hrsh7th/nvim-cmp" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Seguindo o melhor plugin para snippets, esse com certeza vai ser o melhor plugin para autocomplete que você irá ter no mercado até o momento. Seu design flexível permite incluir vários tipos diferentes de <em>sources</em>, desde sugestões de LSP, snippets, etc. A lista é <a href="https://github.com/hrsh7th/nvim-cmp/wiki/List-of-sources" rel="noopener noreferrer">gigantesca</a> e você também conta com uma excelente documentação caso queira criar os seus próprios sources também.</p>
+
+<h2>
+
+
+ #2
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/nvim-lualine" rel="noopener noreferrer">
+ nvim-lualine
+ </a> / <a href="https://github.com/nvim-lualine/lualine.nvim" rel="noopener noreferrer">
+ lualine.nvim
+ </a>
+ </h2>
+ <h3>
+ A blazing fast and easy to configure neovim statusline plugin written in pure lua.
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">lualine.nvim</h1>
+</div>
+<p><a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/6da4d97c7bbc5bccb5827dacee2c9ab833fc2f7d7e800a518b68e5304c0937dd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c616e6775616765732f636f64652d73697a652f6e76696d2d6c75616c696e652f6c75616c696e652e6e76696d3f7374796c653d666c61742d737175617265"><img src="https://camo.githubusercontent.com/6da4d97c7bbc5bccb5827dacee2c9ab833fc2f7d7e800a518b68e5304c0937dd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c616e6775616765732f636f64652d73697a652f6e76696d2d6c75616c696e652f6c75616c696e652e6e76696d3f7374796c653d666c61742d737175617265" alt="code size" /></a>
+<a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/626a72ea58ecf1972a27235993041be5014e1ef417a65b4988a1ef0a13457219/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6e76696d2d6c75616c696e652f6c75616c696e652e6e76696d3f7374796c653d666c61742d737175617265"><img src="https://camo.githubusercontent.com/626a72ea58ecf1972a27235993041be5014e1ef417a65b4988a1ef0a13457219/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6e76696d2d6c75616c696e652f6c75616c696e652e6e76696d3f7374796c653d666c61742d737175617265" alt="license" /></a>
+<a href="https://buymeacoffee.com/shadmansalj" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/ad541f86521726ee2afc61b260dcb24d3a359f0bdd4f8f7ddff53456eba73b64/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4275792532304d6525323061253230436f666665652d4637393431443f7374796c653d706c6173746963266c6f676f3d6275792d6d652d612d636f66666565266c6f676f436f6c6f723d7768697465" alt="Buy Me a Coffee" /></a></p>
+<p>A blazing fast and easy to configure Neovim statusline written in Lua.</p>
+<p><code>lualine.nvim</code> requires Neovim >= 0.7.</p>
+<p>For previous versions of neovim please use compatability tags for example
+compat-nvim-0.5</p>
+<div class="markdown-heading">
+<h2 class="heading-element">Contributing</h2>
+
+</div>
+<p>Feel free to create an issue/PR if you want to see anything else implemented.
+If you have some question or need help with configuration, start a <a href="https://github.com/nvim-lualine/lualine.nvim/discussions" rel="noopener noreferrer">discussion</a>.</p>
+<p>Please read <a href="https://github.com/nvim-lualine/lualine.nvim./CONTRIBUTING.md" rel="noopener noreferrer">CONTRIBUTING.md</a> before opening a PR.
+You can also help with documentation in the <a href="https://github.com/nvim-lualine/lualine.nvim/wiki" rel="noopener noreferrer">wiki</a>.</p>
+
+<div class="markdown-heading">
+<h2 class="heading-element">Screenshots</h2>
+
+</div>
+<p>Here is a preview of what lualine can look like.</p>
+<p>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/41551030/108650373-bb025580-74bf-11eb-8682-2c09321dd18e.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F41551030%2F108650373-bb025580-74bf-11eb-8682-2c09321dd18e.png" /></a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/41551030/108650377-bd64af80-74bf-11eb-9c55-fbfc51b39fe8.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F41551030%2F108650377-bd64af80-74bf-11eb-9c55-fbfc51b39fe8.png" /></a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/41551030/108650378-be95dc80-74bf-11eb-9718-82b242ecdd54.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F41551030%2F108650378-be95dc80-74bf-11eb-9718-82b242ecdd54.png" /></a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/41551030/108650381-bfc70980-74bf-11eb-9245-85c48f0f154a.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F41551030%2F108650381-bfc70980-74bf-11eb-9245-85c48f0f154a.png" /></a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/41551030/103467925-32372b00-4d54-11eb-88d6-6d39c46854d8.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F41551030%2F103467925-32372b00-4d54-11eb-88d6-6d39c46854d8.png" /></a>
+</p>
+
+<p>Screenshots of all available themes are listed in <a href="https://github.com/nvim-lualine/lualine.nvim./THEMES.md" rel="noopener noreferrer">THEMES.md</a></p>
+
+<p>For those who want to break the norms, you can create custom looks for lualine.</p>
+
+<p><strong>Example</strong> :</p>
+
+<ul>
+<li>
+<a href="https://github.com/nvim-lualine/lualine.nvimexamples/evil_lualine.lua" rel="noopener noreferrer">evil_lualine</a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/13149513/113875129-4453ba00-97d8-11eb-8f21-94a9ef565db3.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F13149513%2F113875129-4453ba00-97d8-11eb-8f21-94a9ef565db3.png" /></a>
+</li>
+<li>
+<a href="https://github.com/nvim-lualine/lualine.nvimexamples/slanted-gaps.lua" rel="noopener noreferrer">slanted-gaps</a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/13149513/143395518-f6d6f748-c1ca-491b-9dab-246d0a8cf23f.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F13149513%2F143395518-f6d6f748-c1ca-491b-9dab-246d0a8cf23f.png" /></a>
+</li>
+<li>
+<a href="https://github.com/nvim-lualine/lualine.nvimexamples/bubbles.lua" rel="noopener noreferrer">bubbles</a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/20235646/131350468-fc556196-5f46-4bfe-a72e-960f6a58db2c.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F20235646%2F131350468-fc556196-5f46-4bfe-a72e-960f6a58db2c.png" /></a>
+</li>
+<li>
+<a href="https://github.com/nvim-lualine/lualine.nvimexamples/cosmicink.lua" rel="noopener noreferrer">cosmicink</a>
+<a rel="noopener noreferrer" href="https://private-user-images.githubusercontent.com/125453388/423959036-c8d3e4ba-4997-42e9-a1bb-d5e2a444bbfd.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDQ4MTIxMTAsIm5iZiI6MTc0NDgxMTgxMCwicGF0aCI6Ii8xMjU0NTMzODgvNDIzOTU5MDM2LWM4ZDNlNGJhLTQ5OTctNDJlOS1hMWJiLWQ1ZTJhNDQ0YmJmZC5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwNDE2JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDQxNlQxMzU2NTBaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0wOWU3Nzg3M2NhNzQ4YzY1ZjVhNTVmNDI2MDNkMTM3ODY5MzNiZjQ2NzYyMWE0MmFjYjk3ODkwYmI3ZmEzZWM0JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.nAz4h2CN1evpZdG8RPdAhGxdROfh57jmLX71a34_ZFE"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprivate-user-images.githubusercontent.com%2F125453388%2F423959036-c8d3e4ba-4997-42e9-a1bb-d5e2a444bbfd.png%3Fjwt%3DeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDQ4MTIxMTAsIm5iZiI6MTc0NDgxMTgxMCwicGF0aCI6Ii8xMjU0NTMzODgvNDIzOTU5MDM2LWM4ZDNlNGJhLTQ5OTctNDJlOS1hMWJiLWQ1ZTJhNDQ0YmJmZC5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwNDE2JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDQxNlQxMzU2NTBaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0wOWU3Nzg3M2NhNzQ4YzY1ZjVhNTVmNDI2MDNkMTM3ODY5MzNiZjQ2NzYyMWE0MmFjYjk3ODkwYmI3ZmEzZWM0JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.nAz4h2CN1evpZdG8RPdAhGxdROfh57jmLX71a34_ZFE" /></a>
+</li>
+</ul>
+
+<div class="markdown-heading">
+<h2 class="heading-element">Performance compared to other plugins</h2>
+
+</div>
+
+<p>Unlike other statusline plugins, lualine loads only the components you specify, and nothing else.</p>
+
+<p>Startup time performance measured with an amazing plugin <a href="https://github.com/dstein64/vim-startuptime" rel="noopener noreferrer">dstein64/vim-startuptime</a></p>
+
+<p>Times are measured with a…</p>
+</div>
+
+
+</div>
+<br />
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/nvim-lualine/lualine.nvim" rel="noopener noreferrer">View on GitHub</a></div>
+<br />
+</div>
+<br />
+
+
+<p>Minha escolha para plugin de statusline. Simples, rápido e bem flexível.</p>
+
+<h2>
+
+
+ #1
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/nvim-telescope" rel="noopener noreferrer">
+ nvim-telescope
+ </a> / <a href="https://github.com/nvim-telescope/telescope.nvim" rel="noopener noreferrer">
+ telescope.nvim
+ </a>
+ </h2>
+ <h3>
+ Find, Filter, Preview, Pick. All lua, all the time.
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">telescope.nvim</h1>
+</div>
+<p><a href="https://gitter.im/nvim-telescope/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/4768045434e6ee57bdd36b11fcf554e88d207acd01e0b78a2ef3c04b2f8a7d25/68747470733a2f2f6261646765732e6769747465722e696d2f6e76696d2d74656c6573636f70652f636f6d6d756e6974792e737667" alt="Gitter" /></a>
+<a href="https://luarocks.org/modules/Conni2461/telescope.nvim" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/4b597076c327f7a8f7005e116cdd17c5e936a7d2a8f99655b31f1c81468b6fc4/68747470733a2f2f696d672e736869656c64732e696f2f6c7561726f636b732f762f436f6e6e69323436312f74656c6573636f70652e6e76696d3f6c6f676f3d6c756126636f6c6f723d707572706c65" alt="LuaRocks" /></a></p>
+<p>Gaze deeply into unknown regions using the power of the moon.</p>
+<div class="markdown-heading">
+<h2 class="heading-element">What Is Telescope?</h2>
+</div>
+<p><code>telescope.nvim</code> is a highly extendable fuzzy finder over lists. Built on the
+latest awesome features from <code>neovim</code> core. Telescope is centered around
+modularity, allowing for easy customization.</p>
+<p>Community driven builtin <a href="https://github.com/nvim-telescope/telescope.nvim#pickers" rel="noopener noreferrer">pickers</a>, <a href="https://github.com/nvim-telescope/telescope.nvim#sorters" rel="noopener noreferrer">sorters</a> and
+<a href="https://github.com/nvim-telescope/telescope.nvim#previewers" rel="noopener noreferrer">previewers</a>.</p>
+<p><a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/5eb234defa4dcc0145ba0d8e327a03869f83af8ac0def9007828e4835dfecd32/68747470733a2f2f692e696d6775722e636f6d2f5454546a6136742e676966"><img src="https://camo.githubusercontent.com/5eb234defa4dcc0145ba0d8e327a03869f83af8ac0def9007828e4835dfecd32/68747470733a2f2f692e696d6775722e636f6d2f5454546a6136742e676966" alt="Preview" /></a>
+For more showcases of Telescope, please visit the <a href="https://github.com/nvim-telescope/telescope.nvim/wiki/Showcase" rel="noopener noreferrer">Showcase
+section</a> in the
+Telescope Wiki</p>
+<div class="markdown-heading">
+<h2 class="heading-element">Telescope Table of Contents</h2>
+</div>
+<ul>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#getting-started" rel="noopener noreferrer">Getting Started</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#usage" rel="noopener noreferrer">Usage</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#customization" rel="noopener noreferrer">Customization</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#default-mappings" rel="noopener noreferrer">Default Mappings</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#pickers" rel="noopener noreferrer">Pickers</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#previewers" rel="noopener noreferrer">Previewers</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#sorters" rel="noopener noreferrer">Sorters</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#layout-display" rel="noopener noreferrer">Layout</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#themes" rel="noopener noreferrer">Themes</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#vim-commands" rel="noopener noreferrer">Commands</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#autocmds" rel="noopener noreferrer">Autocmds</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#extensions" rel="noopener noreferrer">Extensions</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#api" rel="noopener noreferrer">API</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#media" rel="noopener noreferrer">Media</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#contributing" rel="noopener noreferrer">Contributing</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim/blob/master/doc/telescope_changelog.txt" rel="noopener noreferrer">Changelog</a></li>
+</ul>
+<div class="markdown-heading">
+<h2 class="heading-element">Getting Started</h2>
+
+</div>
+<p>This section should guide you to run your first builtin pickers.</p>
+<p><a href="https://github.com/neovim/neovim/releases/tag/v0.9.0" rel="noopener noreferrer">Neovim (v0.9.0)</a> or the
+latest neovim nightly commit is required for <code>telescope.nvim</code> to work.
+The neovim version also needs to be compiled with LuaJIT, we currently do not
+support Lua5.1 because of some ongoing issues.</p>
+<div class="markdown-heading">
+<h3 class="heading-element">Required dependencies</h3>
+
+</div>
+<ul>
+<li>
+<a href="https://github.com/nvim-lua/plenary.nvim" rel="noopener noreferrer">nvim-lua/plenary.nvim</a> is required.</li>
+</ul>
+<div class="markdown-heading">
+<h3 class="heading-element">Suggested dependencies</h3>
+
+</div>
+<ul>
+<li>
+<a href="https://github.com/BurntSushi/ripgrep" rel="noopener noreferrer">BurntSushi/ripgrep</a> is required for
+<code>live_grep</code> and <code>grep_string</code> and…</li>
+</ul>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/nvim-telescope/telescope.nvim" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Talvez o plugin mais essencial de todos hoje. Faça buscas por arquivo, texto e muito mais, numa velocidade absurda, com esse plugin fantástico.</p>
+
+<h2>
+
+
+ Conclusão
+</h2>
+
+<p>Com o ecossistema Lua se desenvolvendo cada dia mais, <a href="https://github.com/topics/neovim-plugin?l=lua" rel="noopener noreferrer">muitos plugins estão surgindo todos os dias</a> e com certeza essa lista provavelmente precisará ser atualizada em um futuro próximo.</p>
+
+<p><strong>Faltou algum plugin nessa lista? Me conta ae nos comentários!</strong></p>
+ 2022-05-13T17:56:41+00:00
+
+
+ PythonClub: Participe da Python Brasil 2021, a maior conferência de Python da América Latina
+ https://pythonclub.com.br/python-brasil-2021.html
+ <p><em>Python Brasil 2021, a maior conferência de Python da América Latina, acontecerá entre os dias 11 e 17 de outubro, reunindo pessoas desenvolvedoras, cientistas de dados e entusiastas da tecnologia.</em></p>
+<p><img alt="alt text" src="https://pythonclub.com.br/images/rafpyprog/pybr-banner.jpeg" title="Python Brasil 2021" /></p>
+<p>A <a href="https://2021.pythonbrasil.org.br/">Python Brasil 2021</a>, evento promovido pela comunidade brasileira de Python, traz nesta edição uma agenda imperdível para quem quer mergulhar no universo de uma das linguagens de programação mais utilizadas na atualidade.</p>
+<p>Uma das principais novidades deste ano é a trilha de atividades em espanhol, que vai trazer ao evento palestras de toda a América Latina, criando um ambiente de muita diversidade onde as comunidades Python de língua espanhola vão poder se conectar e trocar experiências.</p>
+<p>Em sua 17 edição, o evento será realizado de forma online, gratuito e aberto para qualquer pessoa de 11 a 17 de outubro. É esperado um público de mais de 10 mil participantes de todas as partes do Brasil e do mundo.
+Nos sete dias de imersão, os participantes poderão contribuir para projetos de software livre, participar de treinamentos e adquirir novos conhecimentos com outras pessoas da comunidade.</p>
+<p>E que tal falar sobre aquele projeto inovador, dar dicas sobre carreira ou mostrar formas de usar a tecnologia para causar impacto social, tudo isso em um ambiente inclusivo, seguro e acolhedor?! Até o dia 12 de agosto você poderá <a href="https://docs.google.com/forms/d/e/1FAIpQLSfXA7KGJbmoE6BHRgWAtK8LlBTEULv8QTS8ffHLIKUgeiLkZA/viewform">submeter sua proposta</a> para apresentação de palestras e tutoriais na maior conferência Python da América Latina.</p>
+<p>Você poderá acompanhar a Python Brasil pelo YouTube em tempo real e também participar de atividades e interagir com toda a comunidade pelo servidor do Discord criado especialmente para o evento.</p>
+<p>Não deixe de seguir o <a href="https://instagram.com/pythonbrasil?utm_medium=copy_link">perfil do evento no Instagram</a> ou <a href="https://twitter.com/pythonbrasil">Twitter</a> para ficar por dentro das últimas novidades!</p>
+ 2021-08-02T18:00:00+00:00
+ Rafael Alves RIbeiro
+
+
+ Filipe Saraiva: Ciclo de Entrevistas sobre as Pesquisas no PPGCC da UFPA – Inteligência Computacional
+ https://blog.filipesaraiva.info/?p=2250
+ <p><img /></p>
+<p>A <a href="http://computacao.ufpa.br/">Faculdade de Computação</a> e o <a href="https://ppgcc.propesp.ufpa.br/">Programa de Pós-Graduação em Ciência da Computação</a> da <a href="https://ufpa.br/">UFPA</a> estão desenvolvendo um projeto que pretende atingir dois objetivos: o primeiro, fazer uma melhor divulgação para o público externo à universidade do que produzimos em nossas pesquisas; o segundo, uma melhor divulgação INTERNA da mesma coisa – o que desenvolvemos em nossas pesquisas.</p>
+<p>Sim, <strong>INTERNA</strong> – não bastasse de fato a comunicação deficitária e pulverizada do que fazemos para os nossos próprios alunos, a pandemia só veio a piorar esse quadro. Após mais de um ano sem contato próximo com as turmas, com aulas completamente à distância e sem maiores interações extra-classe, os alunos em geral estão perdidos sobre as possibilidades de temas para TCCs e pesquisas que eles podem realizar conosco.</p>
+<p>Dessa forma as duas subunidades estão entrevistando todos os professores para que falem um pouco sobre os temas que trabalham, o histórico de pesquisa, o que vem sendo feito e, mais interessante, o que pode vir a ser feito. As entrevistas ocorrem no canal do YouTube <a href="https://www.youtube.com/c/ComputaçãoUFPA/">Computação UFPA</a> e depois são retrabalhadas para aparecerem no <a href="https://open.spotify.com/show/7oGkFKS4eV4WM2XjbEFeJu">FacompCast</a>.</p>
+<p>Feitas as devidas introduções, nesta terça dia 22/06 às 11h eu e o amigo prof. Jefferson Morais iremos falar um pouco sobre as pesquisas em Inteligência Computacional (ou seria Artificial?) desenvolvidas por nós. Será um bom apanhado sobre os trabalhos em 4 áreas que atuamos – aprendizado de máquina, metaheurísticas, sistemas fuzzy e sistemas multiagentes -, expondo projetos atuais e novos para os interessados.</p>
+<p>Nos vemos portanto logo mais na <a href="https://www.youtube.com/watch?v=VRytpiFWTQI">sala da entrevista</a>.</p>
+<p><strong>UPDATE:</strong></p>
+<p>A gravação já está disponível, segue abaixo:</p>
+<p></p>
+ 2021-06-21T21:51:57+00:00
+
+
+ PythonClub: Orientação a objetos de outra forma: Property
+ https://pythonclub.com.br/oo-de-outra-forma-6.html
+ <p>Seguindo com a série, chegou a hora de discutir sobre encapsulamento, ou seja, ocultar detalhes de implementação de uma classe do resto do código. Em algumas linguagens de programação isso é feito utilizando <code>protected</code> ou <code>private</code>, e às vezes o acesso aos atributos é feito através de funções <em>getters</em> e <em>setters</em>. Nesse texto vamos ver como o Python lida com essas questões.</p>
+<h2>Métodos protegidos e privados</h2>
+<p>Diferente de linguagens como Java e PHP que possuem palavras-chave como <code>protected</code> e <code>private</code> para impedir que outras classes acessem determinados métodos ou atributos, Python deixa tudo como público. Porém isso não significa que todas as funções de uma classe podem ser chamadas por outras, ou todos os atributos podem ser lidos e alterados sem cuidados.</p>
+<p>Para que quem estiver escrevendo um código saiba quais as funções ou atributos que não deveriam ser acessados diretamente, segue-se o padrão de começá-los com <code>_</code>, de forma similar aos arquivos ocultos em sistemas UNIX, que começam com <code>.</code>. Esse padrão já foi seguido na classe <code>AutenticavelComRegistro</code> da postagem sobre <a href="https://dev.to/acaverna/orientacao-a-objetos-de-outra-forma-heranca-multiplas-e-mixins-31eb">mixins</a>, onde a função que pega a data do sistema foi nomeada <code>_get_data</code>. Entretanto isso é apenas uma sugestão, nada impede dela ser chamada, como no exemplo a baixo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
+
+
+<span class="k">class</span> <span class="nc">Exemplo</span><span class="p">:</span>
+ <span class="k">def</span> <span class="nf">_get_data</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'</span><span class="si">%d</span><span class="s1">/%m/%Y %T'</span><span class="p">)</span>
+
+
+<span class="n">obj</span> <span class="o">=</span> <span class="n">Exemplo</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">_get_data</span><span class="p">())</span>
+</pre></div>
+
+
+<p>Porém algumas bibliotecas também utilizam o <code>_</code> para indicar outras informações como metadados do objeto, e que podem ser acessados sem muitos problemas. Assim é possível utilizar esse símbolo duas vezes (<code>__</code>) para indicar que realmente essa variável ou função não deveria ser acessada de fora da classe, apresentando erro de que o atributo não foi encontrado ao tentar executar a função, porém ela ainda pode ser acessada:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
+
+
+<span class="k">class</span> <span class="nc">Exemplo</span><span class="p">:</span>
+ <span class="k">def</span> <span class="nf">__get_data</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'</span><span class="si">%d</span><span class="s1">/%m/%Y %T'</span><span class="p">)</span>
+
+
+<span class="n">obj</span> <span class="o">=</span> <span class="n">Exemplo</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">__get_data</span><span class="p">())</span> <span class="c1"># AttributeError</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">_Exemplo__get_data</span><span class="p">())</span> <span class="c1"># Executa a função</span>
+</pre></div>
+
+
+<h2>Property</h2>
+<p>Os <em>getters</em> e <em>setters</em> muitas vezes são usados para impedir que determinadas variáveis sejam alteradas, ou validar o valor antes de atribuir a variável, ou ainda processar um valor a partir de outras variáveis. Porém como o Python incentiva o acesso direto as variáveis, existe a <em>property</em>, que ao tentar acessar uma variável ou alterar um valor, uma função é chamada. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+ <span class="nd">@property</span>
+ <span class="k">def</span> <span class="nf">nome</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_nome</span>
+
+ <span class="nd">@property</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.nome}</span><span class="s1"> </span><span class="si">{self.sobrenome}</span><span class="s1">'</span>
+
+ <span class="nd">@nome_completo</span><span class="o">.</span><span class="n">setter</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="n">valor</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">' '</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_nome</span> <span class="o">=</span> <span class="n">valor</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">valor</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
+
+ <span class="nd">@property</span>
+ <span class="k">def</span> <span class="nf">idade</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_idade</span>
+
+ <span class="nd">@idade</span><span class="o">.</span><span class="n">setter</span>
+ <span class="k">def</span> <span class="nf">idade</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">valor</span> <span class="o"><</span> <span class="mi">0</span><span class="p">:</span>
+ <span class="k">raise</span> <span class="ne">ValueError</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_idade</span> <span class="o">=</span> <span class="n">valor</span>
+
+ <span class="k">def</span> <span class="nf">fazer_aniversario</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">+=</span> <span class="mi">1</span>
+</pre></div>
+
+
+<p>Nesse código algumas variáveis são acessíveis através de <em>properties</em>, de forma geral, as variáveis foram definidas começando com <code>_</code> e com uma <em>property</em> de mesmo nome (sem o <code>_</code>). O primeiro caso é o <code>nome</code>, que possui apenas o <em>getter</em>, sendo possível o seu acesso como <code>obj.nome</code>, porém ao tentar atribuir um valor, será lançado um erro (<code>AttributeError: can't set attribute</code>). Em relação ao <code>sobrenome</code>, como não é necessário nenhum tratamento especial, não foi utilizado um <em>property</em>, porém futuramente pode ser facilmente substituído por um sem precisar alterar os demais códigos. Porém a função <code>nome_completo</code> foi substituída por um <em>property</em>, permitindo tanto o acesso ao nome completo da pessoa, como se fosse uma variável, quanto trocar <code>nome</code> e <code>sobrenome</code> ao atribuir um novo valor para essa <em>property</em>. Quanto a <code>idade</code> utiliza o <em>setter</em> do <em>property</em> para validar o valor recebido, retornando erro para idades inválidas (negativas).</p>
+<p>Vale observar também que todas as funções de <em>getter</em> não recebem nenhum argumento (além do <code>self</code>), enquanto as funções de <em>setter</em> recebem o valor atribuído à variável.</p>
+<p>Utilizando a <a href="https://dev.to/acaverna/orientacao-a-objetos-de-outra-forma-abc-89b">ABC</a>, ainda é possível informar que alguma classe filha deverá implementar alguma <em>property</em>. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span>
+
+
+<span class="k">class</span> <span class="nc">Pessoa</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+ <span class="nd">@property</span>
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="k">class</span> <span class="nc">Brasileiro</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="nd">@property</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.nome}</span><span class="s1"> </span><span class="si">{self.sobrenome}</span><span class="s1">'</span>
+
+
+<span class="k">class</span> <span class="nc">Japones</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="nd">@property</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.sobrenome}</span><span class="s1"> </span><span class="si">{self.nome}</span><span class="s1">'</span>
+</pre></div>
+
+
+<h2>Considerações</h2>
+<p>Diferente de algumas linguagens que ocultam as variáveis dos objetos, permitindo o seu acesso apenas através de funções, Python seguem no sentido contrário, acessando as funções de <em>getter</em> e <em>setter</em> como se fossem variáveis, isso permite começar com uma classe simples e ir adicionando funcionalidades conforme elas forem necessárias, sem precisar mudar o código das demais partes da aplicação, além de deixar transparente para quem desenvolve, não sendo necessário lembrar se precisa usar <em>getteres</em> e <em>setteres</em> ou não.</p>
+<p>De forma geral, programação orientada a objetos consiste em seguir determinados padrões de código, e as linguagens que implementam esse paradigma oferecem facilidades para escrever código seguindo esses padrões, e às vezes até ocultando detalhes complexos de suas implementações. Nesse contexto, eu recomendo a palestra do autor do <a href="https://htop.dev/">htop</a> feita no FISL 16, onde ele comenta como usou <a href="http://hemingway.softwarelivre.org/fisl16/high/41f/sala_41f-high-201507091200.ogv">orientação a objetos em C</a>. E para quem ainda quiser se aprofundar no assunto de orientação a objetos no Python, recomendo os vídeos do <a href="https://www.youtube.com/playlist?list=PLOQgLBuj2-3L_L6ahsBVA_SzuGtKre3OK">Eduardo Mendes</a> (também conhecido como dunossauro).</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ 2021-05-17T21:00:00+00:00
+ Eduardo Klosowski
+
+
+ Blog do PauloHRPinheiro: Iniciando com o ORM Pony no Python III - Erros e Exceções
+ https://paulohrpinheiro.xyz/texts/python/2021-05-16-iniciando-com-o-orm-pony-no-python-iii-erros-e-excecoes.html
+ Seguindo o trilha com o ORM Pony, neste terceiro texto, agora começaremos a gerar situações de erro e ver o que os objetos retornam ou que exceções são geradas.
+ 2021-05-16T00:00:00+00:00
+
+
+ PythonClub: Orientação a objetos de outra forma: ABC
+ https://pythonclub.com.br/oo-de-outra-forma-5.html
+ <p>Na discussão sobre <a href="https://dev.to/acaverna/orientacao-a-objetos-de-outra-forma-heranca-multiplas-e-mixins-31eb">herança e mixins</a> foram criadas várias classes, como <code>Autenticavel</code> e <code>AutenticavelComRegistro</code> que adicionam funcionalidades a outras classes e implementavam tudo o que precisavam para seu funcionamento. Entretanto podem existir casos em que não seja possível implementar todas as funções na própria classe, deixando com que as classes que a estende implemente essas funções. Uma forma de fazer isso é través das <a href="https://docs.python.org/pt-br/3/library/abc.html">ABC</a> (<em>abstract base classes</em>, ou classes base abstratas).</p>
+<h2>Sem uso de classes base abstratas</h2>
+<p>Um exemplo de classe que não é possível implementar todas as funcionalidades foi dada no texto <a href="https://dev.to/acaverna/encapsulamento-da-logica-do-algoritmo-298e">Encapsulamento da lógica do algoritmo</a>, que discutia a leitura de valores do teclado até que um valor válido fosse lido (ou que repete a leitura caso um valor inválido tivesse sido informado). Nesse caso a classe <code>ValidaInput</code> implementava a lógica base de funcionamento, porém eram suas classes filhas (<code>ValidaNomeInput</code> e <code>ValidaNotaInput</code>) que implementavam as funções para tratar o que foi lido do teclado e verificar se é um valor válido ou não.</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ValidaInput</span><span class="p">:</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Valor inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">raise</span> <span class="ne">NotImplementedError</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">raise</span> <span class="ne">NotImplementedError</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">valor</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNomeInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nome inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">entrada</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">title</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">valor</span> <span class="o">!=</span> <span class="s1">''</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNotaInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nota inválida!'</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">entrada</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">valor</span> <span class="o"><=</span> <span class="mi">10</span>
+</pre></div>
+
+
+<p>Entretanto, esse código permite a criação de objetos da classe <code>ValidaInput</code> mesmo sem ter uma implementação das funções <code>transformar_entrada</code> e <code>validar_valor</code>. E a única mensagem de erro ocorreria ao tentar executar essas funções, o que poderia estar longe do problema real, que é a criação de um objeto a partir de uma classe que não prove todas as implementações das suas funções, o que seria semelhante a uma classe abstrata em outras linguagens.</p>
+<div class="highlight"><pre><span></span><span class="n">obj</span> <span class="o">=</span> <span class="n">ValidaInput</span><span class="p">()</span>
+
+<span class="c1"># Diversas linhas de código</span>
+
+<span class="n">obj</span><span class="p">(</span><span class="s1">'Entrada: '</span><span class="p">)</span> <span class="c1"># Exceção NotImplementedError lançada</span>
+</pre></div>
+
+
+<h2>Com uso de classes base abstratas</h2>
+<p>Seguindo a documentação da <a href="https://docs.python.org/pt-br/3/library/abc.html">ABC</a>, para utilizá-las é necessário informar a metaclasse <code>ABCMeta</code> na criação da classe, ou simplesmente estender a classe <code>ABC</code>, e decorar com <code>abstractmethod</code> as funções que as classes que a estenderem deverão implementar. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaInput</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Valor inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="o">...</span>
+
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="o">...</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">valor</span>
+</pre></div>
+
+
+<p>Desta forma, ocorrerá um erro já ao tentar criar um objeto do tipo <code>ValidaInput</code>, dizendo quais são as funções que precisam ser implementadas. Porém funcionará normalmente ao criar objetos a partir das classes <code>ValidaNomeInput</code> e <code>ValidaNotaInput</code> visto que elas implementam essas funções.</p>
+<div class="highlight"><pre><span></span><span class="n">obj</span> <span class="o">=</span> <span class="n">ValidaInput</span><span class="p">()</span> <span class="c1"># Exceção TypeError lançada</span>
+
+<span class="n">nome_input</span> <span class="o">=</span> <span class="n">ValidaNomeInput</span><span class="p">()</span> <span class="c1"># Objeto criado</span>
+<span class="n">nota_input</span> <span class="o">=</span> <span class="n">ValidaNotaInput</span><span class="p">()</span> <span class="c1"># Objeto criado</span>
+</pre></div>
+
+
+<p>Como essas funções não utilizam a referência ao objeto (<code>self</code>), ainda é possível decorar as funções com <code>staticmethod</code>, como:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaInput</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Valor inválido!'</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="n">entrada</span><span class="p">):</span>
+ <span class="o">...</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="o">...</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">valor</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNomeInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nome inválido!'</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">entrada</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">title</span><span class="p">()</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">valor</span> <span class="o">!=</span> <span class="s1">''</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNotaInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nota inválida!'</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">entrada</span><span class="p">)</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">valor</span> <span class="o"><=</span> <span class="mi">10</span>
+</pre></div>
+
+
+<p>Isso também seria válido para funções decoradas com <code>classmethod</code>, que receberiam a referência a classe (<code>cls</code>).</p>
+<h2>Considerações</h2>
+<p>Não é necessário utilizar ABC para fazer o exemplo discutido, porém ao utilizar essa biblioteca ficou mais explícito quais as funções que precisavam ser implementados nas classes filhas, ainda mais que sem utilizar ABC a classe base poderia nem ter as funções, com:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ValidaInput</span><span class="p">:</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Valor inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">valor</span>
+</pre></div>
+
+
+<p>Como Python possui <a href="https://docs.python.org/pt-br/3/glossary.html#term-duck-typing">duck-typing</a>, não é necessário uma grande preocupação com os tipos, como definir e utilizar interfaces presentes em outras implementações de orientação a objetos, porém devido à herança múltipla, ABC pode ser utilizada como interface que não existe em Python, fazendo com que as classes implementem determinadas funções. Para mais a respeito desse assunto, recomendo as duas lives do dunossauro sobre ABC (<a href="https://www.youtube.com/watch?v=yLHV1__nZZw">1</a> e <a href="https://www.youtube.com/watch?v=erAXvsuihPQ">2</a>), e a apresentação do Luciano Ramalho sobre <a href="https://www.youtube.com/watch?v=AJK2LqrlnTE">type hints</a>.</p>
+<p>Uma classe filha também não é obrigada a implementar todas as funções decoradas com <code>abstractmethod</code>, mas assim como a classe pai, não será possível criar objetos a partir dessa classe, apenas de uma classe filha dela que implemente as demais funções. Como se ao aplicar um <code>abstractmethod</code> tornasse a classe abstrata, e qualquer classe filha só deixasse de ser abstrata quando a última função decorada com <code>abstractmethod</code> for sobrescrita. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
+
+
+<span class="k">class</span> <span class="nc">A</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">func1</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="o">...</span>
+
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">func2</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="k">class</span> <span class="nc">B</span><span class="p">(</span><span class="n">A</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">func1</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'1'</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">C</span><span class="p">(</span><span class="n">B</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">func2</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'2'</span><span class="p">)</span>
+
+
+<span class="n">a</span> <span class="o">=</span> <span class="n">A</span><span class="p">()</span> <span class="c1"># Erro por não implementar func1 e func2</span>
+<span class="n">b</span> <span class="o">=</span> <span class="n">B</span><span class="p">()</span> <span class="c1"># Erro por não implementar func2</span>
+<span class="n">c</span> <span class="o">=</span> <span class="n">C</span><span class="p">()</span> <span class="c1"># Objeto criado</span>
+</pre></div>
+
+
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ 2021-05-10T15:00:00+00:00
+ Eduardo Klosowski
+
+
+ PythonClub: Orientação a objetos de outra forma: Herança múltiplas e mixins
+ https://pythonclub.com.br/oo-de-outra-forma-4.html
+ <p>No <a href="https://dev.to/acaverna/orientacao-a-objetos-de-outra-forma-heranca-3dm7">texto anterior</a> foi apresentando o conceito de herança, que herda toda a estrutura e comportamento de uma classe, podendo estendê-la com outros atributos e comportamentos. Esse texto apresentará a ideia de <a href="https://pt.wikipedia.org/wiki/Heran%C3%A7a_m%C3%BAltipla">herança múltipla</a>, e uma forma para se aproveitar esse recurso, através de mixins.</p>
+<h2>Herança múltiplas</h2>
+<p>Voltando ao sistema para lidar com dados das pessoas, onde algumas dessas pessoas possuem a possibilidade de acessar o sistema através de usuário e senha, também deseja-se permitir que outros sistemas autentiquem e tenham acesso os dados através de uma <a href="https://pt.wikipedia.org/wiki/Interface_de_programa%C3%A7%C3%A3o_de_aplica%C3%A7%C3%B5es">API</a>. Isso pode ser feito criando uma classe para representar os sistemas que terão permissão para acessar os dados. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Sistema</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">=</span> <span class="n">senha</span>
+
+ <span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">==</span> <span class="n">usuario</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">==</span> <span class="n">senha</span>
+</pre></div>
+
+
+<p>Porém, esse código repete a implementação feita para <code>PessoaAutenticavel</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">PessoaAutenticavel</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">=</span> <span class="n">senha</span>
+
+ <span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">==</span> <span class="n">usuario</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">==</span> <span class="n">senha</span>
+</pre></div>
+
+
+<p>Aproveitando que Python, diferente de outras linguagens, possui herança múltipla, é possível extrair essa lógica das classes, centralizando a implementação em uma outra classe e simplesmente herdá-la. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Autenticavel</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">=</span> <span class="n">senha</span>
+
+ <span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">==</span> <span class="n">usuario</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">==</span> <span class="n">senha</span>
+
+
+<span class="k">class</span> <span class="nc">PessoaAutenticavel</span><span class="p">(</span><span class="n">Autenticavel</span><span class="p">,</span> <span class="n">Pessoa</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="k">class</span> <span class="nc">Sistema</span><span class="p">(</span><span class="n">Autenticavel</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="n">p</span> <span class="o">=</span> <span class="n">PessoaAutenticavel</span><span class="p">(</span><span class="n">nome</span><span class="o">=</span><span class="s1">'João'</span><span class="p">,</span> <span class="n">sobrenome</span><span class="o">=</span><span class="s1">'da Silva'</span><span class="p">,</span> <span class="n">idade</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span>
+ <span class="n">usuario</span><span class="o">=</span><span class="s1">'joao'</span><span class="p">,</span> <span class="n">senha</span><span class="o">=</span><span class="s1">'secreta'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>A primeira coisa a ser observada são os argumentos <code>*args</code> e <code>**kwargs</code> no <code>__init__</code> da classe <code>Autenticavel</code>, eles são usados uma vez que não se sabe todos os argumentos que o <code>__init__</code> da classe que estenderá o <code>Autenticavel</code> espera receber, funcionando de forma dinâmica (mais sobre esse recurso pode ser visto na <a href="https://docs.python.org/pt-br/3/tutorial/controlflow.html#more-on-defining-functions">documentação do Python</a>).</p>
+<p>A segunda coisa a ser verificada é que para a classe <code>PessoaAutenticavel</code>, agora cria em seus objetos, a estrutura tanto da classe <code>Pessoa</code>, quanto <code>Autenticavel</code>. Algo similar a versão sem orientação a objetos a baixo:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa_autenticavel.py</span>
+
+<span class="kn">import</span> <span class="nn">autenticavel</span>
+<span class="kn">import</span> <span class="nn">pessoa</span>
+
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="n">autenticavel</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Também vale observar que as classes <code>PessoaAutenticavel</code> e <code>Sistema</code> não precisam definir nenhuma função, uma vez que elas cumprem seus papéis apenas herdando outras classes, porém seria possível implementar funções específicas dessas classes, assim como sobrescrever as funções definidas por outras classes.</p>
+<h2>Ordem de resolução de métodos</h2>
+<p>Embora herança múltiplas sejam interessantes, existe um problema, se ambas as classes pai possuírem uma função com um mesmo nome, a classe filha deveria chamar qual das funções? A do primeiro pai? A do último? Para lidar com esse problema o Python usa o MRO (<em>method resolution order</em>, ordem de resolução do método), que consiste em uma tupla com a ordem de qual classe o Python usará para encontrar o método a ser chamado. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="n">PessoaAutenticavel</span><span class="o">.</span><span class="vm">__mro__</span><span class="p">)</span>
+<span class="c1"># (<class '__main__.PessoaAutenticavel'>, <class '__main__.Autenticavel'>, <class '__main__.Pessoa'>, <class 'object'>)</span>
+</pre></div>
+
+
+<p>Por esse motivo que também foi possível chamar o <code>super().__init__</code> dentro de <code>Autenticavel</code>, que devido ao MRO, o Python chama o <code>__init__</code> da outra classe pai da classe que estendeu <code>Autenticavel</code>, em vez de precisar fazer um método <code>__init__</code> em <code>PessoaAutenticavel</code> chamando o <code>__init__</code> de todas as suas classes pais, como foi feito na versão sem orientação a objetos. E por isso a ordem <code>Autenticavel</code> e <code>Pessoa</code> na herança de <code>PessoaAutenticavel</code>, para fazer o MRO procurar os métodos primeiro em <code>Autenticavel</code> e depois em <code>Pessoa</code>.</p>
+<p>Para tentar fugir da complexidade que pode ser herança múltipla, é possível escrever classes que tem por objetivo unicamente incluir alguma funcionalidade em outra, como o caso da classe <code>Autenticavel</code>, que pode ser herdada por qualquer outra classe do sistema para permitir o acesso ao sistema. Essas classes recebem o nome de mixins, e adiciona uma funcionalidade bem definida.</p>
+<h2>Estendendo mixins</h2>
+<p>Imagine se além de permitir o acesso ao sistema, também gostaríamos de registrar algumas tentativas de acesso, informando quando houve a tentativa e se o acesso foi concedido ou não. Como <code>Autenticavel</code> é uma classe, é possível extendê-la para implementar essa funcionalidade na função <code>autenticar</code>. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
+
+
+<span class="k">class</span> <span class="nc">AutenticavelComRegistro</span><span class="p">(</span><span class="n">Autenticavel</span><span class="p">):</span>
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">_get_data</span><span class="p">():</span>
+ <span class="k">return</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'</span><span class="si">%d</span><span class="s1">/%m/%Y %T'</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'{self._get_data()} Tentativa de acesso de </span><span class="si">{usuario}</span><span class="s1">'</span><span class="p">)</span>
+ <span class="n">acesso</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">acesso</span><span class="p">:</span>
+ <span class="n">acesso_str</span> <span class="o">=</span> <span class="s1">'permitido'</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="n">acesso_str</span> <span class="o">=</span> <span class="s1">'negado'</span>
+ <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'{self._get_data()} Acesso de </span><span class="si">{usuario}</span><span class="s1"> </span><span class="si">{acesso_str}</span><span class="s1">'</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">acesso</span>
+
+
+<span class="k">class</span> <span class="nc">PessoaAutenticavelComRegistro</span><span class="p">(</span><span class="n">AutenticavelComRegistro</span><span class="p">,</span> <span class="n">Pessoa</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="k">class</span> <span class="nc">SistemaAutenticavelComRegistro</span><span class="p">(</span><span class="n">AutenticavelComRegistro</span><span class="p">,</span> <span class="n">Sistema</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="n">p</span> <span class="o">=</span> <span class="n">PessoaAutenticavelComRegistro</span><span class="p">(</span>
+ <span class="n">nome</span><span class="o">=</span><span class="s1">'João'</span><span class="p">,</span> <span class="n">sobrenome</span><span class="o">=</span><span class="s1">'da Silva'</span><span class="p">,</span> <span class="n">idade</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span>
+ <span class="n">usuario</span><span class="o">=</span><span class="s1">'joao'</span><span class="p">,</span> <span class="n">senha</span><span class="o">=</span><span class="s1">'secreta'</span><span class="p">,</span>
+<span class="p">)</span>
+<span class="n">p</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">)</span>
+<span class="c1"># Saída na tela:</span>
+<span class="c1"># 23/04/2021 16:56:58 Tentativa de acesso de joao</span>
+<span class="c1"># 23/04/2021 16:56:58 Acesso de joao permitido</span>
+</pre></div>
+
+
+<p>Essa implementação utiliza-se do <code>super()</code> para acessar a função <code>autenticar</code> da classe <code>Autenticavel</code> para não precisar reimplementar a autenticação. Porém, antes de chamá-la, manipula seus argumentos para registrar quem tentou acessar o sistema, assim como também manipula o seu retorno para registrar se o acesso foi permitido ou não.</p>
+<p>Essa classe também permite analisar melhor a ordem em que as classes são consultadas quando uma função é chamada:</p>
+<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="n">PessoaAutenticavelComRegistro</span><span class="o">.</span><span class="vm">__mro__</span><span class="p">)</span>
+<span class="c1"># (<class '__main__.PessoaAutenticavelComRegistro'>, <class '__main__.AutenticavelComRegistro'>, <class '__main__.Autenticavel'>, <class '__main__.Pessoa'>, <class 'object'>)</span>
+</pre></div>
+
+
+<p>Que também pode ser visto na forma de um digrama de classes:</p>
+<p><img alt="Diagrama de classes" src="https://pythonclub.com.br/feeds/images/eduardoklosowski/oo-de-outra-forma-4/mro.png" /></p>
+<p>Onde é feito uma <a href="https://pt.wikipedia.org/wiki/Busca_em_profundidade">busca em profundidade</a>, como se a função fosse chamada no primeiro pai, e só se ela não for encontrada, busca-se no segundo pai e assim por diante. Também é possível observar a classe <code>object</code>, que sempre será a última classe, e é a classe pai de todas as outras classes do Python quando elas não possuirem um pai declarado explicitamente.</p>
+<h2>Considerações</h2>
+<p>Herança múltipla pode dificultar bastante o entendimento do código, principalmente para encontrar onde determinada função está definida, porém pode facilitar bastante o código. Um exemplo que usa bastante herança e mixins são as <em>views</em> baseadas em classe do django (<a href="https://docs.djangoproject.com/pt-br/3.2/topics/class-based-views/"><em>class-based views</em></a>), porém para facilitar a visualização existe o site <a href="https://ccbv.co.uk/">Classy Class-Based Views</a> que lista todas as classes, e os mixins utilizados em cada uma, como pode ser visto em "Ancestors" como na <a href="https://ccbv.co.uk/projects/Django/3.1/django.views.generic.edit/UpdateView/">UpdateView</a>, que é usado para criar uma página com formulário para editar um registro já existente no banco, assim ela usa mixins para pegar um objeto do banco (<code>SingleObjectMixin</code>), processar formulário baseado em uma tabela do banco (<code>ModelFormMixin</code>) e algumas outras funcionalidades necessárias para implementar essa página.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ 2021-05-03T18:00:00+00:00
+ Eduardo Klosowski
+
+
+ PythonClub: Orientação a objetos de outra forma: Herança
+ https://pythonclub.com.br/oo-de-outra-forma-3.html
+ <p>Algo que ajuda no desenvolvimento é a reutilização de código. Em orientação a objetos, essa reutilização pode ocorrer através de herança, onde um objeto pode se comportar como um objeto da sua própria classe, como também da classe que herdou.</p>
+<h2>Adicionando funcionalidades</h2>
+<p>Uma das utilidades da herança é estender uma classe para adicionar funcionalidades. Pensando no contexto das postagens anteriores, poderíamos querer criar um usuário e senha para algumas pessoas poderem acessar o sistema. Isso poderia ser feito adicionando atributos usuário e senha para as pessoas, além de uma função para validar se os dados estão corretos, e assim permitir o acesso ao sistema. Porém isso não pode ser feito para todas as pessoas, e sim apenas para aqueles que possuem permissão de acesso.</p>
+<h3>Sem orientação a objetos</h3>
+<p>Voltando a solução com dicionários (sem utilizar orientação a objetos), isso consistiria em criar um dicionário com a estrutura de uma pessoa, e em seguida estender essa estrutura com os novos campos de usuário e senha nesse mesmo dicionário, algo como:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa.py</span>
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'nome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'sobrenome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'idade'</span><span class="p">]</span> <span class="o">=</span> <span class="n">idade</span>
+
+
+<span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="n">pessoa</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{pessoa['nome']}</span><span class="s2"> </span><span class="si">{pessoa['sobrenome']}</span><span class="s2">"</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa_autenticavel.py</span>
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'usuario'</span><span class="p">]</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'senha'</span><span class="p">]</span> <span class="o">=</span> <span class="n">senha</span>
+
+
+<span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">pessoa</span><span class="p">[</span><span class="s1">'usuario'</span><span class="p">]</span> <span class="o">==</span> <span class="n">usuario</span> <span class="ow">and</span> <span class="n">pessoa</span><span class="p">[</span><span class="s1">'senha'</span><span class="p">]</span> <span class="o">==</span> <span class="n">senha</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+<span class="kn">import</span> <span class="nn">pessoa_autenticavel</span>
+
+<span class="n">p</span> <span class="o">=</span> <span class="p">{}</span>
+<span class="n">pessoa</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">pessoa_autenticavel</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">pessoa</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">(</span><span class="n">p</span><span class="p">))</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">pessoa_autenticavel</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">))</span>
+</pre></div>
+
+
+<p>Porém nessa solução é possível que o programador esqueça de chamar as duas funções <code>init</code> diferentes, e como queremos que todo dicionário com a estrutura de <code>pessoa_autenticavel</code> contenha também a estrutura de <code>pessoa</code>, podemos chamar o <code>init</code> de pessoa dentro do <code>init</code> de <code>pessoa_autenticavel</code>:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa_autenticavel.py</span>
+
+<span class="kn">import</span> <span class="nn">pessoa</span>
+
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="n">p</span><span class="p">[</span><span class="s1">'usuario'</span><span class="p">]</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="n">p</span><span class="p">[</span><span class="s1">'senha'</span><span class="p">]</span> <span class="o">=</span> <span class="n">senha</span>
+
+
+<span class="o">...</span> <span class="c1"># Demais funções</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+<span class="kn">import</span> <span class="nn">pessoa_autenticavel</span>
+
+<span class="n">p</span> <span class="o">=</span> <span class="p">{}</span>
+<span class="n">pessoa_autenticavel</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">pessoa</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">(</span><span class="n">p</span><span class="p">))</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">pessoa_autenticavel</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">))</span>
+</pre></div>
+
+
+<p>Nesse caso foi necessário alterar o nome do argumento <code>pessoa</code> da função <code>pessoa_autenticavel.init</code> para não conflitar com o outro módulo importado com esse mesmo nome. Porém ao chamar um <code>init</code> dentro de outro, temos a garantia de que o dicionário será compatível tanto com a estrutura pedida para ser criada pelo programador, quanto pelas estruturas pais dela.</p>
+<h3>Com orientação a objetos</h3>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.nome}</span><span class="s1"> </span><span class="si">{self.sobrenome}</span><span class="s1">'</span>
+
+
+<span class="k">class</span> <span class="nc">PessoaAutenticavel</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="n">Pessoa</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">=</span> <span class="n">senha</span>
+
+ <span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">==</span> <span class="n">usuario</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">==</span> <span class="n">senha</span>
+
+
+<span class="n">p</span> <span class="o">=</span> <span class="n">PessoaAutenticavel</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">Pessoa</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">(</span><span class="n">p</span><span class="p">))</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">PessoaAutenticavel</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">))</span>
+</pre></div>
+
+
+<p>A principal novidade desse exemplo é que ao declarar a classe <code>PessoaAutenticavel</code> (filha), foi declarado a classe <code>Pessoa</code> (pai) entre parênteses, isso faz o interpretador Python criar uma cópia dessa classe estendendo-a com as novas funções que estamos criando. Porém pode ser um pouco redundante chamar <code>Pessoa.__init__</code> dentro da função <code>__init__</code> sendo que já foi declarado que ela estende <code>Pessoa</code>, podendo ser trocado por <code>super()</code>, que aponta para a classe que foi estendida. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">PessoaAutenticavel</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">=</span> <span class="n">senha</span>
+
+ <span class="o">...</span> <span class="c1"># Demais funções</span>
+</pre></div>
+
+
+<p>Assim se evita repetir o nome da classe, e já passa automaticamente a referência para <code>self</code>, assim como quando usamos o açúcar sintático apresentado na primeira postagem dessa série. E esse açúcar sintática também pode ser usado para chamar tanto as funções declaradas em <code>Pessoa</code> quanto em <code>PessoaAutenticavel</code>. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">p</span> <span class="o">=</span> <span class="n">PessoaAutenticavel</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">())</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">))</span>
+</pre></div>
+
+
+<p>Esse método também facilita a utilização das funções, uma vez que não é necessário lembrar em qual classe que cada função foi declarada. Na verdade, como <code>PessoaAutenticavel</code> estende <code>Pessoa</code>, seria possível executar também <code>PessoaAutenticavel.nome_completo</code>, porém eles apontam para a mesma função.</p>
+<h2>Sobrescrevendo uma função</h2>
+<p>A classe <code>Pessoa</code> possui a função <code>nome_completo</code> que retorna uma <code>str</code> contento nome e sobrenome. Porém no Japão, assim como em outros países asiáticos, o sobrenome vem primeiro, e até <a href="https://noticias.uol.com.br/ultimas-noticias/efe/2019/06/24/japao-quer-voltar-a-ordem-tradicional-dos-nomes-abe-shinzo-nao-shinzo-abe.htm">estão pedindo para seguir a tradição deles ao falarem os nomes de japoneses</a>, como o caso do primeiro-ministro, mudando de Shinzo Abe para Abe Shinzo.</p>
+<h3>Com orientação a objetos</h3>
+<p>Isso também pode ser feito no sistema usando herança, porém em vez de criar uma nova função com outro nome, é possível criar uma função com o mesmo nome, sobrescrevendo a anterior, porém apenas para os objetos da classe filha. Algo semelhante ao que já foi feito com a função <code>__init__</code>. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Japones</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.sobrenome}</span><span class="s1"> </span><span class="si">{self.nome}</span><span class="s1">'</span>
+
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">p2</span> <span class="o">=</span> <span class="n">Japones</span><span class="p">(</span><span class="s1">'Shinzo'</span><span class="p">,</span> <span class="s1">'Abe'</span><span class="p">,</span> <span class="mi">66</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">())</span> <span class="c1"># João da Silva</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p2</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">())</span> <span class="c1"># Abe Shinzo</span>
+</pre></div>
+
+
+<p>Essa relação de herança traz algo interessante, todo objeto da classe <code>Japones</code> se comporta como um objeto da classe <code>Pessoa</code>, porém a relação inversa não é verdade. Assim como podemos dizer que todo japonês é uma pessoa, mas nem todas as pessoas são japonesas. Ser japonês é um caso mais específico de pessoa, assim como as demais nacionalidades.</p>
+<h3>Sem orientação a objetos</h3>
+<p>Esse comportamento de sobrescrever a função <code>nome_completo</code> não é tão simples de replicar em uma estrutura de dicionário, porém é possível fazer. Porém como uma pessoa pode ser tanto japonês quanto não ser, não é possível saber de antemão para escrever no código <code>pessoa.nome_completo</code> ou <code>japones.nome_completo</code>, que diferente do exemplo da autenticação, agora são duas funções diferentes, isso precisa ser descoberto dinamicamente quando se precisar chamar a função.</p>
+<p>Uma forma de fazer isso é guardar uma referência para a função que deve ser chamada dentro da própria estrutura. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa.py</span>
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'nome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'sobrenome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'idade'</span><span class="p">]</span> <span class="o">=</span> <span class="n">idade</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'nome_completo'</span><span class="p">]</span> <span class="o">=</span> <span class="n">nome_completo</span>
+
+
+<span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="n">pessoa</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{pessoa['nome']}</span><span class="s2"> </span><span class="si">{pessoa['sobrenome']}</span><span class="s2">"</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: japones.py</span>
+
+<span class="kn">import</span> <span class="nn">pessoa</span>
+
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">japones</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">(</span><span class="n">japones</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="n">japones</span><span class="p">[</span><span class="s1">'nome_completo'</span><span class="p">]</span> <span class="o">=</span> <span class="n">nome_completo</span>
+
+
+<span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="n">japones</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{pessoa['sobrenome']}</span><span class="s2"> </span><span class="si">{pessoa['nome']}</span><span class="s2">"</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+<span class="kn">import</span> <span class="nn">japones</span>
+
+<span class="n">p1</span> <span class="o">=</span> <span class="p">{}</span>
+<span class="n">pessoa</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">p2</span> <span class="o">=</span> <span class="p">{}</span>
+<span class="n">japones</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p2</span><span class="p">,</span> <span class="s1">'Shinzo'</span><span class="p">,</span> <span class="s1">'Abe'</span><span class="p">,</span> <span class="mi">66</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="p">[</span><span class="s1">'nome_completo'</span><span class="p">](</span><span class="n">p1</span><span class="p">))</span> <span class="c1"># João da Silva</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p2</span><span class="p">[</span><span class="s1">'nome_completo'</span><span class="p">](</span><span class="n">p2</span><span class="p">))</span> <span class="c1"># Abe Shinzo</span>
+</pre></div>
+
+
+<p>Perceba que a forma de chamar a função foi alterada. O que acontece na prática é que toda função que pode ser sobrescrita não é chamada diretamente, e sim a partir de uma referência, e isso gera um custo computacional adicional. Como esse custo não é tão alto (muitas vezes sendo quase irrelevante), esse é o comportamento adotado em várias linguagens, porém em C++, por exemplo, existe a palavra-chave <code>virtual</code> para descrever quando uma função pode ser sobrescrita ou não.</p>
+<h2>Considerações</h2>
+<p>Herança é um mecanismo interessante para ser explorado com o objetivo de reaproveitar código e evitar repeti-lo. Porém isso pode vir com alguns custos, seja computacional durante sua execução, seja durante a leitura do código, sendo necessário verificar diversas classes para saber o que de fato está sendo executado, porém isso também pode ser usado para ocultar e abstrair lógicas mais complicadas, como eu já comentei em outra <a href="https://dev.to/acaverna/encapsulamento-da-logica-do-algoritmo-298e">postagem</a>.</p>
+<p>Herança também permite trabalhar com generalização e especialização, podendo descrever o comportamento mais geral, ou mais específico. Ou simplesmente só adicionar mais funcionalidades a uma classe já existente.</p>
+<p>Assim como foi utilizado o <code>super()</code> para chamar a função <code>__init__</code> da classe pai, é possível utilizá-lo para chamar qualquer outra função. Isso permite, por exemplo, tratar os argumentos da função, aplicando modificações antes de chamar a função original, ou seu retorno, executando algum processamento em cima do retorno dela, não precisando rescrever toda a função.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ 2021-04-26T20:00:00+00:00
+ Eduardo Klosowski
+
+
+ PythonClub: Orientação a objetos de outra forma: Métodos estáticos e de classes
+ https://pythonclub.com.br/oo-de-outra-forma-2.html
+ <p>Na <a href="https://dev.to/acaverna/orientacao-a-objetos-de-outra-forma-classes-e-objetos-3mfd">postagem anterior</a> foi apresentado o <code>self</code>, nessa postagem será discutido mais a respeito desse argumento, considerando opções para ele e suas aplicações.</p>
+<h2>Métodos estáticos</h2>
+<p>Nem todas as funções de uma classe precisam receber uma referência de um objeto para lê-lo ou alterá-lo, muitas vezes uma função pode fazer o seu papel apenas com os dados passados como argumento, por exemplo, receber um nome e validar se ele possui pelo menos três caracteres sem espaço. Dessa forma, essa função poderia ser colocada fora do escopo da classe, porém para facilitar sua chamada, e possíveis alterações (que será discutido em outra postagem), é possível colocar essa função dentro da classe e informar que ela não receberá o argumento <code>self</code> com o decorador <code>@staticmethod</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="o">...</span> <span class="c1"># Demais funções</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">valida_nome</span><span class="p">(</span><span class="n">nome</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">nome</span><span class="p">)</span> <span class="o">>=</span> <span class="mi">3</span> <span class="ow">and</span> <span class="s1">' '</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">nome</span>
+</pre></div>
+
+
+<p>Dessa forma, essa função pode ser chamada diretamente de um objeto pessoa, ou até mesmo diretamente da classe, sem precisar criar um objeto primeiro:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Chamando diretamente da classe</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">Pessoa</span><span class="o">.</span><span class="n">valida_nome</span><span class="p">(</span><span class="s1">'João'</span><span class="p">))</span>
+
+<span class="c1"># Chamando através de um objeto do tipo Pessoa</span>
+<span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">valida_nome</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">nome</span><span class="p">))</span>
+</pre></div>
+
+
+<p>E essa função também pode ser utilizada dendro de outras funções, como validar o nome na criação de uma pessoa, de forma que caso o nome informado seja válido, será criado um objeto do tipo Pessoa, e caso o nome seja inválido, será lançado uma exceção:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">valida_nome</span><span class="p">(</span><span class="n">nome</span><span class="p">):</span>
+ <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s1">'Nome inválido'</span><span class="p">)</span>
+
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+ <span class="o">...</span> <span class="c1"># Demais funções</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">valida_nome</span><span class="p">(</span><span class="n">nome</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">nome</span><span class="p">)</span> <span class="o">>=</span> <span class="mi">3</span> <span class="ow">and</span> <span class="s1">' '</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">nome</span>
+
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span> <span class="c1"># Cria objeto</span>
+<span class="n">p2</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'a'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span> <span class="c1"># Lança ValueError: Nome inválido</span>
+</pre></div>
+
+
+<h2>Métodos da classe</h2>
+<p>Entretanto algumas funções podem precisar de um meio termo, necessitar acessar o contexto da classe, porém sem necessitar de um objeto. Isso é feito através do decorador <code>@classmethod</code>, onde a função decorada com ele, em vez de receber um objeto como primeiro argumento, recebe a própria classe.</p>
+<p>Para demonstrar essa funcionalidade será implementado um <em>id</em> auto incremental para os objetos da classe <code>Pessoa</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="n">total_de_pessoas</span> <span class="o">=</span> <span class="mi">0</span>
+
+ <span class="nd">@classmethod</span>
+ <span class="k">def</span> <span class="nf">novo_id</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span>
+ <span class="bp">cls</span><span class="o">.</span><span class="n">total_de_pessoas</span> <span class="o">+=</span> <span class="mi">1</span>
+ <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">total_de_pessoas</span>
+
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">id</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">novo_id</span><span class="p">()</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">id</span><span class="p">)</span> <span class="c1"># Imprime 1</span>
+<span class="n">p2</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'Maria'</span><span class="p">,</span> <span class="s1">'dos Santos'</span><span class="p">,</span> <span class="mi">18</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p2</span><span class="o">.</span><span class="n">id</span><span class="p">)</span> <span class="c1"># Imprime 2</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">Pessoa</span><span class="o">.</span><span class="n">total_de_pessoas</span><span class="p">)</span> <span class="c1"># Imprime 2</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">total_de_pessoas</span><span class="p">)</span> <span class="c1"># Imprime 2</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p2</span><span class="o">.</span><span class="n">total_de_pessoas</span><span class="p">)</span> <span class="c1"># Imprime 2</span>
+</pre></div>
+
+
+<p>Nesse código é criado uma variável <code>total_de_pessoas</code> dentro do escopo da classe <code>Pessoas</code>, e que é compartilhado tanto pela classe, como pelos objetos dessa classe, diferente de declará-la com <code>self.</code> dentro do <code>__init__</code>, onde esse valor pertenceria apenas ao objeto, e não é compartilhado com os demais objetos. Declarar variáveis dentro do contexto da classe é similar ao se declarar variáveis com <code>static</code> em outras linguagens, assim como o <code>@classmethod</code> é semelhante a declaração de funções com <code>static</code>.</p>
+<p>As funções declaradas com <code>@classmethod</code> também podem ser chamadas sem a necessidade de se criar um objeto, como <code>Pessoa.novo_id()</code>, embora que para essa função específica isso não faça muito sentido, ou receber outros argumentos, tudo depende do que essa função fará.</p>
+<h2>Considerações</h2>
+<p>Embora possa parecer confuso identificar a diferença de uma função de um objeto (função sem decorador), função de uma classe (com decorador <code>@classmethod</code>) e função sem acesso a nenhum outro contexto (com decorador <code>@staticmethod</code>), essa diferença fica mais clara ao se analisar o primeiro argumento recebido por cada tipo de função. Podendo ser a referência a um objeto (<code>self</code>) e assim necessitando que um objeto seja criado anteriormente, ser uma classe (<code>cls</code>) e não necessitando receber um objeto, ou simplesmente não recebendo nenhum argumento especial, apenas os demais argumentos necessários para a função. Sendo diferenciados pelo uso dos decoradores.</p>
+<p>Na orientação a objetos implementada pelo Python, algumas coisas podem ficar confusas quando se mistura com nomenclaturas de outras linguagens que possuem implementações diferentes. A linguagem Java, por exemplo, utiliza a palavra-chave <code>static</code> para definir os atributos e métodos de classe, enquanto no Python um método estático é aquele que não acessa nem um objeto, nem uma classe, devendo ser utilizado o escopo da classe e o decorador <code>@classmethod</code> para se criar atributos e métodos da classe.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ 2021-04-19T20:00:00+00:00
+ Eduardo Klosowski
+
+
+ PythonClub: Orientação a objetos de outra forma: Classes e objetos
+ https://pythonclub.com.br/oo-de-outra-forma-1.html
+ <p>Nas poucas e raríssimas lives que eu fiz na <a href="https://www.twitch.tv/eduardoklosowski">Twitch</a>, surgiu a ideia de escrever sobre programação orientada a objetos em <a href="https://www.python.org/">Python</a>, principalmente por algumas diferenças de como ela foi implementada nessa linguagem. Aproveitando o tema, vou fazer uma série de postagens dando uma visão diferente sobre orientação a objetos. E nessa primeira postagem falarei sobre classes e objetos.</p>
+<h2>Usando um dicionário</h2>
+<p>Entretanto, antes de começar com orientação a objetos, gostaria de apresentar e discutir alguns exemplos sem utilizar esse paradigma de programação.</p>
+<p>Pensando em um sistema que precise manipular dados de pessoas, é possível utilizar os <a href="https://docs.python.org/pt-br/3/library/stdtypes.html#mapping-types-dict">dicionários</a> do Python para agrupar os dados de uma pessoa em uma única variável, como no exemplo a baixo:</p>
+<div class="highlight"><pre><span></span><span class="n">pessoa</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'nome'</span><span class="p">:</span> <span class="s1">'João'</span><span class="p">,</span>
+ <span class="s1">'sobrenome'</span><span class="p">:</span> <span class="s1">'da Silva'</span><span class="p">,</span>
+ <span class="s1">'idade'</span><span class="p">:</span> <span class="mi">20</span><span class="p">,</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>Onde os dados poderiam ser acessados através da variável e do nome do dado desejado, como:</p>
+<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="n">pessoa</span><span class="p">[</span><span class="s1">'nome'</span><span class="p">])</span> <span class="c1"># Imprimindo João</span>
+</pre></div>
+
+
+<p>Assim, todos os dados de uma pessoa ficam agrupados em uma variável, o que facilita bastante a programação, visto que não é necessário criar uma variável para cada dado, e quando se manipula os dados de diferentes pessoas fica muito mais fácil identificar de qual pessoa aquele dado se refere, bastando utilizar variáveis diferentes.</p>
+<h3>Função para criar o dicionário</h3>
+<p>Apesar de prático, é necessário replicar essa estrutura de dicionário toda vez que se desejar utilizar os dados de uma nova pessoa. Para evitar a repetição de código, a criação desse dicionário pode ser feita dentro de uma função que pode ser colocada em um módulo <code>pessoa</code> (arquivo, nesse caso com o nome de <code>pessoa.py</code>):</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa.py</span>
+
+<span class="k">def</span> <span class="nf">nova</span><span class="p">(</span><span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="k">return</span> <span class="p">{</span>
+ <span class="s1">'nome'</span><span class="p">:</span> <span class="n">nome</span><span class="p">,</span>
+ <span class="s1">'sobrenome'</span><span class="p">:</span> <span class="n">sobrenome</span><span class="p">,</span>
+ <span class="s1">'idade'</span><span class="p">:</span> <span class="n">idade</span><span class="p">,</span>
+ <span class="p">}</span>
+</pre></div>
+
+
+<p>E para criar o dicionário que representa uma pessoa, basta importar esse módulo (arquivo) e chamar a função <code>nova</code>:</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">pessoa</span><span class="o">.</span><span class="n">nova</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">p2</span> <span class="o">=</span> <span class="n">pessoa</span><span class="o">.</span><span class="n">nova</span><span class="p">(</span><span class="s1">'Maria'</span><span class="p">,</span> <span class="s1">'dos Santos'</span><span class="p">,</span> <span class="mi">18</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Desta forma, garante-se que todos os dicionários representando pessoas terão os campos desejados e devidamente preenchidos.</p>
+<h3>Função com o dicionário</h3>
+<p>Também é possível criar algumas funções para executar operações com os dados desses dicionários, como pegar o nome completo da pessoa, trocar o seu sobrenome, ou fazer aniversário (o que aumentaria a idade da pessoa em um ano):</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa.py</span>
+
+<span class="k">def</span> <span class="nf">nova</span><span class="p">(</span><span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="o">...</span> <span class="c1"># Código abreviado</span>
+
+
+<span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="n">pessoa</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{pessoa['nome']}</span><span class="s2"> </span><span class="si">{pessoa['sobrenome']}</span><span class="s2">"</span>
+
+
+<span class="k">def</span> <span class="nf">trocar_sobrenome</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'sobrenome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">sobrenome</span>
+
+
+<span class="k">def</span> <span class="nf">fazer_aniversario</span><span class="p">(</span><span class="n">pessoa</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'idade'</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
+</pre></div>
+
+
+<p>E sendo usado como:</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">pessoa</span><span class="o">.</span><span class="n">nova</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">pessoa</span><span class="o">.</span><span class="n">trocar_sobrenome</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="s1">'dos Santos'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">pessoa</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">(</span><span class="n">p1</span><span class="p">))</span>
+<span class="n">pessoa</span><span class="o">.</span><span class="n">fazer_aniversario</span><span class="p">(</span><span class="n">p1</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="p">[</span><span class="s1">'idade'</span><span class="p">])</span>
+</pre></div>
+
+
+<p>Nesse caso, pode-se observar que todas as funções aqui implementadas seguem o padrão de receber o dicionário que representa a pessoa como primeiro argumento, podendo ter outros argumentos ou não conforme a necessidade, acessando e alterando os valores desse dicionário.</p>
+<h2>Versão com orientação a objetos</h2>
+<p>Antes de entrar na versão orientada a objetos propriamente dita dos exemplos anteriores, vou fazer uma pequena alteração para facilitar o entendimento posterior. A função <code>nova</code> será separada em duas partes, a primeira que criará um dicionário, e chamará uma segunda função (<code>init</code>), que receberá esse dicionário como primeiro argumento (seguindo o padrão das demais funções) e criará sua estrutura com os devidos valores.</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa.py</span>
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'nome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'sobrenome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'idade'</span><span class="p">]</span> <span class="o">=</span> <span class="n">idade</span>
+
+
+<span class="k">def</span> <span class="nf">nova</span><span class="p">(</span><span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="n">pessoa</span> <span class="o">=</span> <span class="p">{}</span>
+ <span class="n">init</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">pessoa</span>
+
+
+<span class="o">...</span> <span class="c1"># Demais funções do arquivo</span>
+</pre></div>
+
+
+<p>Porém isso não muda a forma de uso:</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">pessoa</span><span class="o">.</span><span class="n">nova</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+</pre></div>
+
+
+<h3>Função para criar uma pessoa</h3>
+<p>A maioria das linguagens de programação que possuem o paradigma de programação orientado a objetos faz o uso de classes para definir a estrutura dos objetos. O Python também utiliza classes, que podem ser definidas com a palavra-chave <code>class</code> seguidas de um nome para ela. E dentro dessa estrutura, podem ser definidas funções para manipular os objetos daquela classe, que em algumas linguagens também são chamadas de métodos (funções declaradas dentro do escopo uma classe).</p>
+<p>Para converter o dicionário para uma classe, o primeiro passo é implementar uma função para criar a estrutura desejada. Essa função deve possui o nome <code>__init__</code>, e é bastante similar a função <code>init</code> do código anterior:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+</pre></div>
+
+
+<p>As diferenças são que agora o primeiro parâmetro se chama <code>self</code>, que é um padrão utilizado no Python, e em vez de usar colchetes e aspas para acessar os dados, aqui basta utilizar o ponto e o nome do dado desejado (que aqui também pode ser chamado de atributo, visto que é uma variável do objeto). A função <code>nova</code> implementada anteriormente não é necessária, a própria linguagem cria um objeto e passa ele como primeiro argumento para o <code>__init__</code>. E assim para se criar um objeto da classe <code>Pessoa</code> basta chamar a classe como se fosse uma função, ignorando o argumento <code>self</code> e informando os demais, como se estivesse chamando a função <code>__init__</code> diretamente:</p>
+<div class="highlight"><pre><span></span><span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Nesse caso, como a própria classe cria um contexto diferente para as funções (escopo ou <em>namespace</em>), não está mais sendo utilizado arquivos diferentes, porém ainda é possível fazê-lo, sendo necessário apenas fazer o <code>import</code> adequado. Mas para simplificação, tanto a declaração da classe, como a criação do objeto da classe <code>Pessoa</code> podem ser feitas no mesmo arquivo, assim como os demais exemplos dessa postagem.</p>
+<h3>Outras funções</h3>
+<p>As demais funções feitas anteriormente para o dicionário também podem ser feitas na classe <code>Pessoa</code>, seguindo as mesmas diferenças já apontadas anteriormente:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.nome}</span><span class="s1"> </span><span class="si">{self.sobrenome}</span><span class="s1">'</span>
+
+ <span class="k">def</span> <span class="nf">trocar_sobrenome</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+
+ <span class="k">def</span> <span class="nf">fazer_aniversario</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">+=</span> <span class="mi">1</span>
+</pre></div>
+
+
+<p>Para se chamar essas funções, basta acessá-las através do contexto da classe, passando o objeto criado anteriormente como primeiro argumento:</p>
+<div class="highlight"><pre><span></span><span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'dos Santos'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">Pessoa</span><span class="o">.</span><span class="n">trocar_sobrenome</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="s1">'dos Santos'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">Pessoa</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">(</span><span class="n">p1</span><span class="p">))</span>
+<span class="n">Pessoa</span><span class="o">.</span><span class="n">fazer_aniversario</span><span class="p">(</span><span class="n">p1</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">idade</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Essa sintaxe é bastante semelhante a versão sem orientação a objetos implementada anteriormente. Porém quando se está utilizando objetos, é possível chamar essas funções com uma outra sintaxe, informando primeiro o objeto, seguido de ponto e o nome da função desejada, com a diferença de que não é mais necessário informar o objeto como primeiro argumento. Como a função foi chamada através de um objeto, o próprio Python se encarrega de passá-lo para o argumento <code>self</code>, sendo necessário informar apenas os demais argumentos:</p>
+<div class="highlight"><pre><span></span><span class="n">p1</span><span class="o">.</span><span class="n">trocar_sobrenome</span><span class="p">(</span><span class="s1">'dos Santos'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">())</span>
+<span class="n">p1</span><span class="o">.</span><span class="n">fazer_aniversario</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">idade</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Existem algumas diferenças entre as duas sintaxes, porém isso será tratado posteriormente. Por enquanto a segunda sintaxe pode ser vista como um <a href="https://pt.wikipedia.org/wiki/A%C3%A7%C3%BAcar_sint%C3%A1tico">açúcar sintático</a> da primeira, ou seja, uma forma mais rápida e fácil de fazer a mesma coisa que a primeira, e por isso sendo a recomendada.</p>
+<h2>Considerações</h2>
+<p>Como visto nos exemplos, programação orientada a objetos é uma técnica para juntar variáveis em uma mesma estrutura e facilitar a escrita de funções que seguem um determinado padrão, recebendo a estrutura como argumento, porém a sintaxe mais utilizada no Python para chamar as funções de um objeto (métodos) posiciona a variável que guarda a estrutura antes do nome da função, em vez do primeiro argumento.</p>
+<p>No Python, o argumento da estrutura ou objeto (<code>self</code>) aparece explicitamente como primeiro argumento da função, enquanto em outras linguagens essa variável pode receber outro nome (como <code>this</code>) e não aparece explicitamente nos argumentos da função, embora essa variável tenha que ser criada dentro do contexto da função para permitir manipular o objeto.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ 2021-04-12T18:00:00+00:00
+ Eduardo Klosowski
+
+
+ PythonClub: Funções in place ou cópia de valor
+ https://pythonclub.com.br/funcao-inplace-ou-copia-de-valor.html
+ <p>Eventualmente observo dificuldades de algumas pessoas em usar corretamente alguma função, seja porque a função deveria ser executada isoladamente, e utilizado a própria variável que foi passada como argumento posteriormente, seja porque deveria se atribuir o retorno da função a alguma variável, e utilizar essa nova variável. No Python, essa diferença pode ser observada nos métodos das listas <code>sort</code> e <code>reverse</code> para as funções <code>sorted</code> e <code>reversed</code>, que são implementadas com padrões diferentes, <em>in place</em> e cópia de valor respectivamente. Assim pretendo discutir esses dois padrões de funções, comentando qual a diferença e o melhor caso de aplicação de cada padrão.</p>
+<h2>Função de exemplo</h2>
+<p>Para demonstrar como esses padrões funcionam, será implementado uma função que recebe uma lista e calcula o dobro dos valores dessa lista. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">entrada</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+
+<span class="c1"># Execução da função</span>
+
+<span class="n">resultado</span> <span class="o">=</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">8</span><span class="p">]</span>
+</pre></div>
+
+
+<h3>Função com in place</h3>
+<p>A ideia do padrão <em>in place</em> é alterar a própria variável recebida pela função (ou o próprio objeto, caso esteja lidando com orientação a objetos). Neste caso, bastaria calcular o dobro do valor de cada posição da lista, e sobrescrever a posição com seu resultado. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span>
+
+
+<span class="k">def</span> <span class="nf">dobro_inplace</span><span class="p">(</span><span class="n">lista</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
+ <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">lista</span><span class="p">)):</span>
+ <span class="n">lista</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">lista</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
+
+
+<span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">retorno</span> <span class="o">=</span> <span class="n">dobro_inplace</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: valores | Tipo: {type(valores)} | Valor: </span><span class="si">{valores}</span><span class="s1">'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: retorno | Tipo: {type(retorno)} | Valor: </span><span class="si">{retorno}</span><span class="s1">'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Resultado da execução:</p>
+<div class="highlight"><pre><span></span><span class="err">Variável: valores | Tipo: <class 'list'> | Valor: [10, 4, 16, 12, 8]</span>
+<span class="err">Variável: retorno | Tipo: <class 'NoneType'> | Valor: None</span>
+</pre></div>
+
+
+<p>Com essa execução é possível observar que os valores da lista foram alterados, e que o retorno da função é nulo (<code>None</code>), ou seja, a função alterou a própria lista passada como argumento. Outro ponto importante a ser observado é a assinatura da função (tipo dos argumentos e do retorno da função), que recebe uma lista de inteiros e não tem retorno ou é nulo (<code>None</code>). Dessa forma embora seja possível chamar essa função diretamente quando está se informando os argumentos de outra função, como <code>print(dobro_inplace(valores))</code>, a função <code>print</code> receberia <code>None</code> e não a lista como argumento.</p>
+<h3>Função com cópia de valor</h3>
+<p>A ideia do padrão cópia de valor é criar uma cópia do valor passado como argumento e retornar essa cópia, sem alterar a variável recebida (ou criando um novo objeto, no caso de orientação a objetos). Neste caso, é necessário criar uma nova lista e adicionar nela os valores calculados. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span>
+
+
+<span class="k">def</span> <span class="nf">dobro_copia</span><span class="p">(</span><span class="n">lista</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
+ <span class="n">nova_lista</span> <span class="o">=</span> <span class="p">[]</span>
+
+ <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">lista</span><span class="p">)):</span>
+ <span class="n">nova_lista</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">lista</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
+
+ <span class="k">return</span> <span class="n">nova_lista</span>
+
+
+<span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">retorno</span> <span class="o">=</span> <span class="n">dobro_copia</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: valores | Tipo: {type(valores)} | Valor: </span><span class="si">{valores}</span><span class="s1">'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: retorno | Tipo: {type(retorno)} | Valor: </span><span class="si">{retorno}</span><span class="s1">'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Resultado da execução:</p>
+<div class="highlight"><pre><span></span><span class="err">Variável: valores | Tipo: <class 'list'> | Valor: [5, 2, 8, 6, 4]</span>
+<span class="err">Variável: retorno | Tipo: <class 'list'> | Valor: [10, 4, 16, 12, 8]</span>
+</pre></div>
+
+
+<p>Com essa execução é possível observar que a variável <code>valores</code> continua com os valores que tinha antes da execução da função, e a variável retorno apresenta uma lista com os dobros, ou seja, a função não altera a lista passada como argumento e retorna uma nova lista com os valores calculados. Observado a assinatura da função, ela recebe uma lista de inteiros e retorna uma lista de inteiros. Isso permite chamar essa função diretamente nos argumentos para outra função, como <code>print(dobro_copia(valores))</code>, nesse caso a função <code>print</code> receberia a lista de dobros como argumento. Porém caso o retorno da função não seja armazenado, parecerá que a função não fez nada, ou não funcionou. Então em alguns casos, quando o valor anterior não é mais necessário, pode-se reatribuir o retorno da função a própria variável passada como argumento:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="n">dobro_copia</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+</pre></div>
+
+
+<h3>Função híbrida</h3>
+<p>Ainda é possível mesclar os dois padrões de função, alterando o valor passado e retornando-o. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span>
+
+
+<span class="k">def</span> <span class="nf">dobro_hibrido</span><span class="p">(</span><span class="n">lista</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
+ <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">lista</span><span class="p">)):</span>
+ <span class="n">lista</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">lista</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
+
+ <span class="k">return</span> <span class="n">lista</span>
+
+
+<span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">retorno</span> <span class="o">=</span> <span class="n">dobro_hibrido</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: valores | Tipo: {type(valores)} | Valor: </span><span class="si">{valores}</span><span class="s1">'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: retorno | Tipo: {type(retorno)} | Valor: </span><span class="si">{retorno}</span><span class="s1">'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Resultado da execução:</p>
+<div class="highlight"><pre><span></span><span class="err">Variável: valores | Tipo: <class 'list'> | Valor: [10, 4, 16, 12, 8]</span>
+<span class="err">Variável: retorno | Tipo: <class 'list'> | Valor: [10, 4, 16, 12, 8]</span>
+</pre></div>
+
+
+<p>Nesse caso, pode-se apenas chamar a função, como também utilizá-la nos argumentos de outras funções. Porém para se ter os valores originais, deve-se fazer uma cópia manualmente antes de executar a função.</p>
+<h2>Exemplo na biblioteca padrão</h2>
+<p>Na biblioteca padrão do Python, existem os métodos <code>sort</code> e <code>reverse</code> que seguem o padrão <em>in place</em>, e as funções <code>sorted</code> e <code>reversed</code> que seguem o padrão cópia de valor, podendo ser utilizados para ordenar e inverter os valores de uma lista, por exemplo. Quando não é mais necessário uma cópia da lista com a ordem original, é preferível utilizar funções <em>in place</em>, que alteram a própria lista, e como não criam uma cópia da lista, utilizam menos memória. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">valores</span><span class="o">.</span><span class="n">sort</span><span class="p">()</span>
+<span class="n">valores</span><span class="o">.</span><span class="n">reverse</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Se for necessário manter uma cópia da lista inalterada, deve-se optar pelas funções de cópia de valor. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">novos_valores</span> <span class="o">=</span> <span class="nb">reversed</span><span class="p">(</span><span class="nb">sorted</span><span class="p">(</span><span class="n">valores</span><span class="p">))</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">novos_valores</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Porém esse exemplo cria duas cópias da lista, uma em cada função. Para criar apenas uma cópia, pode-se misturar funções <em>in place</em> com cópia de valor. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">novos_valores</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+<span class="n">novos_valores</span><span class="o">.</span><span class="n">reverse</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">novos_valores</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Também vale observar que algumas utilizações dessas funções podem dar a impressão de que elas não funcionaram, como:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+
+<span class="nb">sorted</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span> <span class="c1"># Imprime a lista original, e não a ordenada</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">valores</span><span class="o">.</span><span class="n">sort</span><span class="p">())</span> <span class="c1"># Imprime None e não a lista</span>
+</pre></div>
+
+
+<h2>Considerações</h2>
+<p>Nem sempre é possível utilizar o padrão desejado, <em>strings</em> no Python (<code>str</code>) são imutáveis, logo todas as funções que manipulam elas seguiram o padrão cópia de valor, e para outros tipos, pode ocorrer de só existir funções <em>in place</em>, sendo necessário fazer uma cópia manualmente antes de chamar a função, caso necessário. Para saber qual padrão a função implementa, é necessário consultar sua documentação, ou verificando sua assinatura, embora ainda possa existir uma dúvida entre cópia de valor e híbrida, visto que a assinatura dos dois padrões são iguais.</p>
+<p>Os exemplos aqui dados são didáticos. Caso deseja-se ordenar de forma reversa, tanto o método <code>sort</code>, quanto a função <code>sorted</code> podem receber como argumento <code>reverse=True</code>, e assim já fazer a ordenação reversa. Assim como é possível criar uma nova lista já com os valores, sem precisar adicionar manualmente item por item, como os exemplos:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">partes_dos_valores</span> <span class="o">=</span> <span class="n">valores</span><span class="p">[</span><span class="mi">2</span><span class="p">:]</span>
+<span class="n">novos_valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span> <span class="o">*</span> <span class="n">valor</span> <span class="k">for</span> <span class="n">valor</span> <span class="ow">in</span> <span class="n">valores</span><span class="p">]</span>
+</pre></div>
+
+
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ 2021-03-29T15:00:00+00:00
+ Eduardo Klosowski
+
+
+ PythonClub: Encapsulamento da lógica do algoritmo
+ https://pythonclub.com.br/encapsulamento-da-logica-do-algoritmo.html
+ <p>Muitas listas de exercícios de lógica de programação pedem em algum momento que um valor seja lido do teclado, e caso esse valor seja inválido, deve-se avisar, e repetir a leitura até que um valor válido seja informado. Utilizando a ideia de <a href="https://dev.to/acaverna/otimizando-o-algoritmo-passo-a-passo-4co0">otimização do algoritmo passo a passo</a>, começando com uma solução simples, pretendo estudar como reduzir a duplicação de código alterando o algoritmo, encapsulando a lógica em funções, e encapsulando em classes.</p>
+<h2>Exercício</h2>
+<p>Um exemplo de exercício que pede esse tipo de validação é a leitura de notas, que devem estar entre 0 e 10. A solução mais simples, consiste em ler um valor, e enquanto esse valor for inválido, dar o aviso e ler outro valor. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">))</span>
+<span class="k">while</span> <span class="n">nota</span> <span class="o"><</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">nota</span> <span class="o">></span> <span class="mi">10</span><span class="p">:</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'Nota inválida'</span><span class="p">)</span>
+ <span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">))</span>
+</pre></div>
+
+
+<p>Esse algoritmo funciona, porém existe uma duplicação no código que faz a leitura da nota (uma antes do <em>loop</em> e outra dentro). Caso seja necessário uma alteração, como a mudança da nota para um valor inteiro entre 0 e 100, deve-se alterar os dois lugares, e se feito em apenas um lugar, o algoritmo poderia processar valores inválidos.</p>
+<h3>Alterando o algoritmo</h3>
+<p>Visando remover a repetição de código, é possível unificar a leitura do valor dentro do <em>loop</em>, uma vez que é necessário repetir essa instrução até que o valor válido seja obtido. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">))</span>
+ <span class="k">if</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">nota</span> <span class="o"><=</span> <span class="mi">10</span><span class="p">:</span>
+ <span class="k">break</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'Nota inválida!'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Dessa forma, não existe mais a repetição de código. A condição de parada, que antes verificava se o valor era inválido (o que pode ter uma leitura não tão intuitiva), agora verifica se é um valor válido (que é geralmente é mais fácil de ler e escrever a condição). E a ordem dos comandos dentro do <em>loop</em>, que agora estão em uma ordem que facilita a leitura, visto que no algoritmo anterior era necessário tem em mente o que era executado antes do <em>loop</em>.</p>
+<p>Porém esses algoritmos validam apenas o valor lido, apresentando erro caso seja informado um valor com formato inválido, como letras em vez de números. Isso pode ser resolvido tratando as exceções lançadas. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">))</span>
+ <span class="k">if</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">nota</span> <span class="o"><=</span> <span class="mi">10</span><span class="p">:</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'Nota inválida!'</span><span class="p">)</span>
+</pre></div>
+
+
+<h3>Encapsulamento da lógica em função</h3>
+<p>Caso fosse necessário ler várias notas, com os algoritmos apresentados até então, seria necessário repetir todo esse trecho de código, ou utilizá-lo dentro de uma estrutura de repetição. Para facilitar sua reutilização, evitando a duplicação de código, é possível encapsular esse algoritmo dentro de uma função. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">nota_input</span><span class="p">(</span><span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">nota</span> <span class="o"><=</span> <span class="mi">10</span><span class="p">:</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'Nota inválida!'</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">nota</span>
+
+
+<span class="n">nota1</span> <span class="o">=</span> <span class="n">nota_input</span><span class="p">(</span><span class="s1">'Digite a primeira nota: '</span><span class="p">)</span>
+<span class="n">nota2</span> <span class="o">=</span> <span class="n">nota_input</span><span class="p">(</span><span class="s1">'Digite a segunda nota: '</span><span class="p">)</span>
+</pre></div>
+
+
+<h3>Encapsulamento da lógica em classes</h3>
+<p>Em vez de encapsular essa lógica em uma função, é possível encapsulá-la em uma classe, o que permitiria separar cada etapa do algoritmo em métodos, assim como ter um método responsável por controlar qual etapa deveria ser chamada em qual momento. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ValidaNotaInput</span><span class="p">:</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nota inválida!'</span>
+
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">entrada</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">validar_nota</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nota</span><span class="p">):</span>
+ <span class="k">return</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">nota</span> <span class="o"><=</span> <span class="mi">10</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">nota</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_nota</span><span class="p">(</span><span class="n">nota</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">nota</span>
+
+
+<span class="n">nota_input</span> <span class="o">=</span> <span class="n">ValidaNotaInput</span><span class="p">()</span>
+
+
+<span class="n">nota</span> <span class="o">=</span> <span class="n">nota_input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Vale observar que o método <code>__call__</code> permite que o objeto criado a partir dessa classe seja chamado como se fosse uma função. Nesse caso ele é o responsável por chamar cada etapa do algoritmo, como: <code>ler_entrada</code> que é responsável por ler o que foi digitado no teclado, <code>transformar_entrada</code> que é responsável por converter o texto lido para o tipo desejado (converter de <code>str</code> para <code>float</code>), e <code>validar_nota</code> que é responsável por dizer se o valor é válido ou não. Vale observar que ao dividir o algoritmo em métodos diferentes, seu código principal virou uma espécie de código comentado, descrevendo o que está sendo feito e onde está sendo feito.</p>
+<p>Outra vantagem de encapsular a lógica em classe, em vez de uma função, é a possibilidade de generalizá-la. Se fosse necessário validar outro tipo de entrada, encapsulando em uma função, seria necessário criar outra função repetindo todo o algoritmo, alterando apenas a parte referente a transformação do valor lido, e validação, o que gera uma espécie de repetição de código. Ao encapsular em classes, é possível se aproveitar dos mecanismos de herança para evitar essa repetição. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ValidaInput</span><span class="p">:</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Valor inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">raise</span> <span class="ne">NotImplementedError</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">raise</span> <span class="ne">NotImplementedError</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">valor</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNomeInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nome inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">entrada</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">title</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">valor</span> <span class="o">!=</span> <span class="s1">''</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNotaInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nota inválida!'</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">entrada</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">valor</span> <span class="o"><=</span> <span class="mi">10</span>
+
+
+<span class="n">nome_input</span> <span class="o">=</span> <span class="n">ValidaNomeInput</span><span class="p">()</span>
+<span class="n">nota_input</span> <span class="o">=</span> <span class="n">ValidaNotaInput</span><span class="p">()</span>
+
+
+<span class="n">nome</span> <span class="o">=</span> <span class="n">nome_input</span><span class="p">(</span><span class="s1">'Digite o nome: '</span><span class="p">)</span>
+<span class="n">nota</span> <span class="o">=</span> <span class="n">nota_input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Dessa forma, é possível reutilizar o código já existente para criar outras validações, sendo necessário implementar apenas como converter a <code>str</code> lida do teclado para o tipo desejado, e como esse valor deve ser validado. Não é necessário entender e repetir a lógica de ler o valor, validá-lo, imprimir a mensagem de erro, e repetir até que seja informado um valor válido.</p>
+<h2>Considerações</h2>
+<p>É possível encapsular a lógica de um algoritmo em funções ou em classes. Embora para fazê-lo em uma classe exija conhecimentos de programação orientada a objetos, o seu reaproveitamento é facilitado, abstraindo toda a complexidade do algoritmo, que pode ser disponibilizado através de uma biblioteca, exigindo apenas a implementações de métodos simples por quem for a utilizar.</p>
+<p>Ainda poderia ser discutido outras formas de fazer essa implementação, como passar funções como parâmetro e a utilização de <a href="https://docs.python.org/pt-br/3/library/asyncio-task.html">corrotinas</a> no encapsulamento do algoritmo em função, assim como a utilização de <a href="https://docs.python.org/pt-br/3/library/functions.html#classmethod">classmethod</a>, <a href="https://docs.python.org/pt-br/3/library/functions.html#staticmethod">staticmethod</a> e <a href="https://docs.python.org/pt-br/3/library/abc.html">ABC</a> no encapsulamento do algoritmo em classes.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ 2021-03-02T18:00:00+00:00
+ Eduardo Klosowski
+
+
+ PythonClub: Fazendo backup do banco de dados no Django
+ https://pythonclub.com.br/fazendo-backup-do-banco-de-dados-no-django.html
+ <h2>Apresentação</h2>
+<p>Em algum momento, durante o seu processo de desenvolvimento com Django, pode ser que surja a necessidade de criar e restaurar o banco de dados da aplicação. Pensando nisso, resolvi fazer um pequeno tutorial, básico, de como realizar essa operação.</p>
+<p>Nesse tutorial, usaremos o <a href="https://github.com/django-dbbackup/django-dbbackup">django-dbbackup</a>, um pacote desenvolvido especificamente para isso.</p>
+<h2>Configurando nosso ambiente</h2>
+<p>Primeiro, partindo do início, vamos criar uma pasta para o nosso projeto e, nela, isolar o nosso ambiente de desenvolvimento usando uma <a href="https://virtualenv.pypa.io/en/latest/index.html">virtualenv</a>:</p>
+<div class="highlight"><pre><span></span>mkdir projeto_db <span class="o">&&</span> <span class="nb">cd</span> projeto_db <span class="c1">#criando a pasta do nosso projeto</span>
+
+virtualenv -p python3.8 env <span class="o">&&</span> <span class="nb">source</span> env/bin/activate <span class="c1">#criando e ativando a nossa virtualenv</span>
+</pre></div>
+
+
+<p>Depois disso e com o nosso ambiente já ativo, vamos realizar os seguintes procedimentos:</p>
+<div class="highlight"><pre><span></span>pip install -U pip <span class="c1">#com isso, atualizamos a verão do pip instalado</span>
+</pre></div>
+
+
+<h2>Instalando as dependências</h2>
+<p>Agora, vamos instalar o <a href="https://www.djangoproject.com/">Django</a> e o pacote que usaremos para fazer nossos backups.</p>
+<div class="highlight"><pre><span></span>pip install <span class="nv">Django</span><span class="o">==</span><span class="m">3</span>.1.2 <span class="c1">#instalando o Django</span>
+
+pip install django-dbbackup <span class="c1">#instalando o django-dbbackup</span>
+</pre></div>
+
+
+<h2>Criando e configurando projeto</h2>
+<p>Depois de instaladas nossas dependências, vamos criar o nosso projeto e configurar o nosso pacote nas configurações do Django.</p>
+<div class="highlight"><pre><span></span>django-admin startproject django_db . <span class="c1">#dentro da nossa pasta projeto_db, criamos um projeto Django com o nome de django_db.</span>
+</pre></div>
+
+
+<p>Depois de criado nosso projeto, vamos criar e popular o nosso banco de dados.</p>
+<div class="highlight"><pre><span></span>python manage.py migrate <span class="c1">#com isso, sincronizamos o estado do banco de dados com o conjunto atual de modelos e migrações.</span>
+</pre></div>
+
+
+<p>Criado nosso banco de dados, vamos criar um superusuário para podemos o painel admin do nosso projeto.</p>
+<div class="highlight"><pre><span></span>python manage.py createsuperuser
+</pre></div>
+
+
+<p>Perfeito. Já temos tudo que precisamos para executar nosso projeto. Para execução dele, é só fazermos:</p>
+<div class="highlight"><pre><span></span>python manage.py runserver
+</pre></div>
+
+
+<p>Você terá uma imagem assim do seu projeto:</p>
+<p><img alt="" src="https://jacksonosvaldo.github.io/img/django_db.png" /></p>
+<h2>Configurando o django-dbbackup</h2>
+<p>Dentro do seu projeto, vamos acessar o arquivo settings.py, como expresso abaixo:</p>
+<div class="highlight"><pre><span></span>django_db/
+├── settings.py
+</pre></div>
+
+
+<p>Dentro desse arquivos iremos, primeiro, adiconar o django-dbbackup às apps do projeto:</p>
+<div class="highlight"><pre><span></span><span class="n">INSTALLED_APPS</span> <span class="o">=</span> <span class="p">(</span>
+ <span class="o">...</span>
+ <span class="s1">'dbbackup'</span><span class="p">,</span> <span class="c1"># adicionando django-dbbackup</span>
+<span class="p">)</span>
+</pre></div>
+
+
+<p>Depois de adicionado às apps, vamos dizer para o Django o que vamos salvar no backup e, depois, indicar a pasta para onde será encaminhado esse arquivo. Essa inserção deve ou pode ser feita no final do arquivo <em>settings.py</em>:</p>
+<div class="highlight"><pre><span></span><span class="n">DBBACKUP_STORAGE</span> <span class="o">=</span> <span class="s1">'django.core.files.storage.FileSystemStorage'</span> <span class="c1">#o que salvar</span>
+<span class="n">DBBACKUP_STORAGE_OPTIONS</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'location'</span><span class="p">:</span> <span class="s1">'backups/'</span><span class="p">}</span> <span class="c1"># onde salvar</span>
+</pre></div>
+
+
+<p>Percebam que dissemos para o Django salvar o backup na pasta <em>backups</em>, mas essa pasta ainda não existe no nosso projeto. Por isso, precisamos criá-la [fora da pasta do projeto]:</p>
+<div class="highlight"><pre><span></span>mkdir backups
+</pre></div>
+
+
+<h2>Criando e restaurando nosso backup</h2>
+<p>Já temos tudo pronto. Agora, vamos criar o nosso primeiro backup:</p>
+<div class="highlight"><pre><span></span>python manage.py dbbackup
+</pre></div>
+
+
+<p>Depois de exetudado, será criado um arquivo -- no nosso exemplo, esse arquivo terá uma extensão .dump --, salvo na pasta <em>backups</em>. Esse arquivo contem todo backup do nosso banco de dados.</p>
+<p>Para recuperarmos nosso banco, vamos supor que migramos nosso sistema de um servidor antigo para um novo e, por algum motivo, nossa base de dados foi corrompida, inviabilizando seu uso. Ou seja, estamos com o sistema/projeto sem banco de dados -- ou seja, exlua ou mova a a sua base dados .sqlite3 para que esse exemplo seja útil --, mas temos os backups. Com isso, vamos restaurar o banco:</p>
+<div class="highlight"><pre><span></span>python manage.py dbrestore
+</pre></div>
+
+
+<p>Prontinho, restauramos nosso banco de dados. O interessante do django-dbbackup, dentre outras coisas, é que ele gera os backups com datas e horários específicos, facilitando o processo de recuperação das informações mais recentes.</p>
+<p>Por hoje é isso, pessoal. Até a próxima. ;) </p>
+ 2020-10-30T13:40:00+00:00
+ Jackson Osvaldo
+
+
+ Filipe Saraiva: Seqtembro de eventos virtuais e gratuitos sobre Qt e KDE
+ https://blog.filipesaraiva.info/?p=2185
+ <p>(Ok a piada com <em>seqtembro</em> funciona melhor na versão em inglês, <em>seqtember</em>, mas simbora)</p>
+<p>Por uma grande coincidência, obra do destino, ou nada disso, teremos um Setembro de 2020 repleto de eventos virtuais e gratuitos de alta qualidade sobre <a href="https://www.qt.io/" target="_blank" rel="noopener noreferrer">Qt</a> e <a href="https://kde.org/" target="_blank" rel="noopener noreferrer">KDE</a>.</p>
+<p>Começando de 4 à 11 do referido mês teremos o <a href="https://akademy.kde.org/2020/" target="_blank" rel="noopener noreferrer">Akademy 2020</a>, o grande encontro mundial da comunidade KDE que esse ano, por motivos que todos sabemos, acontecerá de forma virtual. A <a href="https://akademy.kde.org/2020/program" target="_blank" rel="noopener noreferrer">programação</a> do Akademy traz palestras, treinamentos, <em>hacking sessions</em>, discussões com foco em aplicações KDE específicas, e mais, reunindo hackers, designers, gerentes de projetos, tradutores, e colabores dos mais diversos segmentos para discutir e planejar o KDE e seus futuros passos.</p>
+<p><a href="https://blog.filipesaraiva.info/?attachment_id=2190" rel="attachment wp-att-2190"><img /></a></p>
+<p>E como falamos em KDE, por extensão, também falamos em Qt – afinal, grande parte das aplicações é escrita nesse framework. Portanto, mesmo que você trabalhe com Qt mas não use nada do KDE, vale a pena participar do evento – e também se perguntar “porque diabos não estou usando e desenvolvendo aplicações do KDE?”.</p>
+<p>Um incentivo extra é que durante o Akademy, entre 7 e 11, acontecerá o <a href="https://www.qtdesktopdays.com/" target="_blank" rel="noopener noreferrer">Qt Desktop Days</a>, evento da <a href="https://www.kdab.com/" target="_blank" rel="noopener noreferrer">KDAB</a> voltado para Qt no desktop (surpresa?). A <a href="https://www.qtdesktopdays.com/program/" target="_blank" rel="noopener noreferrer">programação preliminar</a> já está disponível e será muito interessante ver os avanços da tecnologia em um campo que pode parecer menos <em>sexy</em> hoje em dia, por conta da muita atenção dada a projetos mobile ou embarcados, mas que pelo contrário, continua vibrante e recebendo muito investimento.</p>
+<p><img /></p>
+<p>Após uma rápida pausa para um respiro, temos a <a href="http://bit.do/maratonaqt" target="_blank" rel="noopener noreferrer">Maratona Qt</a>. Nosso amigo <a href="https://sandroandrade.org/" target="_blank" rel="noopener noreferrer">Sandro Andrade</a>, professor do IFBA e colaborador de longa data do KDE, resolveu dedicar uma semana inteira, de 14 à 18 de setembro, para apresentar 5 tópicos sobre o Qt tratando de seus fundamentos e passos iniciais de cada um. O programa cobre QML, C++ e Qt, Qt no Android, no iOS, na web (sim!), computação gráfica e mesmo jogos! Extremamente recomendada pra todo mundo que conhece ou quer conhecer o framework.</p>
+<p>A Maratona Qt vai servir como um esquenta para a <a href="https://br.qtcon.org/" target="_blank" rel="noopener noreferrer">QtCon Brasil 2020</a>, esse ano também virtual. Em 26 e 27 de setembro o pessoal da <a href="http://qmob.solutions/" target="_blank" rel="noopener noreferrer">qmob.solutions</a> reunirá desenvolvedores Qt de vários países para apresentarem, entre outras coisas, trabalhos com Wayland, visão computacional e IA, análise de dados, Python, containers, prototipagem, embarcados, e outros, tudo envolvendo Qt! E também haverá uma apresentação sobre a próxima versão <em>major</em> da ferramenta, Qt 6.</p>
+<p>Portanto pessoal, reservem este mês para uma grande imersão nos vários aspectos e possibilidades disponibilizadas pelo Qt.</p>
+ 2020-08-29T18:48:00+00:00
+
+
+ Gabbleblotchits: MinHashing all the things: a quick analysis of MAG search results
+ https://blog.luizirber.org/2020/07/24/mag-results/
+ <p><a href="https://blog.luizirber.org/2020/07/22/mag-search/">Last time</a> I described a way to search MAGs in metagenomes,
+and teased about interesting results.
+Let's dig in some of them!</p>
+<p>I prepared a <a href="https://github.com/luizirber/2020-07-22-mag-search/">repo</a> with the data and a notebook with the analysis I did in this
+post.
+You can also follow along in <a href="https://mybinder.org">Binder</a>,
+as well as do your own analysis! <a href="https://mybinder.org/v2/gh/luizirber/2020-07-22-mag-search/master?filepath=index.ipynb"><img alt="Binder" src="https://mybinder.org/badge_logo.svg" /></a></p>
+<h2>Preparing some metadata</h2>
+<p>The supplemental materials for <a href="https://www.nature.com/articles/sdata2017203">Tully et al</a> include more details about each MAG,
+so let's download them.
+I prepared a small snakemake workflow to do that,
+as well as downloading information about the SRA datasets from Tara Oceans
+(the dataset used to generate the MAGs),
+as well as from <a href="https://www.nature.com/articles/s41564-017-0012-7">Parks et al</a>,
+which also generated MAGs from Tara Oceans.
+Feel free to include them in your analysis,
+but I was curious to find matches in other metagenomes.</p>
+<h2>Loading the data</h2>
+<p>The results from the MAG search are in a CSV file,
+with a column for the MAG name,
+another for the SRA dataset ID for the metagenome and a third column for the
+containment of the MAG in the metagenome.
+I also fixed the names to make it easier to query,
+and finally removed the Tara and Parks metagenomes
+(because we already knew they contained these MAGs).</p>
+<p>This left us with 23,644 SRA metagenomes with matches,
+covering 2,291 of the 2,631 MAGs.
+These are results for a fairly low containment (10%),
+so if we limit to MAGs with more than 50% containment we still have 1,407 MAGs and 2,938 metagenomes left.</p>
+<h2>TOBG_NP-110, I choose you!</h2>
+<p>That's still a lot,
+so I decided to pick a candidate to check before doing any large scale analysis.
+I chose TOBG_NP-110 because there were many matches above 50% containment,
+and even some at 99%.
+Turns out it is also an Archaeal MAG that failed to be classified further than Phylum level (Euryarchaeota),
+with a 70.3% complete score in the original analysis.
+Oh, let me dissect the name a bit:
+TOBG is "Tara Ocean Binned Genome" and "NP" is North Pacific.</p>
+<p>And so I went checking where the other metagenome matches came from.
+5 of the 12 matches above 50% containment come from one study,
+<a href="https://trace.ncbi.nlm.nih.gov/Traces/sra/?study=SRP044185">SRP044185</a>,
+with samples collected from a column of water in a station in Manzanillo, Mexico.
+Other 3 matches come from
+<a href="https://trace.ncbi.nlm.nih.gov/Traces/sra/?study=SRP003331">SRP003331</a>,
+in the South Pacific ocean (in northern Chile).
+Another match,
+<a href="https://trace.ncbi.nlm.nih.gov/Traces/sra/?run=ERR3256923">ERR3256923</a>,
+also comes from the South Pacific.</p>
+<h2>What else can I do?</h2>
+<p>I'm curious to follow <a href="http://merenlab.org/data/refining-mags/">the refining MAGs</a> tutorial from the Meren Lab and see where this goes,
+and especially in using <a href="https://genomebiology.biomedcentral.com/articles/10.1186/s13059-020-02066-4"><code>spacegraphcats</code></a>
+to extract neighborhoods from the MAG and better evaluate what is missing or if there are other interesting bits that
+the MAG generation methods ended up discarding.</p>
+<p>So, for now that's it.
+But more important,
+I didn't want to sit on these results until there is a publication in press,
+especially when there are people that can do so much more with these,
+so I decided to make it all public.
+It is way more exciting to see this being used to know more about these
+organisms than me being the only one with access to this info.</p>
+<p>And yesterday I saw <a href="https://twitter.com/DrJonathanRosa/status/1286381346605027328">this tweet</a> by
+<a href="https://twitter.com/DrJonathanRosa/status/1286381346605027328">@DrJonathanRosa</a>,
+saying:</p>
+<blockquote>
+<p>I don’t know who told students that the goal of research is to find some
+previously undiscovered research topic, claim individual ownership over it,
+& fiercely protect it from theft, but that almost sounds like, well,
+colonialism, capitalism, & policing </p>
+</blockquote>
+<p>Amen.</p>
+<h2>I want to run this with my data!</h2>
+<p>Next time. But we will have a discussion about scientific infrastructure and
+sustainability first =]</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://twitter.com/luizirber/status/1286700888111738880">Thread on Twitter</a></li>
+</ul>
+ 2020-07-24T15:00:00+00:00
+ luizirber
+
+
+ Gabbleblotchits: MinHashing all the things: searching for MAGs in the SRA
+ https://blog.luizirber.org/2020/07/22/mag-search/
+ <p>(or: Top-down and bottom-up approaches for working around sourmash limitations)</p>
+<p>In the last month I updated <a href="https://wort.oxli.org">wort</a>,
+the system I developed for computing sourmash signature for public genomic databases,
+and started calculating signatures
+for the <a href="https://www.ncbi.nlm.nih.gov/sra/?term=%22METAGENOMIC%22%5Bsource%5D+NOT+amplicon%5BAll+Fields%5D)">metagenomes</a> in the <a href="https://www.ncbi.nlm.nih.gov/sra/">Sequence Read Archive</a>.
+This is a more challenging subset than the <a href="https://blog.luizirber.org/2016/12/28/soursigs-arch-1/">microbial datasets</a> I was doing previously,
+since there are around 534k datasets from metagenomic sources in the SRA,
+totalling 447 TB of data.
+Another problem is the size of the datasets,
+ranging from a couple of MB to 170 GB.
+Turns out that the workers I have in <code>wort</code> are very good for small-ish datasets,
+but I still need to figure out how to pull large datasets faster from the SRA,
+because the large ones take forever to process...</p>
+<p>The good news is that I managed to calculate signatures for almost 402k of them
+<sup id="sf-mag-search-1-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-mag-search-1" class="simple-footnote" title="pulling about a 100 TB in 3 days, which was pretty fun to see because I ended up DDoS myself because I couldn't download the generated sigs fast enough from the S3 bucket where they are temporarily stored =P">1</a></sup>,
+which already let us work on some pretty exciting problems =]</p>
+<h2>Looking for MAGs in the SRA</h2>
+<p>Metagenome-assembled genomes are essential for studying organisms that are hard to isolate and culture in lab,
+especially for environmental metagenomes.
+<a href="https://www.nature.com/articles/sdata2017203">Tully et al</a> published 2,631 draft MAGs from 234 samples collected during the Tara Oceans expedition,
+and I wanted to check if they can also be found in other metagenomes besides the Tara Oceans ones.
+The idea is to extract the reads from these other matches and evaluate how the MAG can be improved,
+or at least evaluate what is missing in them.
+I choose to use environmental samples under the assumption they are easier to deposit on the SRA and have public access,
+but there are many human gut microbiomes in the SRA and this MAG search would work just fine with those too.</p>
+<p>Moreover,
+I want to search for containment,
+and not similarity.
+The distinction is subtle,
+but similarity takes into account both datasets sizes
+(well, the size of the union of all elements in both datasets),
+while containment only considers the size of the query.
+This is relevant because the similarity of a MAG and a metagenome is going to be very small (and is symmetrical),
+but the containment of the MAG in the metagenome might be large
+(and is asymmetrical, since the containment of the metagenome in the MAG is likely very small because the metagenome is so much larger than the MAG).</p>
+<h2>The computational challenge: indexing and searching</h2>
+<p>sourmash signatures are a small fraction of the original size of the datasets,
+but when you have hundreds of thousands of them the collection ends up being pretty large too.
+More precisely, 825 GB large.
+That is way bigger than any index I ever built for sourmash,
+and it would also have pretty distinct characteristics than what we usually do:
+we tend to index genomes and run <code>search</code> (to find similar genomes) or <code>gather</code>
+(to decompose metagenomes into their constituent genomes),
+but for this MAG search I want to find which metagenomes have my MAG query above a certain containment threshold.
+Sort of a <code>sourmash search --containment</code>,
+but over thousands of metagenome signatures.
+The main benefit of an SBT index in this context is to avoid checking all signatures because we can prune the search early,
+but currently SBT indices need to be totally loaded in memory during <code>sourmash index</code>.
+I will have to do this in the medium term,
+but I want a solution NOW! =]</p>
+<p><a href="https://github.com/dib-lab/sourmash/releases/tag/v3.4.0">sourmash 3.4.0</a> introduced <code>--from-file</code> in many commands,
+and since I can't build an index I decided to use it to load signatures for the metagenomes.
+But... <code>sourmash search</code> tries to load all signatures in memory,
+and while I might be able to find a cluster machine with hundreds of GBs of RAM available,
+that's not very practical.</p>
+<p>So, what to do?</p>
+<h2>The top-down solution: a snakemake workflow</h2>
+<p>I don't want to modify sourmash now,
+so why not make a workflow and use snakemake to run one <code>sourmash search --containment</code> for each metagenome?
+That means 402k tasks,
+but at least I can use <a href="https://snakemake.readthedocs.io/en/stable/executing/cli.html#dealing-with-very-large-workflows">batches</a> and <a href="https://slurm.schedmd.com/job_array.html">SLURM job arrays</a> to submit reasonably-sized jobs to our HPC queue.
+After running all batches I summarized results for each task,
+and it worked well for a proof of concept.</p>
+<p>But... it was still pretty resource intensive:
+each task was running one query MAG against one metagenome,
+and so each task needed to do all the overhead of starting the Python interpreter and parsing the query signature,
+which is exactly the same for all tasks.
+Extending it to support multiple queries to the same metagenome would involve duplicating tasks,
+and 402k metagenomes times 2,631 MAGs is...
+a very large number of jobs.</p>
+<p>I also wanted to avoid clogging the job queues,
+which is not very nice to the other researchers using the cluster.
+This limited how many batches I could run in parallel...</p>
+<h2>The bottom-up solution: Rust to the rescue!</h2>
+<p>Thinking a bit more about the problem,
+here is another solution:
+what if we load all the MAGs in memory
+(as they will be queried frequently and are not that large),
+and then for each metagenome signature load it,
+perform all MAG queries,
+and then unload the metagenome signature from memory?
+This way we can control memory consumption
+(it's going to be proportional to all the MAG sizes plus the size of the largest metagenome)
+and can also efficiently parallelize the code because each task/metagenome is independent
+and the MAG signatures can be shared freely (since they are read-only).</p>
+<p>This could be done with the sourmash Python API plus <code>multiprocessing</code> or some
+other parallelization approach (maybe dask?),
+but turns out that everything we need comes from the Rust API.
+Why not enjoy a bit of the <a href="https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html">fearless concurrency</a> that is one of the major Rust goals?</p>
+<p><a href="https://github.com/luizirber/phd/blob/aa1ed9eb33ba71fdf9b3f2c92931701be6df00cd/experiments/wort/sra_search/src/main.rs">The whole code</a> ended up being 176 lines long,
+including command line parsing using <a href="https://docs.rs/structopt/latest/structopt/">strucopt</a> and parallelizing the search using <a href="https://docs.rs/rayon/latest/rayon/">rayon</a>
+and a <a href="https://doc.rust-lang.org/std/sync/mpsc/fn.channel.html">multiple-producer, single-consumer channel</a> to write results to an output
+(either the terminal or a file).
+This version took 11 hours to run,
+using less than 5GB of RAM and 32 processors,
+to search 2k MAGs against 402k metagenomes.
+And, bonus! It can also be parallelized again if you have multiple machines,
+so it potentially takes a bit more than an hour to run if you can allocate 10 batch jobs,
+with each batch 1/10 of the metagenome signatures.</p>
+<h2>So, is bottom-up always the better choice?</h2>
+<p>I would like to answer "Yes!",
+but bioinformatics software tends to be organized as command line interfaces,
+not as libraries.
+Libraries also tend to have even less documentation than CLIs,
+and this particular case is not a fair comparison because...
+Well, I wrote most of the library,
+and the Rust API is not that well documented for general use.</p>
+<p>But I'm pretty happy with how the sourmash CLI is viable both for the top-down approach
+(and whatever workflow software you want to use) as well as how the Rust core worked for the bottom-up approach.
+I think the most important is having the option to choose which way to go,
+especially because now I can use the bottom-up approach to make the sourmash CLI
+and Python API better.
+The top-down approach is also way more accessible in general,
+because you can pick your favorite workflow software and use all the tricks you're comfortable with.</p>
+<h2>But, what about the results?!?!?!</h2>
+<p>Next time. But I did find MAGs with over 90% containment in very different locations,
+which is pretty exciting!</p>
+<p>I also need to find a better way of distributing all these signature,
+because storing 4 TB of data in S3 is somewhat cheap,
+but transferring data is very expensive.
+All signatures are also available on IPFS,
+but I need more people to host them and share.
+Get in contact if you're interested in helping =]</p>
+<p>And while I'm asking for help,
+any tips on pulling data faster from the SRA are greatly appreciated!</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://twitter.com/luizirber/status/1285782732790849537">Thread on Twitter</a></li>
+</ul><hr /><h2>Footnotes</h2><ol><li id="sf-mag-search-1"><p>pulling about a 100 TB in 3 days, which was pretty fun to see because I
+ended up DDoS myself because I couldn't download the generated sigs fast enough
+from the S3 bucket where they are temporarily stored =P <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-mag-search-1-back" class="simple-footnote-back">↩</a></p></li></ol>
+ 2020-07-22T15:00:00+00:00
+ luizirber
+
+
+ Gabbleblotchits: Putting it all together
+ https://blog.luizirber.org/2020/05/11/sbt-zip/
+ <p>sourmash <a href="https://twitter.com/ctitusbrown/status/1257418140729868291">3.3</a> was released last week,
+and it is the first version supporting <a href="http://ivory.idyll.org/blog/2020-sourmash-databases-as-zip-files.html">zipped databases</a>.
+Here is my personal account of how that came to be =]</p>
+<h2>What is a sourmash database?</h2>
+<p>A sourmash database contains signatures (typically Scaled MinHash sketches built from genomic datasets) and
+an index for allowing efficient similarity and containment queries over these signatures.
+The two types of index are SBT,
+a hierarchical index that uses less memory by keeping data on disk,
+and LCA,
+an inverted index that uses more memory but is potentially faster.
+Indices are described as JSON files,
+with LCA storing all the data in one JSON file and SBT opting for saving a description of the index structure in JSON,
+and all the data into a hidden directory with many files.</p>
+<p>We distribute some <a href="https://sourmash.readthedocs.io/en/v3.3.0/databases.html">prepared databases</a> (with SBT indices) for Genbank and RefSeq as compressed TAR files.
+The compressed file is ~8GB,
+but after decompressing it turns into almost 200k files in a hidden directory,
+using about 40 GB of disk space.</p>
+<h2>Can we avoid generating so many hidden files?</h2>
+<p>The initial issue in this saga is <a href="https://github.com/dib-lab/sourmash/issues/490">dib-lab/sourmash#490</a>,
+and the idea was to take the existing support for multiple data storages
+(hidden dir,
+TAR files,
+IPFS and Redis) and save the index description in the storage,
+allowing loading everything from the storage.
+Since we already had the databases as TAR files,
+the first test tried to use them but it didn't take long to see it was a doomed approach:
+TAR files are terrible from random access
+(or at least the <code>tarfile</code> module in Python is).</p>
+<p>Zip files showed up as a better alternative,
+and it helps that Python has the <code>zipfile</code> module already available in the
+standard library.
+Initial tests were promising,
+and led to <a href="https://github.com/dib-lab/sourmash/pull/648">dib-lab/sourmash#648</a>.
+The main issue was performance:
+compressing and decompressing was slow,
+but there was also another limitation...</p>
+<h2>Loading Nodegraphs from a memory buffer</h2>
+<p>Another challenge was efficiently loading the data from a storage.
+The two core methods in a storage are <code>save(location, content)</code>,
+where <code>content</code> is a bytes buffer,
+and <code>load(location)</code>,
+which returns a bytes buffer that was previously saved.
+This didn't interact well with the <code>khmer</code> <code>Nodegraph</code>s (the Bloom Filter we use for SBTs),
+since <code>khmer</code> only loads data from files,
+not from memory buffers.
+We ended up doing a temporary file dance,
+which made things slower for the default storage (hidden dir),
+where it could have been optimized to work directly with files,
+and involved interacting with the filesystem for the other storages
+(IPFS and Redis could be pulling data directly from the network,
+for example).</p>
+<p>This one could be fixed in <code>khmer</code> by exposing C++ stream methods,
+and I did a <a href="https://github.com/luizirber/2018-cython-streams">small PoC</a> to test the idea.
+While doable,
+this is something that was happening while the sourmash conversion to Rust was underway,
+and depending on <code>khmer</code> was a problem for my Webassembly aspirations...
+so,
+having the Nodegraph <a href="https://github.com/luizirber/sourmash-rust/pull/15">implemented in Rust</a> seemed like a better direction,
+That has actually been quietly living in the sourmash codebase for quite some time,
+but it was never exposed to the Python (and it was also lacking more extensive
+tests).</p>
+<p>After the release of sourmash 3 and the replacement of the C++ for the Rust implementation,
+all the pieces for exposing the Nodegraph where in place,
+so <a href="https://github.com/dib-lab/sourmash/pull/799">dib-lab/sourmash#799</a> was the next step.
+It wasn't a priority at first because other optimizations
+(that were released in 3.1 and 3.2)
+were more important,
+but then it was time to check how this would perform.
+And...</p>
+<h2>Your Rust code is not so fast, huh?</h2>
+<p>Turns out that my Nodegraph loading code was way slower than <code>khmer</code>.
+The Nodegraph binary format <a href="https://khmer.readthedocs.io/en/latest/dev/binary-file-formats.html#nodegrap://khmer.readthedocs.io/en/latest/dev/binary-file-formats.html#nodegraph">is well documented</a>,
+and doing an initial implementation wasn't so hard by using the <code>byteorder</code> crate
+to read binary data with the right endianess,
+and then setting the appropriate bits in the internal <code>fixedbitset</code> in memory.
+But the khmer code doesn't parse bit by bit:
+it <a href="https://github.com/dib-lab/khmer/blob/fe0ce116456b296c522ba24294a0cabce3b2648b/src/oxli/storage.cc#L233">reads</a> a long <code>char</code> buffer directly,
+and that is many orders of magnitude faster than setting bit by bit.</p>
+<p>And there was no way to replicate this behavior directly with <code>fixedbitset</code>.
+At this point I could either bit-indexing into a large buffer
+and lose all the useful methods that <code>fixedbitset</code> provides,
+or try to find a way to support loading the data directly into <code>fixedbitset</code> and
+open a PR.</p>
+<p><a href="https://github.com/petgraph/fixedbitset/pull/42">I chose the PR</a> (and even got #42! =]).</p>
+<p>It was more straightforward than I expected,
+but it did expose the internal representation of <code>fixedbitset</code>,
+so I was a bit nervous it wasn't going to be merged.
+But <a href="https://github.com/bluss">bluss</a> was super nice,
+and his suggestions made the PR way better!
+This <a href="https://github.com/dib-lab/sourmash/blob/9a695fb03b99c060bb8d1384ab78bb3797c5eb65/src/core/src/sketch/nodegraph.rs#L235L261">simplified</a> the final <code>Nodegraph</code> code,
+and actually was more correct
+(because I was messing a few corner cases when doing the bit-by-bit parsing before).
+Win-win!</p>
+<h2>Nodegraphs are kind of large, can we compress them?</h2>
+<p>Being able to save and load <code>Nodegraph</code>s in Rust allowed using memory buffers,
+but also opened the way to support other operations not supported in khmer <code>Nodegraph</code>s.
+One example is loading/saving compressed files,
+which is supported for <code>Countgraph</code>
+(another khmer data structure,
+based on Count-Min Sketch)
+but not in <code>Nodegraph</code>.</p>
+<p>If only there was an easy way to support working with compressed files...</p>
+<p>Oh wait, there is! <a href="https://github.com/luizirber/niffler">niffler</a> is a crate that I made with <a href="https://twitter.com/pierre_marijon">Pierre Marijon</a> based
+on some functionality I saw in one of his projects,
+and we iterated a bit on the API and documented everything to make it more
+useful for a larger audience.
+<code>niffler</code> tries to be as transparent as possible,
+with very little boilerplate when using it but with useful features nonetheless
+(like auto detection of the compression format).
+If you want more about the motivation and how it happened,
+check this <a href="https://twitter.com/luizirber/status/1253445504622424064">Twitter thread</a>.</p>
+<p>The cool thing is that adding compressed files support in <code>sourmash</code> was mostly
+<a href="https://github.com/dib-lab/sourmash/pull/799/files#diff-313a7ff0fdb14f408a64b3f010f46f65R220">one-line changes</a> for loading
+(and <a href="https://github.com/dib-lab/sourmash/pull/648/files#diff-d80ae1dd777d07300d7b6066b3318397L249-R273">a bit more</a> for saving,
+but mostly because converting compression levels could use some refactoring).</p>
+<h2>Putting it all together: zipped SBT indices</h2>
+<p>With all these other pieces in places,
+it's time to go back to <a href="https://github.com/dib-lab/sourmash/pull/648">dib-lab/sourmash#648</a>.
+Compressing and decompressing with the Python <code>zipfile</code> module is slow,
+but Zip files can also be used just for storage,
+handing back the data without extracting it.
+And since we have compression/decompression implemented in Rust with <code>niffler</code>,
+that's what the zipped sourmash databases are:
+data is loaded and saved into the Zip file without using the Python module
+compression/decompression,
+and all the work is done before (or after) in the Rust side.</p>
+<p>This allows keeping the Zip file with similar sizes to the original TAR files we started with,
+but with very low overhead for decompression.
+For compression we opted for using Gzip level 1,
+which doesn't compress perfectly but also doesn't take much longer to run:</p>
+<table>
+<thead>
+<tr>
+<th>Level</th>
+<th>Size</th>
+<th>Time</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>0</td>
+<td>407 MB</td>
+<td>16s</td>
+</tr>
+<tr>
+<td>1</td>
+<td>252 MB</td>
+<td>21s</td>
+</tr>
+<tr>
+<td>5</td>
+<td>250 MB</td>
+<td>39s</td>
+</tr>
+<tr>
+<td>9</td>
+<td>246 MB</td>
+<td>1m48s</td>
+</tr>
+</tbody>
+</table>
+<p>In this table, <code>0</code> is without compression,
+while <code>9</code> is the best compression.
+The size difference from <code>1</code> to <code>9</code> is only 6 MB (~2% difference)
+but runs 5x faster,
+and it's only 30% slower than saving the uncompressed data.</p>
+<p>The last challenge was updating an existing Zip file.
+It's easy to support appending new data,
+but if any of the already existing data in the file changes
+(which happens when internal nodes change in the SBT,
+after a new dataset is inserted) then there is no easy way to replace the data in the Zip file.
+Worse,
+the Python <code>zipfile</code> will add the new data while keeping the old one around,
+leading to ginormous files over time<sup id="sf-sbt-zip-1-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sbt-zip-1" class="simple-footnote" title="The zipfile module does throw a UserWarning pointing that duplicated files were inserted, which is useful during development but generally doesn't show during regular usage...">1</a></sup>
+So, what to do?</p>
+<p>I ended up opting for dealing with the complexity and <a href="https://github.com/dib-lab/sourmash/pull/648/files#diff-a99b088adcc872e1b408fbdcca20ebebR110-R248">complicating the ZipStorage</a> implementation a bit,
+by keeping a buffer for new data.
+If it's a new file or it already exists but there are no insertions
+the buffer is ignored and all works as before.</p>
+<p>If the file exists and new data is inserted,
+then it is first stored in the buffer
+(where it might also replace a previous entry with the same name).
+In this case we also need to check the buffer when trying to load some data
+(because it might exist only in the buffer,
+and not in the original file).</p>
+<p>Finally,
+when the <code>ZipStorage</code> is closed it needs to verify if there are new items in the buffer.
+If not,
+it is safe just to close the original file.
+If there are new items but they were not present in the original file,
+then we can append the new data to the original file.
+The final case is if there are new items that were also in the original file,
+and in this case a new Zip file is created and all the content from buffer and
+original file are copied to it,
+prioritizing items from the buffer.
+The original file is replaced by the new Zip file.</p>
+<p>Turns out this worked quite well! And so the PR was merged =]</p>
+<h2>The future</h2>
+<p>Zipped databases open the possibility of distributing extra data that might be
+useful for some kinds of analysis.
+One thing we are already considering is adding <a href="https://github.com/dib-lab/sourmash/issues/969">taxonomy information</a>,
+let's see what else shows up.</p>
+<p>Having <code>Nodegraph</code> in Rust is also pretty exciting,
+because now we can change the internal representation for something that uses
+less memory (maybe using <a href="https://alexbowe.com/rrr/">RRR encoding</a>?),
+but more importantly:
+now they can also be used with Webassembly,
+which opens many possibilities for running not only <a href="https://blog.luizirber.org/2018/08/27/sourmash-wasm/">signature computation</a> but
+also <code>search</code> and <code>gather</code> in the browser,
+since now we have all the pieces to build it.</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://twitter.com/luizirber/status/1260031886744621059">Thread on Twitter</a></li>
+</ul><hr /><h2>Footnotes</h2><ol><li id="sf-sbt-zip-1"><p>The <code>zipfile</code> module does throw a <code>UserWarning</code> pointing that duplicated files were inserted,
+which is useful during development but generally doesn't show during regular usage... <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sbt-zip-1-back" class="simple-footnote-back">↩</a></p></li></ol>
+ 2020-05-11T15:00:00+00:00
+ luizirber
+
+
+ PythonClub: Criando um CI de uma aplicação Django usando Github Actions
+ https://pythonclub.com.br/django-ci-github-actions.html
+ <p>Fala pessoal, tudo bom?</p>
+<p>Nos vídeo abaixo vou mostrar como podemos configurar um CI de uma aplicação Django usando Github Actions.</p>
+<p><a class="reference external" href="https://www.youtube.com/watch?v=KpSlY8leYFY">https://www.youtube.com/watch?v=KpSlY8leYFY</a>.</p>
+<div class="youtube youtube-16x9"></div>
+ 2020-01-24T15:10:00+00:00
+ Lucas Magnum
+
+
+ Gabbleblotchits: Oxidizing sourmash: PR walkthrough
+ https://blog.luizirber.org/2020/01/10/sourmash-pr/
+ <p>sourmash 3 was released last week,
+finally landing the Rust backend.
+But, what changes when developing new features in sourmash?
+I was thinking about how to best document this process,
+and since <a href="https://github.com/dib-lab/sourmash/pull/826/files">PR #826</a> is a short example touching all the layers I decided to do a
+small walkthrough.</p>
+<p>Shall we?</p>
+<h2>The problem</h2>
+<p>The first step is describing the problem,
+and trying to convince reviewers (and yourself) that the changes bring enough benefits to justify a merge.
+This is the description I put in the PR:</p>
+<blockquote>
+<p>Calling <code>.add_hash()</code> on a MinHash sketch is fine,
+but if you're calling it all the time it's better to pass a list of hashes and call <code>.add_many()</code> instead.
+Before this PR <code>add_many</code> just called <code>add_hash</code> for each hash it was passed,
+but now it will pass the full list to Rust (and that's way faster).</p>
+<p>No changes for public APIs,
+and I changed the <code>_signatures</code> method in LCA to accumulate hashes for each sig first,
+and then set them all at once.
+This is way faster,
+but might use more intermediate memory (I'll evaluate this now).</p>
+</blockquote>
+<p>There are many details that sound like jargon for someone not familiar with the codebase,
+but if I write something too long I'll probably be wasting the reviewers time too.
+The benefit of a very detailed description is extending the knowledge for other people
+(not necessarily the maintainers),
+but that also takes effort that might be better allocated to solve other problems.
+Or, more realistically, putting out other fires =P</p>
+<p>Nonetheless,
+some points I like to add in PR descriptions:
+- why is there a problem with the current approach?
+- is this the minimal viable change, or is it trying to change too many things
+ at once? The former is way better, in general.
+- what are the trade-offs? This PR is using more memory to lower the runtime,
+ but I hadn't measure it yet when I opened it.
+- Not changing public APIs is always good to convince reviewers.
+ If the project follows a <a href="https://semver.org/">semantic versioning</a> scheme,
+ changes to the public APIs are major version bumps,
+ and that can brings other consequences for users.</p>
+<h2>Setting up for changing code</h2>
+<p>If this was a bug fix PR,
+the first thing I would do is write a new test triggering the bug,
+and then proceed to fix it in the code
+(Hmm, maybe that would be another good walkthrough?).
+But this PR is making performance claims ("it's going to be faster"),
+and that's a bit hard to codify in tests.
+<sup id="sf-sourmash-pr-1-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-1" class="simple-footnote" title="We do have https://asv.readthedocs.io/ set up for micro-benchmarks, and now that I think about it... I could have started by writing a benchmark for add_many, and then showing that it is faster. I will add this approach to the sourmash PR checklist =]">1</a></sup>
+Since it's also proposing to change a method (<code>_signatures</code> in LCA indices) that is better to benchmark with a real index (and not a toy example),
+I used the same data and command I run in <a href="https://github.com/luizirber/sourmash_resources">sourmash_resources</a> to check how memory consumption and runtime changed.
+For reference, this is the command: </p>
+<div class="highlight"><pre><span></span>sourmash search -o out.csv --scaled <span class="m">2000</span> -k <span class="m">51</span> HSMA33OT.fastq.gz.sig genbank-k51.lca.json.gz
+</pre></div>
+
+
+<p>I'm using the <code>benchmark</code> feature from <a href="https://snakemake.readthedocs.io/">snakemake</a> in <a href="https://github.com/luizirber/sourmash_resources/blob/83ea237397d242e48c9d95eb0d9f50ceb4ad95c7/Snakefile#L99L114">sourmash_resources</a> to
+track how much memory, runtime and I/O is used for each command (and version) of sourmash,
+and generate the plots in the README in that repo.
+That is fine for a high-level view ("what's the maximum memory used?"),
+but not so useful for digging into details ("what method is consuming most memory?").</p>
+<p>Another additional problem is the dual<sup id="sf-sourmash-pr-2-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-2" class="simple-footnote" title="or triple, if you count C">2</a></sup> language nature of sourmash,
+where we have Python calling into Rust code (via CFFI).
+There are great tools for measuring and profiling Python code,
+but they tend to not work with extension code...</p>
+<p>So, let's bring two of my favorite tools to help!</p>
+<h3>Memory profiling: heaptrack</h3>
+<p><a href="https://github.com/KDE/heaptrack">heaptrack</a> is a heap profiler, and I first heard about it from <a href="https://www.vincentprouillet.com/">Vincent Prouillet</a>.
+Its main advantage over other solutions (like valgrind's massif) is the low
+overhead and... how easy it is to use:
+just stick <code>heaptrack</code> in front of your command,
+and you're good to go!</p>
+<p>Example output:</p>
+<div class="highlight"><pre><span></span>$ heaptrack sourmash search -o out.csv --scaled <span class="m">2000</span> -k <span class="m">51</span> HSMA33OT.fastq.gz.sig genbank-k51.lca.json.gz
+
+heaptrack stats:
+ allocations: <span class="m">1379353</span>
+ leaked allocations: <span class="m">1660</span>
+ temporary allocations: <span class="m">168984</span>
+Heaptrack finished! Now run the following to investigate the data:
+
+ heaptrack --analyze heaptrack.sourmash.66565.gz
+</pre></div>
+
+
+<p><code>heaptrack --analyze</code> is a very nice graphical interface for analyzing the results,
+but for this PR I'm mostly focusing on the Summary page (and overall memory consumption).
+Tracking allocations in Python doesn't give many details,
+because it shows the CPython functions being called,
+but the ability to track into the extension code (Rust) allocations is amazing
+for finding bottlenecks (and memory leaks =P).
+<sup id="sf-sourmash-pr-3-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-3" class="simple-footnote" title="It would be super cool to have the unwinding code from py-spy in heaptrack, and be able to see exactly what Python methods/lines of code were calling the Rust parts...">3</a></sup></p>
+<h3>CPU profiling: py-spy</h3>
+<p>Just as other solutions exist for profiling memory,
+there are many for profiling CPU usage in Python,
+including <code>profile</code> and <code>cProfile</code> in the standard library.
+Again, the issue is being able to analyze extension code,
+and bringing the cannon (the <code>perf</code> command in Linux, for example) looses the
+benefit of tracking Python code properly (because we get back the CPython
+functions, not what you defined in your Python code).</p>
+<p>Enters <a href="https://github.com/benfred/py-spy">py-spy</a> by <a href="https://www.benfrederickson.com">Ben Frederickson</a>,
+based on the <a href="https://github.com/rbspy/rbspy">rbspy</a> project by <a href="https://jvns.ca">Julia Evans</a>.
+Both use a great idea:
+read the process maps for the interpreters and resolve the full stack trace information,
+with low overhead (because it uses sampling).
+<a href="https://github.com/benfred/py-spy">py-spy</a> also goes further and resolves <a href="https://www.benfrederickson.com/profiling-native-python-extensions-with-py-spy/">native Python extensions</a> stack traces,
+meaning we can get the complete picture all the way from the Python CLI to the
+Rust core library!<sup id="sf-sourmash-pr-4-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-4" class="simple-footnote" title="Even if py-spy doesn't talk explicitly about Rust, it works very very well, woohoo!">4</a></sup></p>
+<p><code>py-spy</code> is also easy to use:
+stick <code>py-spy record --output search.svg -n --</code> in front of the command,
+and it will generate a flamegraph in <code>search.svg</code>.
+The full command for this PR is</p>
+<div class="highlight"><pre><span></span>py-spy record --output search.svg -n -- sourmash search -o out.csv --scaled <span class="m">2000</span> -k <span class="m">51</span> HSMA.fastq.sig genbank-k51.lca.json.gz
+</pre></div>
+
+
+<h2>Show me the code!</h2>
+<p>OK, OK, sheesh. But it's worth repeating: the code is important, but there are
+many other aspects that are just as important =]</p>
+<h3>Replacing <code>add_hash</code> calls with one <code>add_many</code></h3>
+<p>Let's start at the <a href="https://github.com/dib-lab/sourmash/pull/826/files#diff-adf06d14c535d5b22da9fb3862e4a487"><code>_signatures()</code></a> method on LCA indices.
+This is the original method:</p>
+<div class="highlight"><pre><span></span><span class="nd">@cached_property</span>
+<span class="k">def</span> <span class="nf">_signatures</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="s2">"Create a _signatures member dictionary that contains {idx: minhash}."</span>
+ <span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">MinHash</span>
+
+ <span class="n">minhash</span> <span class="o">=</span> <span class="n">MinHash</span><span class="p">(</span><span class="n">n</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">ksize</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">ksize</span><span class="p">,</span> <span class="n">scaled</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">scaled</span><span class="p">)</span>
+
+ <span class="n">debug</span><span class="p">(</span><span class="s1">'creating signatures for LCA DB...'</span><span class="p">)</span>
+ <span class="n">sigd</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="n">minhash</span><span class="o">.</span><span class="n">copy_and_clear</span><span class="p">)</span>
+
+ <span class="k">for</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">hashval_to_idx</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="k">for</span> <span class="n">vv</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span>
+ <span class="n">sigd</span><span class="p">[</span><span class="n">vv</span><span class="p">]</span><span class="o">.</span><span class="n">add_hash</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
+
+ <span class="n">debug</span><span class="p">(</span><span class="s1">'=> </span><span class="si">{}</span><span class="s1"> signatures!'</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">sigd</span><span class="p">))</span>
+ <span class="k">return</span> <span class="n">sigd</span>
+</pre></div>
+
+
+<p><code>sigd[vv].add_hash(k)</code> is the culprit.
+Each call to <code>.add_hash</code> has to go thru CFFI to reach the extension code,
+and the overhead is significant.
+It is a similar situation to accessing array elements in NumPy:
+it works,
+but it is way slower than using operations that avoid crossing from Python to
+the extension code.
+What we want to do instead is call <code>.add_many(hashes)</code>,
+which takes a list of hashes and process it entirely in Rust
+(ideally. We will get there).</p>
+<p>But, to have a list of hashes, there is another issue with this code.</p>
+<div class="highlight"><pre><span></span><span class="k">for</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">hashval_to_idx</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="k">for</span> <span class="n">vv</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span>
+ <span class="n">sigd</span><span class="p">[</span><span class="n">vv</span><span class="p">]</span><span class="o">.</span><span class="n">add_hash</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
+</pre></div>
+
+
+<p>There are two nested for loops,
+and <code>add_hash</code> is being called with values from the inner loop.
+So... we don't have the list of hashes beforehand.</p>
+<p>But we can change the code a bit to save the hashes for each signature
+in a temporary list,
+and then call <code>add_many</code> on the temporary list.
+Like this:</p>
+<div class="highlight"><pre><span></span><span class="n">temp_vals</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span>
+
+<span class="k">for</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">hashval_to_idx</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="k">for</span> <span class="n">vv</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span>
+ <span class="n">temp_vals</span><span class="p">[</span><span class="n">vv</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
+
+<span class="k">for</span> <span class="n">sig</span><span class="p">,</span> <span class="n">vals</span> <span class="ow">in</span> <span class="n">temp_vals</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="n">sigd</span><span class="p">[</span><span class="n">sig</span><span class="p">]</span><span class="o">.</span><span class="n">add_many</span><span class="p">(</span><span class="n">vals</span><span class="p">)</span>
+</pre></div>
+
+
+<p>There is a trade-off here:
+if we save the hashes in temporary lists,
+will the memory consumption be so high that the runtime gains of calling
+<code>add_many</code> in these temporary lists be cancelled?</p>
+<p>Time to measure it =]</p>
+<table>
+<thead>
+<tr>
+<th align="left">version</th>
+<th align="left">mem</th>
+<th align="left">time</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td align="left">original</td>
+<td align="left">1.5 GB</td>
+<td align="left">160s</td>
+</tr>
+<tr>
+<td align="left"><code>list</code></td>
+<td align="left">1.7GB</td>
+<td align="left">173s</td>
+</tr>
+</tbody>
+</table>
+<p>Wait, it got worse?!?! Building temporary lists only takes time and memory,
+and bring no benefits!</p>
+<p>This mystery goes away when you look at the <a href="https://github.com/dib-lab/sourmash/pull/826/files#diff-2f53b2a5be4083c39a0275847c87f88fR190">add_many method</a>:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">add_many</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">hashes</span><span class="p">):</span>
+ <span class="s2">"Add many hashes in at once."</span>
+ <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">hashes</span><span class="p">,</span> <span class="n">MinHash</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_methodcall</span><span class="p">(</span><span class="n">lib</span><span class="o">.</span><span class="n">kmerminhash_add_from</span><span class="p">,</span> <span class="n">hashes</span><span class="o">.</span><span class="n">_objptr</span><span class="p">)</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="k">for</span> <span class="nb">hash</span> <span class="ow">in</span> <span class="n">hashes</span><span class="p">:</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_methodcall</span><span class="p">(</span><span class="n">lib</span><span class="o">.</span><span class="n">kmerminhash_add_hash</span><span class="p">,</span> <span class="nb">hash</span><span class="p">)</span>
+</pre></div>
+
+
+<p>The first check in the <code>if</code> statement is a shortcut for adding hashes from
+another <code>MinHash</code>, so let's focus on <code>else</code> part...
+And turns out that <code>add_many</code> is lying!
+It doesn't process the <code>hashes</code> in the Rust extension,
+but just loops and call <code>add_hash</code> for each <code>hash</code> in the list.
+That's not going to be any faster than what we were doing in <code>_signatures</code>.</p>
+<p>Time to fix <code>add_many</code>!</p>
+<h3>Oxidizing <code>add_many</code></h3>
+<p>The idea is to change this loop in <code>add_many</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">for</span> <span class="nb">hash</span> <span class="ow">in</span> <span class="n">hashes</span><span class="p">:</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_methodcall</span><span class="p">(</span><span class="n">lib</span><span class="o">.</span><span class="n">kmerminhash_add_hash</span><span class="p">,</span> <span class="nb">hash</span><span class="p">)</span>
+</pre></div>
+
+
+<p>with a call to a Rust extension function:</p>
+<div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">_methodcall</span><span class="p">(</span><span class="n">lib</span><span class="o">.</span><span class="n">kmerminhash_add_many</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">hashes</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">hashes</span><span class="p">))</span>
+</pre></div>
+
+
+<p><code>self._methodcall</code> is a convenience method defined in <a href="https://github.com/dib-lab/sourmash/blob/c6cbdf0398ef836797492e13371a38373c544ae1/sourmash/utils.py#L24">RustObject</a>
+which translates a method-like call into a function call,
+since our C layer only has functions.
+This is the C prototype for this function:</p>
+<div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">kmerminhash_add_many</span><span class="p">(</span><span class="w"></span>
+<span class="w"> </span><span class="n">KmerMinHash</span><span class="w"> </span><span class="o">*</span><span class="n">ptr</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">uint64_t</span><span class="w"> </span><span class="o">*</span><span class="n">hashes_ptr</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="kt">uintptr_t</span><span class="w"> </span><span class="n">insize</span><span class="w"></span>
+<span class="w"> </span><span class="p">);</span><span class="w"></span>
+</pre></div>
+
+
+<p>You can almost read it as a Python method declaration,
+where <code>KmerMinHash *ptr</code> means the same as the <code>self</code> in Python methods.
+The other two arguments are a common idiom when passing pointers to data in C,
+with <code>insize</code> being how many elements we have in the list.
+<sup id="sf-sourmash-pr-5-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-5" class="simple-footnote" title="Let's not talk about lack of array bounds checks in C...">5</a></sup>.
+<code>CFFI</code> is very good at converting Python lists into pointers of a specific type,
+as long as the type is of a primitive type
+(<code>uint64_t</code> in our case, since each hash is a 64-bit unsigned integer number).</p>
+<p>And the Rust code with the implementation of the function:</p>
+<div class="highlight"><pre><span></span><span class="n">ffi_fn</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">kmerminhash_add_many</span><span class="p">(</span><span class="w"></span>
+<span class="w"> </span><span class="n">ptr</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">KmerMinHash</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">hashes_ptr</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="kt">u64</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">insize</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="p">)</span><span class="w"> </span>-> <span class="nb">Result</span><span class="o"><</span><span class="p">()</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">mh</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="o">!</span><span class="n">ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">());</span><span class="w"></span>
+<span class="w"> </span><span class="o">&</span><span class="k">mut</span><span class="w"> </span><span class="o">*</span><span class="n">ptr</span><span class="w"></span>
+<span class="w"> </span><span class="p">};</span><span class="w"></span>
+
+<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">hashes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="o">!</span><span class="n">hashes_ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">());</span><span class="w"></span>
+<span class="w"> </span><span class="n">slice</span>::<span class="n">from_raw_parts</span><span class="p">(</span><span class="n">hashes_ptr</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u64</span><span class="p">,</span><span class="w"> </span><span class="n">insize</span><span class="p">)</span><span class="w"></span>
+<span class="w"> </span><span class="p">};</span><span class="w"></span>
+
+<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">hash</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">hashes</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="n">mh</span><span class="p">.</span><span class="n">add_hash</span><span class="p">(</span><span class="o">*</span><span class="n">hash</span><span class="p">);</span><span class="w"></span>
+<span class="w"> </span><span class="p">}</span><span class="w"></span>
+
+<span class="w"> </span><span class="nb">Ok</span><span class="p">(())</span><span class="w"></span>
+<span class="p">}</span><span class="w"></span>
+<span class="p">}</span><span class="w"></span>
+</pre></div>
+
+
+<p>Let's break what's happening here into smaller pieces.
+Starting with the function signature:</p>
+<div class="highlight"><pre><span></span><span class="n">ffi_fn</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">kmerminhash_add_many</span><span class="p">(</span><span class="w"></span>
+<span class="w"> </span><span class="n">ptr</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">KmerMinHash</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">hashes_ptr</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="kt">u64</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">insize</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="p">)</span><span class="w"> </span>-> <span class="nb">Result</span><span class="o"><</span><span class="p">()</span><span class="o">></span><span class="w"></span>
+</pre></div>
+
+
+<p>The weird <code>ffi_fn! {}</code> syntax around the function is a macro in Rust:
+it changes the final generated code to convert the return value (<code>Result<()></code>) into something that is valid C code (in this case, <code>void</code>).
+What happens if there is an error, then?
+The Rust extension has code for passing back an error code and message to Python,
+as well as capturing panics (when things go horrible bad and the program can't recover)
+in a way that Python can then deal with (raising exceptions and cleaning up).
+It also sets the <code>#[no_mangle]</code> attribute in the function,
+meaning that the final name of the function will follow C semantics (instead of Rust semantics),
+and can be called more easily from C and other languages.
+This <code>ffi_fn!</code> macro comes from <a href="https://github.com/getsentry/symbolic">symbolic</a>,
+a big influence on the design of the Python/Rust bridge in sourmash.</p>
+<p><code>unsafe</code> is the keyword in Rust to disable some checks in the code to allow
+potentially dangerous things (like dereferencing a pointer),
+and it is required to interact with C code.
+<code>unsafe</code> doesn't mean that the code is always unsafe to use:
+it's up to whoever is calling this to verify that valid data is being passed and invariants are being preserved.</p>
+<p>If we remove the <code>ffi_fn!</code> macro and the <code>unsafe</code> keyword,
+we have</p>
+<div class="highlight"><pre><span></span><span class="k">fn</span> <span class="nf">kmerminhash_add_many</span><span class="p">(</span><span class="w"></span>
+<span class="w"> </span><span class="n">ptr</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">KmerMinHash</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">hashes_ptr</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="kt">u64</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">insize</span>: <span class="kt">usize</span>
+ <span class="p">);</span><span class="w"></span>
+</pre></div>
+
+
+<p>At this point we can pretty much map between Rust and the C function prototype:</p>
+<div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">kmerminhash_add_many</span><span class="p">(</span><span class="w"></span>
+<span class="w"> </span><span class="n">KmerMinHash</span><span class="w"> </span><span class="o">*</span><span class="n">ptr</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">uint64_t</span><span class="w"> </span><span class="o">*</span><span class="n">hashes_ptr</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="kt">uintptr_t</span><span class="w"> </span><span class="n">insize</span><span class="w"></span>
+<span class="w"> </span><span class="p">);</span><span class="w"></span>
+</pre></div>
+
+
+<p>Some interesting points:</p>
+<ul>
+<li>We use <code>fn</code> to declare a function in Rust.</li>
+<li>The type of an argument comes after the name of the argument in Rust,
+ while it's the other way around in C.
+ Same for the return type (it is omitted in the Rust function, which means it
+ is <code>-> ()</code>, equivalent to a <code>void</code> return type in C).</li>
+<li>In Rust everything is <strong>immutable</strong> by default, so we need to say that we want
+ a mutable pointer to a <code>KmerMinHash</code> item: <code>*mut KmerMinHash</code>).
+ In C everything is mutable by default.</li>
+<li><code>u64</code> in Rust -> <code>uint64_t</code> in C</li>
+<li><code>usize</code> in Rust -> <code>uintptr_t</code> in C</li>
+</ul>
+<p>Let's check the implementation of the function now.
+We start by converting the <code>ptr</code> argument (a raw pointer to a <code>KmerMinHash</code> struct)
+into a regular Rust struct:</p>
+<div class="highlight"><pre><span></span><span class="kd">let</span><span class="w"> </span><span class="n">mh</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="o">!</span><span class="n">ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">());</span><span class="w"></span>
+<span class="w"> </span><span class="o">&</span><span class="k">mut</span><span class="w"> </span><span class="o">*</span><span class="n">ptr</span><span class="w"></span>
+<span class="p">};</span><span class="w"></span>
+</pre></div>
+
+
+<p>This block is asserting that <code>ptr</code> is not a null pointer,
+and if so it dereferences it and store in a mutable reference.
+If it was a null pointer the <code>assert!</code> would panic (which might sound extreme,
+but is way better than continue running because dereferencing a null pointer is
+BAD).
+Note that functions always need all the types in arguments and return values,
+but for variables in the body of the function
+Rust can figure out types most of the time,
+so no need to specify them.</p>
+<p>The next block prepares our list of hashes for use:</p>
+<div class="highlight"><pre><span></span><span class="kd">let</span><span class="w"> </span><span class="n">hashes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="o">!</span><span class="n">hashes_ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">());</span><span class="w"></span>
+<span class="w"> </span><span class="n">slice</span>::<span class="n">from_raw_parts</span><span class="p">(</span><span class="n">hashes_ptr</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u64</span><span class="p">,</span><span class="w"> </span><span class="n">insize</span><span class="p">)</span><span class="w"></span>
+<span class="p">};</span><span class="w"></span>
+</pre></div>
+
+
+<p>We are again asserting that the <code>hashes_ptr</code> is not a null pointer,
+but instead of dereferencing the pointer like before we use it to create a <code>slice</code>,
+a dynamically-sized view into a contiguous sequence.
+The list we got from Python is a contiguous sequence of size <code>insize</code>,
+and the <code>slice::from_raw_parts</code> function creates a slice from a pointer to data and a size.</p>
+<p>Oh, and can you spot the bug?
+I created the slice using <code>*mut u64</code>,
+but the data is declared as <code>*const u64</code>.
+Because we are in an <code>unsafe</code> block Rust let me change the mutability,
+but I shouldn't be doing that,
+since we don't need to mutate the slice.
+Oops.</p>
+<p>Finally, let's add hashes to our MinHash!
+We need a <code>for</code> loop, and call <code>add_hash</code> for each <code>hash</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">for</span><span class="w"> </span><span class="n">hash</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">hashes</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="n">mh</span><span class="p">.</span><span class="n">add_hash</span><span class="p">(</span><span class="o">*</span><span class="n">hash</span><span class="p">);</span><span class="w"></span>
+<span class="p">}</span><span class="w"></span>
+
+<span class="nb">Ok</span><span class="p">(())</span><span class="w"></span>
+</pre></div>
+
+
+<p>We finish the function with <code>Ok(())</code> to indicate no errors occurred.</p>
+<p>Why is calling <code>add_hash</code> here faster than what we were doing before in Python?
+Rust can optimize these calls and generate very efficient native code,
+while Python is an interpreted language and most of the time don't have the same
+guarantees that Rust can leverage to generate the code.
+And, again,
+calling <code>add_hash</code> here doesn't need to cross FFI boundaries or,
+in fact,
+do any dynamic evaluation during runtime,
+because it is all statically analyzed during compilation.</p>
+<h2>Putting it all together</h2>
+<p>And... that's the PR code.
+There are some other unrelated changes that should have been in new PRs,
+but since they were so small it would be more work than necessary.
+OK, that's a lame excuse:
+it's confusing for reviewers to see these changes here,
+so avoid doing that if possible!</p>
+<p>But, did it work?</p>
+<table>
+<thead>
+<tr>
+<th align="left">version</th>
+<th align="left">mem</th>
+<th align="left">time</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td align="left">original</td>
+<td align="left">1.5 GB</td>
+<td align="left">160s</td>
+</tr>
+<tr>
+<td align="left"><code>list</code></td>
+<td align="left">1.7GB</td>
+<td align="left">73s</td>
+</tr>
+</tbody>
+</table>
+<p>We are using 200 MB of extra memory,
+but taking less than half the time it was taking before.
+I think this is a good trade-off,
+and so did the <a href="https://github.com/dib-lab/sourmash/pull/826#pullrequestreview-339020803">reviewer</a> and the PR was approved.</p>
+<p>Hopefully this was useful, 'til next time!</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/103461534713587975">Thread on Mastodon</a></li>
+<li><a href="https://twitter.com/luizirber/status/1215772245928235008">Thread on Twitter</a></li>
+<li><a href="https://lobste.rs/s/xnaugq/oxidizing_sourmash_pr_walkthrough">Lobste.rs submission</a></li>
+</ul>
+<h2>Bonus: <code>list</code> or <code>set</code>?</h2>
+<p>The first version of the PR used a <code>set</code> instead of a <code>list</code> to accumulate hashes.
+Since a <code>set</code> doesn't have repeated elements,
+this could potentially use less memory.
+The code:</p>
+<div class="highlight"><pre><span></span><span class="n">temp_vals</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">set</span><span class="p">)</span>
+
+<span class="k">for</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">hashval_to_idx</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="k">for</span> <span class="n">vv</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span>
+ <span class="n">temp_vals</span><span class="p">[</span><span class="n">vv</span><span class="p">]</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
+
+<span class="k">for</span> <span class="n">sig</span><span class="p">,</span> <span class="n">vals</span> <span class="ow">in</span> <span class="n">temp_vals</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="n">sigd</span><span class="p">[</span><span class="n">sig</span><span class="p">]</span><span class="o">.</span><span class="n">add_many</span><span class="p">(</span><span class="n">vals</span><span class="p">)</span>
+</pre></div>
+
+
+<p>The runtime was again half of the original,
+but...</p>
+<table>
+<thead>
+<tr>
+<th align="left">version</th>
+<th align="left">mem</th>
+<th align="left">time</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td align="left">original</td>
+<td align="left">1.5 GB</td>
+<td align="left">160s</td>
+</tr>
+<tr>
+<td align="left"><code>set</code></td>
+<td align="left">3.8GB</td>
+<td align="left">80s</td>
+</tr>
+<tr>
+<td align="left"><code>list</code></td>
+<td align="left">1.7GB</td>
+<td align="left">73s</td>
+</tr>
+</tbody>
+</table>
+<p>... memory consumption was almost 2.5 times the original! WAT</p>
+<p>The culprit this time? The new <code>kmerminhash_add_many</code> call in the <code>add_many</code>
+method.
+This one:</p>
+<div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">_methodcall</span><span class="p">(</span><span class="n">lib</span><span class="o">.</span><span class="n">kmerminhash_add_many</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">hashes</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">hashes</span><span class="p">))</span>
+</pre></div>
+
+
+<p><code>CFFI</code> doesn't know how to convert a <code>set</code> into something that C understands,
+so we need to call <code>list(hashes)</code> to convert it into a list.
+Since Python (and <code>CFFI</code>) can't know if the data is going to be used later
+<sup id="sf-sourmash-pr-6-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-6" class="simple-footnote" title="something that the memory ownership model in Rust does, BTW">6</a></sup>
+it needs to keep it around
+(and be eventually deallocated by the garbage collector).
+And that's how we get at least double the memory being allocated...</p>
+<p>There is another lesson here.
+If we look at the <code>for</code> loop again:</p>
+<div class="highlight"><pre><span></span><span class="k">for</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">hashval_to_idx</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="k">for</span> <span class="n">vv</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span>
+ <span class="n">temp_vals</span><span class="p">[</span><span class="n">vv</span><span class="p">]</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
+</pre></div>
+
+
+<p>each <code>k</code> is already unique because they are keys in the <code>hashval_to_idx</code> dictionary,
+so the initial assumption
+(that a <code>set</code> might save memory because it doesn't have repeated elements)
+is... irrelevant for the problem =]</p><hr /><h2>Footnotes</h2><ol><li id="sf-sourmash-pr-1"><p>We do have https://asv.readthedocs.io/ set up for micro-benchmarks,
+and now that I think about it...
+I could have started by writing a benchmark for <code>add_many</code>,
+and then showing that it is faster.
+I will add this approach to the sourmash PR checklist =] <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-1-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-pr-2"><p>or triple, if you count C <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-2-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-pr-3"><p>It would be super cool to have the unwinding code from py-spy in heaptrack,
+and be able to see exactly what Python methods/lines of code were calling the
+Rust parts... <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-3-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-pr-4"><p>Even if py-spy doesn't talk explicitly about Rust,
+it works very very well, woohoo! <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-4-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-pr-5"><p>Let's not talk about lack of array bounds checks in C... <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-5-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-pr-6"><p>something that the memory ownership model in Rust does, BTW <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-6-back" class="simple-footnote-back">↩</a></p></li></ol>
+ 2020-01-10T15:00:00+00:00
+ luizirber
+
+
+ Gabbleblotchits: Interoperability #rust2020
+ https://blog.luizirber.org/2019/12/01/rust-2020/
+ <p>In January I wrote a <a href="https://blog.luizirber.org/2019/01/05/rust-2019/">post</a> for the Rust 2019 call for blogs.
+The <a href="https://blog.rust-lang.org/2019/10/29/A-call-for-blogs-2020.html">2020 call</a> is aiming for an RFC and roadmap earlier this time,
+so here is my 2020 post =]</p>
+<h3>Last call review: what happened?</h3>
+<h4>An attribute proc-macro like <code>#[wasm_bindgen]</code> but for FFI</h4>
+<p>This sort of happened... because WebAssembly is growing =]</p>
+<p>I was very excited when <a href="https://hacks.mozilla.org/2019/08/webassembly-interface-types/">Interface Types</a> showed up in August,
+and while it is still very experimental it is moving fast and bringing saner
+paths for interoperability than raw C FFIs.
+David Beazley even point this at the end of his <a href="https://www.youtube.com/watch?v=r-A78RgMhZU">PyCon India keynote</a>,
+talking about how easy is to get information out of a WebAssembly module
+compared to what had to be done for SWIG.</p>
+<p>This doesn't solve the problem where strict C compatibility is required,
+or for platforms where a WebAssembly runtime is not available,
+but I think it is a great solution for scientific software
+(or, at least, for my use cases =]).</p>
+<h4>"More -sys and Rust-like crates for interoperability with the larger ecosystems" and "More (bioinformatics) tools using Rust!"</h4>
+<p>I did some of those this year (<a href="https://crates.io/crates/bbhash-sys">bbhash-sys</a> and <a href="https://crates.io/crates/mqf">mqf</a>),
+and also found some great crates to use in my projects.
+Rust is picking up steam in bioinformatics,
+being used as the primary choice for high quality software
+(like <a href="https://varlociraptor.github.io/">varlociraptor</a>,
+or the many coming from <a href="https://github.com/10XGenomics/">10X Genomics</a>)
+but it is still somewhat hard to find more details
+(I mostly find it on Twitter,
+and sometime Google Scholar alerts).
+It would be great to start bringing this info together,
+which leads to...</p>
+<h4>"A place to find other scientists?"</h4>
+<p>Hey, this one happened! <a href="https://twitter.com/algo_luca/status/1081966759048028162">Luca Palmieri</a> started a conversation on <a href="https://www.reddit.com/r/rust/comments/ae77gt/scientific_computingmachine_learning_do_we_want_a/">reddit</a> and
+the <a href="https://discord.gg/EXTSq4v">#science-and-ai</a> Discord channel on the Rust community server was born!
+I think it works pretty well,
+and Luca also has being doing a great job running <a href="https://github.com/LukeMathWalker/ndarray-koans">workshops</a>
+and guiding the conversation around <a href="https://github.com/rust-ml/discussion">rust-ml</a>.</p>
+<h2>Rust 2021: Interoperability</h2>
+<p>Rust is amazing because it is very good at bringing many concepts and ideas that
+seem contradictory at first,
+but can really shine when <a href="https://rust-lang.github.io/rustconf-2018-keynote/#127">synthesized</a>.
+But can we share this combined wisdom and also improve the situation in other
+places too?
+Despite the "Rewrite it in Rust" meme,
+increased interoperability is something that is already driving a lot of the
+best aspects of Rust:</p>
+<ul>
+<li>
+<p>Interoperability with other languages: as I said before,
+ with WebAssembly (and Rust being having the best toolchain for it)
+ there is a clear route to achieve this,
+ but it will not replace all the software that already exist and can benefit
+ from FFI and C compatibility.
+ Bringing together developers from the many language specific binding
+ generators (<a href="https://github.com/tildeio/helix">helix</a>, <a href="https://github.com/neon-bindings/neon">neon</a>, <a href="https://github.com/rusterlium/rustler">rustler</a>, <a href="https://github.com/PyO3/pyo3">PyO3</a>...) and figuring out what's missing from
+ them (or what is the common parts that can be shared) also seems productive.</p>
+</li>
+<li>
+<p>Interoperability with new and unexplored domains.
+ I think Rust benefits enormously from not focusing only in one domain,
+ and choosing to prioritize CLI, WebAssembly, Networking and Embedded is a good
+ subset to start tackling problems,
+ but how to guide other domains to also use Rust and come up with new
+ contributors and expose missing pieces of the larger picture?</p>
+</li>
+</ul>
+<p>Another point extremely close to interoperability is training.
+A great way to interoperate with other languages and domains is having good
+documentation and material from transitioning into Rust without having to figure
+everything at once.
+Rust documentation is already amazing,
+especially considering the many books published by each working group.
+But... there is a gap on the transitions,
+both from understanding the basics of the language and using it,
+to the progression from beginner to intermediate and expert.</p>
+<p>I see good resources for <a href="https://github.com/yoshuawuyts/rust-for-js-people">JavaScript</a> and <a href="https://github.com/rochacbruno/py2rs">Python</a> developers,
+but we are still covering a pretty small niche:
+programmers curious enough to go learn another language,
+or looking for solutions for problems in their current language.</p>
+<p>Can we bring more people into Rust?
+<a href="https://rustbridge.com/">RustBridge</a> is obviously the reference here,
+but there is space for much,
+much more.
+Using Rust in <a href="https://carpentries.org/">The Carpentries</a> lessons?
+Creating <code>RustOpenSci</code>,
+mirroring the communities of practice of <a href="https://ropensci.org/about/">rOpenSci</a> and <a href="https://www.pyopensci.org/">pyOpenSci</a>?</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/103236549475802733">Thread on Mastodon</a></li>
+<li><a href="https://twitter.com/luizirber/status/1201373423592562690">Thread on Twitter</a></li>
+</ul>
+ 2019-12-01T15:00:00+00:00
+ luizirber
+
+
+ PythonClub: Criando dicts a partir de outros dicts
+ https://pythonclub.com.br/crie_dict-a-partir-de-outros-dicts.html
+ <p>Neste tutorial, será abordado o processo de criação de um <em>dict</em> ou dicionário, a partir de um ou mais <em>dicts</em> em Python. </p>
+<p>Como já é de costume da linguagem, isso pode ser feito de várias maneiras diferentes.</p>
+<h2>Abordagem inicial</h2>
+<p>Pra começar, vamos supor que temos os seguintes dicionários:</p>
+<div class="highlight"><pre><span></span><span class="n">dict_1</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'a'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
+ <span class="s1">'b'</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
+<span class="p">}</span>
+
+<span class="n">dict_2</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'b'</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
+ <span class="s1">'c'</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>Como exemplo, vamos criar um novo dicionário chamado <strong>new_dict</strong> com os valores de <strong>dict_1</strong> e <strong>dict_2</strong> logo acima. Uma abordagem bem conhecida é utilizar o método <em>update</em>.</p>
+<div class="highlight"><pre><span></span><span class="n">new_dict</span> <span class="o">=</span> <span class="p">{}</span>
+
+<span class="n">new_dcit</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">dict_1</span><span class="p">)</span>
+<span class="n">new_dcit</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">dict_2</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Assim, temos que <strong>new_dict</strong> será:</p>
+<div class="highlight"><pre><span></span><span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">)</span>
+<span class="p">{</span>
+ <span class="s1">'a'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
+ <span class="s1">'b'</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
+ <span class="s1">'c'</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>Este método funciona bem, porém temos de chamar o método <em>update</em> para cada <em>dict</em> que desejamos mesclar em <strong>new_dict</strong>. Não seria interessante se fosse possível passar todos os <em>dicts</em> necessários já na inicialização de <strong>new_dict</strong>?</p>
+<h3>Novidades do Python 3</h3>
+<p>O Python 3 introduziu uma maneira bem interessante de se fazer isso, utilizando os operadores <code>**</code>.</p>
+<div class="highlight"><pre><span></span><span class="n">new_dict</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="o">**</span><span class="n">dict_1</span><span class="p">,</span>
+ <span class="o">**</span><span class="n">dict_2</span><span class="p">,</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>Assim, de maneira semelhante ao exemplo anterior, temos que <strong>new_dict</strong> será :</p>
+<div class="highlight"><pre><span></span><span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'a'</span><span class="p">])</span>
+<span class="mi">1</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'b'</span><span class="p">])</span>
+<span class="mi">3</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'c'</span><span class="p">])</span>
+<span class="mi">4</span>
+</pre></div>
+
+
+<h2>Cópia real de <em>dicts</em></h2>
+<p>Ao utilizamos o procedimento de inicialização acima, devemos tomar conseiderar alguns fatores. Apenas os valores do primeiro nível serão realmente duplicados no novo dicionário. Como exemplo, vamos alterar uma chave presente em ambos os <em>dicts</em> e verificar se as mesmas possuem o mesmo valor:</p>
+<div class="highlight"><pre><span></span><span class="o">>></span> <span class="n">dict_1</span><span class="p">[</span><span class="s1">'a'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">10</span>
+<span class="o">>></span> <span class="n">new_dict</span><span class="p">[</span><span class="s1">'a'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">11</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">dict_1</span><span class="p">[</span><span class="s1">'a'</span><span class="p">])</span>
+<span class="mi">10</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'a'</span><span class="p">])</span>
+<span class="mi">11</span>
+</pre></div>
+
+
+<p>Porém isso muda quando um dos valores de <strong>dict_1</strong> for uma <em>list</em>, outro <em>dict</em> ou algum objeto complexo. Por exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">dict_3</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'a'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
+ <span class="s1">'b'</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
+ <span class="s1">'c'</span><span class="p">:</span> <span class="p">{</span>
+ <span class="s1">'d'</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
+ <span class="p">}</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>e agora, vamos criar um novo <em>dict</em> a partir desse:</p>
+<div class="highlight"><pre><span></span><span class="n">new_dict</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="o">**</span><span class="n">dict_3</span><span class="p">,</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>Como no exemplo anterior, podemos imaginar que foi realizado uma cópia de todos os elementos de <strong>dict_3</strong>, porém isso não é totalmente verdade. O que realmente aconteceu é que foi feita uma cópia <em>superficial</em> dos valores de <strong>dict_3</strong>, ou seja, apenas os valores de <em>primeiro nível</em> foram duplicados. Observe o que acontece quando alteramos o valor do <em>dict</em> presente na chave <strong>c</strong>.</p>
+<div class="highlight"><pre><span></span><span class="o">>></span> <span class="n">new_dict</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">11</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">])</span>
+<span class="mi">11</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">dict_3</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">])</span>
+<span class="mi">11</span>
+<span class="c1"># valor anterior era 5</span>
+</pre></div>
+
+
+<p>No caso da chave <strong>c</strong>, ela contem uma referência para outra estrutura de dados (um <em>dict</em>, no caso). Quando alteramos algum valor de <strong>dict_3['c']</strong>, isso reflete em todos os <em>dict</em> que foram inicializados com <strong>dict_3</strong>. Em outras palavras, deve-se ter cuidado ao inicializar um <em>dict</em> a partir de outros <strong>dicts</strong> quando os mesmos possuírem valores complexos, como <em>list</em>, <em>dict</em> ou outros objetos (os atributos deste objeto não serão duplicados).</p>
+<p>De modo a contornar este inconveniente, podemos utilizar o método <em>deepcopy</em> da <em>lib</em> nativa <a href="https://docs.python.org/2/library/copy.html">copy</a>. Agora, ao inicializarmos <strong>new_dict</strong>:</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">copy</span>
+
+<span class="n">dict_3</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'a'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
+ <span class="s1">'b'</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
+ <span class="s1">'c'</span><span class="p">:</span> <span class="p">{</span>
+ <span class="s1">'d'</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
+ <span class="p">}</span>
+<span class="p">}</span>
+
+<span class="n">new_dict</span> <span class="o">=</span> <span class="n">copy</span><span class="o">.</span><span class="n">deepcopy</span><span class="p">(</span><span class="n">dict_3</span><span class="p">)</span>
+</pre></div>
+
+
+<p>O método <em>deepcopy</em> realiza uma cópia recursiva de cada elemento de <strong>dict_3</strong>, resolvendo nosso problema. Veja mais um exemplo:</p>
+<div class="highlight"><pre><span></span><span class="o">>></span> <span class="n">new_dict</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">11</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">])</span>
+<span class="mi">11</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">dict_3</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">])</span>
+<span class="mi">5</span>
+<span class="c1"># valor não foi alterado</span>
+</pre></div>
+
+
+<h2>Conclusão</h2>
+<p>Este artigo tenta demonstrar de maneira simples a criação de <em>dicts</em>, utilizando os diversos recursos que a linguagem oferece bem como os prós e contras de cada abordagem. </p>
+<h2>Referências</h2>
+<p>Para mais detalhes e outros exemplos, deem uma olhada neste <em>post</em> do forum da Python Brasil <a href="https://groups.google.com/forum/#!topic/python-brasil/OhUqYQ32M7E">aqui</a>.</p>
+<p>É isso pessoal. Obrigado por ler!</p>
+ 2019-10-01T23:20:29+00:00
+ Michell Stuttgart
+
+
+ Humberto Rocha: Desbravando o pygame 5 - Movimento e Colisão
+ https://humberto.io/pt-br/blog/desbravando-o-pygame-5-movimento-e-colisao/
+ O movimento é uma característica que está presente na maioria dos jogos. Ao saltar entre plataformas, atirar contra a horda de inimigos, pilotar uma nave espacial e correr pelas estradas estamos exercendo movimento, interagindo com o ambiente do jogo, aplicando ações e causando reações.
+Neste capítulo iremos conhecer os conceitos básicos de movimentação de objetos na tela e sua interação com outros elementos através da detecção de colisão.
+Movimento Se você vem acompanhando esta série de postagens, teve um breve exemplo de movimentação na postagem sobre game loop, onde uma bola que se movimentava quicando pela tela foi implementada.
+ 2019-09-10T00:00:00+00:00
+
+
+ Humberto Rocha: Publicando meu primeiro Jogo
+ https://humberto.io/pt-br/blog/publicando-meu-primeiro-jogo/
+ Jogos sempre me conectam com tecnologia desde o início.
+Eu e meu pai montamos nosso primeiro computador (um Pentium 286) e a primeira coisa que eu me lembro de fazer, foi jogar os jogos de DOS como Prince of Persia e Lunar Lander. Eu aprendi vários comandos de CLI só para poder jogar os meus jogos favoritos.
+A paixão por jogar e fazer jogos sempre me acompanhou como um hobby. I tenho uma série de posts sobre pygame neste blog onde eu passo pelos conceitos básicos de desenvolvimento de jogos tentando explicar para pessoas que estejam iniciando seu aprendizado na área.
+ 2019-08-28T00:00:00+00:00
+
+
+ PythonClub: Tutorial Django 2.2
+ https://pythonclub.com.br/tutorial-django-2.html
+ <p>Este tutorial é baseado no <strong>Intro to Django</strong> que fica na parte de baixo da página <a href="https://www.djangoproject.com/start/">start</a> do Django project.</p>
+<p>Até a data deste post o Django está na versão 2.2.2, e requer Python 3.</p>
+<h2>O que você precisa?</h2>
+<p>Python 3.6 ou superior, pip e virtualenv.</p>
+<p>Considere que você tenha instalado Python 3.6 ou superior, <a href="https://pip.readthedocs.io/en/latest/">pip</a> e <a href="https://virtualenv.pypa.io/en/latest/">virtualenv</a>.</p>
+<h2>Criando o ambiente</h2>
+<p>Crie uma pasta com o nome <code>django2-pythonclub</code></p>
+<div class="highlight"><pre><span></span>$ mkdir django2-pythonclub
+$ <span class="nb">cd</span> django2-pythonclub
+</pre></div>
+
+
+<p>A partir de agora vamos considerar esta como a nossa pasta principal.</p>
+<p>Considerando que você está usando <strong>Python 3</strong>, digite</p>
+<div class="highlight"><pre><span></span><span class="err">python3 -m venv .venv</span>
+</pre></div>
+
+
+<p>Lembre-se de colocar esta pasta no seu <code>.gitignore</code>, caso esteja usando.</p>
+<div class="highlight"><pre><span></span><span class="err">echo .venv >> .gitignore</span>
+</pre></div>
+
+
+<p>Depois ative o ambiente digitando</p>
+<div class="highlight"><pre><span></span><span class="err">source .venv/bin/activate</span>
+</pre></div>
+
+
+<blockquote>
+<p>Lembre-se, sempre quando você for mexer no projeto, tenha certeza de ter ativado o <code>virtualenv</code>, executando o comando <code>source .venv/bin/activate</code>. Você deve repetir esse comando toda a vez que você abrir um novo terminal.</p>
+</blockquote>
+<h2>Instalando Django 2.2.2</h2>
+<p>Basta digitar</p>
+<div class="highlight"><pre><span></span><span class="err">pip install django==2.2.2</span>
+</pre></div>
+
+
+<p>Dica: se você digitar <code>pip freeze</code> você verá a versão dos programas instalados.</p>
+<p>É recomendável que você atualize a versão do <code>pip</code></p>
+<div class="highlight"><pre><span></span><span class="err">pip install -U pip</span>
+</pre></div>
+
+
+<p>Se der erro então faça:</p>
+<div class="highlight"><pre><span></span><span class="err">python -m pip install --upgrade pip</span>
+</pre></div>
+
+
+<h2>Instalando mais dependências</h2>
+<p>Eu gosto de usar o <a href="https://django-extensions.readthedocs.io/en/latest/">django-extensions</a> e o <a href="https://github.com/jazzband/django-widget-tweaks">django-widget-tweaks</a>, então digite</p>
+<div class="highlight"><pre><span></span><span class="err">pip install django-extensions django-widget-tweaks python-decouple</span>
+</pre></div>
+
+
+<p><strong>Importante:</strong> você precisa criar um arquivo <code>requirements.txt</code> para instalações futuras do projeto em outro lugar.</p>
+<div class="highlight"><pre><span></span><span class="err">pip freeze > requirements.txt</span>
+</pre></div>
+
+
+<p>Este é o resultado do meu até o dia deste post:</p>
+<div class="highlight"><pre><span></span><span class="p">(.</span><span class="n">venv</span><span class="p">):</span><span class="err">$</span> <span class="n">cat</span> <span class="n">requirements</span><span class="p">.</span><span class="n">txt</span>
+
+<span class="n">django</span><span class="o">-</span><span class="n">extensions</span><span class="o">==</span><span class="mi">2</span><span class="p">.</span><span class="mi">1</span><span class="p">.</span><span class="mi">6</span>
+<span class="n">django</span><span class="o">-</span><span class="n">widget</span><span class="o">-</span><span class="n">tweaks</span><span class="o">==</span><span class="mi">1</span><span class="p">.</span><span class="mi">4</span><span class="p">.</span><span class="mi">3</span>
+<span class="n">python</span><span class="o">-</span><span class="n">decouple</span><span class="o">==</span><span class="mi">3</span><span class="p">.</span><span class="mi">1</span>
+<span class="n">pytz</span><span class="o">==</span><span class="mi">2018</span><span class="p">.</span><span class="mi">9</span>
+<span class="n">six</span><span class="o">==</span><span class="mi">1</span><span class="p">.</span><span class="mi">12</span><span class="p">.</span><span class="mi">0</span>
+</pre></div>
+
+
+<h2>Escondendo a SECRET_KEY e trabalhando com variáveis de ambiente</h2>
+<p>É muito importante que você não deixe sua SECRET_KEY exposta. Então remova-o imediatamente do seu settings.py ANTES mesmo do primeiro commit. Espero que você esteja usando Git.</p>
+<p>Vamos usar o <a href="https://github.com/henriquebastos/python-decouple">python-decouple</a> escrito por <a href="https://henriquebastos.net/">Henrique Bastos</a> para gerenciar nossas variáveis de ambiente. Repare que já instalamos ele logo acima.</p>
+<p>Em seguida você vai precisar criar um arquivo <code>.env</code>, para isso rode o comando a seguir, ele vai criar uma pasta contrib e dentro dele colocar um arquivo <code>env_gen.py</code></p>
+<div class="highlight"><pre><span></span><span class="err">if [ ! -d contrib ]; then mkdir contrib; fi; git clone https://gist.github.com/22626de522f5c045bc63acdb8fe67b24.git contrib/</span>
+<span class="err">rm -rf contrib/.git/ # remova a pasta .git que está dentro de contrib.</span>
+</pre></div>
+
+
+<p>Em seguida rode</p>
+<div class="highlight"><pre><span></span><span class="err">python contrib/env_gen.py</span>
+</pre></div>
+
+
+<p>que ele vai criar o arquivo <code>.env</code>.</p>
+<p>Supondo que você está versionando seu código com Git, é importante que você escreva isso dentro do seu arquivo <code>.gitignore</code>, faça direto pelo terminal</p>
+<div class="highlight"><pre><span></span><span class="err">echo .env >> .gitignore</span>
+<span class="err">echo .venv >> .gitignore</span>
+<span class="err">echo '*.sqlite3' >> .gitignore</span>
+</pre></div>
+
+
+<p>Pronto, agora você pode dar o primeiro commit.</p>
+<h2>Criando o projeto e a App</h2>
+<p>Para criar o projeto digite</p>
+<div class="highlight"><pre><span></span>$ django-admin startproject myproject .
+</pre></div>
+
+
+<p>repare no ponto no final do comando, isto permite que o arquivo <code>manage.py</code> fique nesta mesma pasta <em>django2-pythonclub</em> .</p>
+<p>Agora vamos criar a <em>app</em> <strong>bands</strong>, mas vamos deixar esta <em>app</em> dentro da pasta <em>myproject</em>. Então entre na pasta</p>
+<div class="highlight"><pre><span></span>$ <span class="nb">cd</span> myproject
+</pre></div>
+
+
+<p>e digite</p>
+<div class="highlight"><pre><span></span>$ python ../manage.py startapp bands
+</pre></div>
+
+
+<p>A intenção é que os arquivos tenham a seguinte hierarquia nas pastas:</p>
+<div class="highlight"><pre><span></span><span class="err">.</span>
+<span class="err">├── manage.py</span>
+<span class="err">├── myproject</span>
+<span class="err">│ ├── bands</span>
+<span class="err">│ │ ├── admin.py</span>
+<span class="err">│ │ ├── apps.py</span>
+<span class="err">│ │ ├── models.py</span>
+<span class="err">│ │ ├── tests.py</span>
+<span class="err">│ │ └── views.py</span>
+<span class="err">│ ├── settings.py</span>
+<span class="err">│ ├── urls.py</span>
+<span class="err">│ └── wsgi.py</span>
+<span class="err">└── requirements.txt</span>
+</pre></div>
+
+
+<p>Agora permaneça sempre na pasta <code>django2-pythonclub</code></p>
+<div class="highlight"><pre><span></span><span class="err">cd ..</span>
+</pre></div>
+
+
+<p>e digite</p>
+<div class="highlight"><pre><span></span>$ python manage.py migrate
+</pre></div>
+
+
+<p>para criar a primeira <em>migração</em> (isto cria o banco de dados SQLite), e depois rode a aplicação com</p>
+<div class="highlight"><pre><span></span>$ python manage.py runserver
+</pre></div>
+
+
+<p>e veja que a aplicação já está funcionando. Veja o endereço da url aqui</p>
+<div class="highlight"><pre><span></span><span class="err">Django version 2.2.2, using settings 'myproject.settings'</span>
+<span class="err">Starting development server at http://127.0.0.1:8000/</span>
+<span class="err">Quit the server with CONTROL-C.</span>
+</pre></div>
+
+
+<h2>Editando settings.py</h2>
+<p>Em <code>INSTALLED_APPS</code> acrescente as linhas abaixo.</p>
+<div class="highlight"><pre><span></span><span class="n">INSTALLED_APPS</span> <span class="o">=</span> <span class="p">(</span>
+ <span class="o">...</span>
+ <span class="s1">'widget_tweaks'</span><span class="p">,</span>
+ <span class="s1">'django_extensions'</span><span class="p">,</span>
+ <span class="s1">'myproject.bands'</span><span class="p">,</span>
+<span class="p">)</span>
+</pre></div>
+
+
+<p>E mude também o idioma.</p>
+<p><code>LANGUAGE_CODE = 'pt-br'</code></p>
+<p>E caso você queira o mesmo horário de Brasília-BR</p>
+<p><code>TIME_ZONE = 'America/Sao_Paulo'</code></p>
+<p>Já que falamos do python-decouple, precisamos de mais alguns ajustes</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">decouple</span> <span class="kn">import</span> <span class="n">config</span><span class="p">,</span> <span class="n">Csv</span>
+
+<span class="c1"># SECURITY WARNING: keep the secret key used in production secret!</span>
+<span class="n">SECRET_KEY</span> <span class="o">=</span> <span class="n">config</span><span class="p">(</span><span class="s1">'SECRET_KEY'</span><span class="p">)</span>
+
+<span class="c1"># SECURITY WARNING: don't run with debug turned on in production!</span>
+<span class="n">DEBUG</span> <span class="o">=</span> <span class="n">config</span><span class="p">(</span><span class="s1">'DEBUG'</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">cast</span><span class="o">=</span><span class="nb">bool</span><span class="p">)</span>
+
+<span class="n">ALLOWED_HOSTS</span> <span class="o">=</span> <span class="n">config</span><span class="p">(</span><span class="s1">'ALLOWED_HOSTS'</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="p">[],</span> <span class="n">cast</span><span class="o">=</span><span class="n">Csv</span><span class="p">())</span>
+</pre></div>
+
+
+<p>Veja que é importante manter sua SECRET_KEY bem guardada (em outro lugar).</p>
+<p>Então crie um arquivo <code>.env</code> e guarde sua SECRET_KEY dentro dele, exemplo:</p>
+<div class="highlight"><pre><span></span><span class="err">SECRET_KEY=your_secret_key</span>
+<span class="err">DEBUG=True</span>
+<span class="err">ALLOWED_HOSTS=127.0.0.1,.localhost</span>
+</pre></div>
+
+
+<h2>Editando models.py</h2>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>
+<span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">reverse_lazy</span>
+
+
+<span class="k">class</span> <span class="nc">Band</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
+
+ <span class="sd">"""A model of a rock band."""</span>
+ <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">200</span><span class="p">)</span>
+ <span class="n">can_rock</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">BooleanField</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
+
+ <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
+ <span class="n">ordering</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'name'</span><span class="p">,)</span>
+ <span class="n">verbose_name</span> <span class="o">=</span> <span class="s1">'band'</span>
+ <span class="n">verbose_name_plural</span> <span class="o">=</span> <span class="s1">'bands'</span>
+
+ <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span>
+
+ <span class="k">def</span> <span class="nf">get_absolute_url</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="c1"># retorna a url no formato /bands/1/</span>
+ <span class="k">return</span> <span class="n">reverse_lazy</span><span class="p">(</span><span class="s1">'band_detail'</span><span class="p">,</span> <span class="n">kwargs</span><span class="o">=</span><span class="p">{</span><span class="s1">'pk'</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pk</span><span class="p">})</span>
+
+ <span class="k">def</span> <span class="nf">get_members_count</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="c1"># count members by band</span>
+ <span class="c1"># conta os membros por banda</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">band</span><span class="o">.</span><span class="n">count</span><span class="p">()</span>
+
+
+<span class="k">class</span> <span class="nc">Member</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
+
+ <span class="sd">"""A model of a rock band member."""</span>
+ <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="s2">"Member's name"</span><span class="p">,</span> <span class="n">max_length</span><span class="o">=</span><span class="mi">200</span><span class="p">)</span>
+ <span class="n">instrument</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">choices</span><span class="o">=</span><span class="p">(</span>
+ <span class="p">(</span><span class="s1">'g'</span><span class="p">,</span> <span class="s2">"Guitar"</span><span class="p">),</span>
+ <span class="p">(</span><span class="s1">'b'</span><span class="p">,</span> <span class="s2">"Bass"</span><span class="p">),</span>
+ <span class="p">(</span><span class="s1">'d'</span><span class="p">,</span> <span class="s2">"Drums"</span><span class="p">),</span>
+ <span class="p">(</span><span class="s1">'v'</span><span class="p">,</span> <span class="s2">"Vocal"</span><span class="p">),</span>
+ <span class="p">(</span><span class="s1">'p'</span><span class="p">,</span> <span class="s2">"Piano"</span><span class="p">),</span>
+ <span class="p">),</span>
+ <span class="n">max_length</span><span class="o">=</span><span class="mi">1</span>
+ <span class="p">)</span>
+
+ <span class="n">band</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s2">"Band"</span><span class="p">,</span> <span class="n">related_name</span><span class="o">=</span><span class="s1">'band'</span><span class="p">,</span> <span class="n">on_delete</span><span class="o">=</span><span class="n">models</span><span class="o">.</span><span class="n">CASCADE</span><span class="p">)</span>
+
+ <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
+ <span class="n">ordering</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'name'</span><span class="p">,)</span>
+ <span class="n">verbose_name</span> <span class="o">=</span> <span class="s1">'member'</span>
+ <span class="n">verbose_name_plural</span> <span class="o">=</span> <span class="s1">'members'</span>
+
+ <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span>
+</pre></div>
+
+
+<p>Tem algumas coisas que eu não estou explicando aqui para o tutorial ficar curto, mas uma coisa importante é que, como nós editamos o models.py vamos precisar criar um arquivo de migração do novo modelo. Para isso digite</p>
+<div class="highlight"><pre><span></span><span class="err">python manage.py makemigrations</span>
+<span class="err">python manage.py migrate</span>
+</pre></div>
+
+
+<p>O primeiro comando cria o arquivo de migração e o segundo o executa, criando as tabelas no banco de dados.</p>
+<h2>Editando urls.py</h2>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">include</span><span class="p">,</span> <span class="n">path</span>
+<span class="kn">from</span> <span class="nn">myproject.bands</span> <span class="kn">import</span> <span class="n">views</span> <span class="k">as</span> <span class="n">v</span>
+<span class="kn">from</span> <span class="nn">django.contrib</span> <span class="kn">import</span> <span class="n">admin</span>
+
+<span class="n">app_name</span> <span class="o">=</span> <span class="s1">'bands'</span>
+
+<span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
+ <span class="n">path</span><span class="p">(</span><span class="s1">''</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">home</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'home'</span><span class="p">),</span>
+ <span class="c1"># path('bands/', v.band_list, name='bands'),</span>
+ <span class="c1"># path('bands/<int:pk>/', v.band_detail, name='band_detail'),</span>
+ <span class="c1"># path('bandform/', v.BandCreate.as_view(), name='band_form'),</span>
+ <span class="c1"># path('memberform/', v.MemberCreate.as_view(), name='member_form'),</span>
+ <span class="c1"># path('contact/', v.band_contact, name='contact'),</span>
+ <span class="c1"># path('protected/', v.protected_view, name='protected'),</span>
+ <span class="c1"># path('accounts/login/', v.message),</span>
+ <span class="n">path</span><span class="p">(</span><span class="s1">'admin/'</span><span class="p">,</span> <span class="n">admin</span><span class="o">.</span><span class="n">site</span><span class="o">.</span><span class="n">urls</span><span class="p">),</span>
+<span class="p">]</span>
+</pre></div>
+
+
+<p>Obs: deixei as demais urls comentada porque precisa da função em views.py para que cada url funcione. Descomente cada url somente depois que você tiver definido a função em classe em views.py a seguir.</p>
+<h2>Editando views.py</h2>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.shortcuts</span> <span class="kn">import</span> <span class="n">render</span>
+<span class="kn">from</span> <span class="nn">django.http</span> <span class="kn">import</span> <span class="n">HttpResponse</span>
+<span class="kn">from</span> <span class="nn">django.contrib.auth.decorators</span> <span class="kn">import</span> <span class="n">login_required</span>
+<span class="kn">from</span> <span class="nn">django.views.generic</span> <span class="kn">import</span> <span class="n">CreateView</span>
+<span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">reverse_lazy</span>
+<span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Band</span><span class="p">,</span> <span class="n">Member</span>
+<span class="c1"># from .forms import BandContactForm, BandForm, MemberForm</span>
+</pre></div>
+
+
+<p>Obs: Deixei a última linha comentada porque ainda não chegamos em forms.</p>
+<p>A função a seguir retorna um <strong>HttpResponse</strong>, ou seja, uma mensagem simples no navegador.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">home</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">HttpResponse</span><span class="p">(</span><span class="s1">'Welcome to the site!'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>A próxima função (use uma ou outra) renderiza um template, uma página html no navegador.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">home</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">'home.html'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>A função <code>band_list</code> retorna todas as bandas.</p>
+<p>Para fazer a <strong>busca</strong> por nome de banda usamos o comando <code>search = request.GET.get('search_box')</code>, onde <code>search_box</code> é o nome do campo no template <strong>band_list.html</strong>.</p>
+<p>E os nomes são retornados a partir do comando <code>bands = bands.filter(name__icontains=search)</code>. Onde <code>icontains</code> procura um texto que contém a palavra, ou seja, você pode digitar o nome incompleto (ignora maiúsculo ou minúsculo).</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">band_list</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="sd">""" A view of all bands. """</span>
+ <span class="n">bands</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">search</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">GET</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'search_box'</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">search</span><span class="p">:</span>
+ <span class="n">bands</span> <span class="o">=</span> <span class="n">bands</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">name__icontains</span><span class="o">=</span><span class="n">search</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">'bands/band_list.html'</span><span class="p">,</span> <span class="p">{</span><span class="s1">'bands'</span><span class="p">:</span> <span class="n">bands</span><span class="p">})</span>
+</pre></div>
+
+
+<p>Em urls.py pode descomentar a linha a seguir:</p>
+<div class="highlight"><pre><span></span><span class="n">path</span><span class="p">(</span><span class="s1">'bands/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">band_list</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'bands'</span><span class="p">),</span>
+</pre></div>
+
+
+<p>A função <code>band_contact</code> mostra como tratar um formulário na view. Esta função requer <code>BandContactForm</code>, explicado em forms.py.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">band_contact</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="sd">""" A example of form """</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'POST'</span><span class="p">:</span>
+ <span class="n">form</span> <span class="o">=</span> <span class="n">BandContactForm</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">POST</span><span class="p">)</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="n">form</span> <span class="o">=</span> <span class="n">BandContactForm</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">'bands/band_contact.html'</span><span class="p">,</span> <span class="p">{</span><span class="s1">'form'</span><span class="p">:</span> <span class="n">form</span><span class="p">})</span>
+</pre></div>
+
+
+<p>Em urls.py pode descomentar a linha a seguir:</p>
+<div class="highlight"><pre><span></span><span class="n">path</span><span class="p">(</span><span class="s1">'contact/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">band_contact</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'contact'</span><span class="p">),</span>
+</pre></div>
+
+
+<p>A função <code>band_detail</code> retorna todos os membros de cada banda, usando o <code>pk</code> da banda junto com o comando <code>filter</code> em members.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">band_detail</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">):</span>
+ <span class="sd">""" A view of all members by bands. """</span>
+ <span class="n">band</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">pk</span><span class="o">=</span><span class="n">pk</span><span class="p">)</span>
+ <span class="n">members</span> <span class="o">=</span> <span class="n">Member</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">band</span><span class="o">=</span><span class="n">band</span><span class="p">)</span>
+ <span class="n">context</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'members'</span><span class="p">:</span> <span class="n">members</span><span class="p">,</span> <span class="s1">'band'</span><span class="p">:</span> <span class="n">band</span><span class="p">}</span>
+ <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">'bands/band_detail.html'</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Em urls.py pode descomentar a linha a seguir:</p>
+<div class="highlight"><pre><span></span><span class="n">path</span><span class="p">(</span><span class="s1">'bands/<int:pk>/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">band_detail</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'band_detail'</span><span class="p">),</span>
+</pre></div>
+
+
+<p><code>BandCreate</code> e <code>MemberCreate</code> usam o <a href="https://docs.djangoproject.com/en/2.2/ref/class-based-views/">Class Based View</a> para tratar formulário de uma forma mais simplificada usando a classe <code>CreateView</code>. O <code>reverse_lazy</code> serve para tratar a url de retorno de página.</p>
+<p>As classes a seguir requerem <code>BandForm</code> e <code>MemberForm</code>, explicado em forms.py.</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">BandCreate</span><span class="p">(</span><span class="n">CreateView</span><span class="p">):</span>
+ <span class="n">model</span> <span class="o">=</span> <span class="n">Band</span>
+ <span class="n">form_class</span> <span class="o">=</span> <span class="n">BandForm</span>
+ <span class="n">template_name</span> <span class="o">=</span> <span class="s1">'bands/band_form.html'</span>
+ <span class="n">success_url</span> <span class="o">=</span> <span class="n">reverse_lazy</span><span class="p">(</span><span class="s1">'bands'</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">MemberCreate</span><span class="p">(</span><span class="n">CreateView</span><span class="p">):</span>
+ <span class="n">model</span> <span class="o">=</span> <span class="n">Member</span>
+ <span class="n">form_class</span> <span class="o">=</span> <span class="n">MemberForm</span>
+ <span class="n">template_name</span> <span class="o">=</span> <span class="s1">'bands/member_form.html'</span>
+ <span class="n">success_url</span> <span class="o">=</span> <span class="n">reverse_lazy</span><span class="p">(</span><span class="s1">'bands'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Em urls.py pode descomentar a linha a seguir:</p>
+<div class="highlight"><pre><span></span><span class="n">path</span><span class="p">(</span><span class="s1">'bandform/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">BandCreate</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span> <span class="n">name</span><span class="o">=</span><span class="s1">'band_form'</span><span class="p">),</span>
+<span class="n">path</span><span class="p">(</span><span class="s1">'memberform/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">MemberCreate</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span> <span class="n">name</span><span class="o">=</span><span class="s1">'member_form'</span><span class="p">),</span>
+</pre></div>
+
+
+<p>A próxima função requer que você entre numa página somente quando estiver logado.</p>
+<p><code>[@login_required](https://docs.djangoproject.com/en/2.2/topics/auth/default/#the-login-required-decorator)</code> é um <strong>decorator</strong>.</p>
+<p><code>login_url='/accounts/login/'</code> é página de erro, ou seja, quando o usuário não conseguiu logar.</p>
+<p>E <code>render(request, 'bands/protected.html',...</code> é página de sucesso.</p>
+<div class="highlight"><pre><span></span><span class="nd">@login_required</span><span class="p">(</span><span class="n">login_url</span><span class="o">=</span><span class="s1">'/accounts/login/'</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">protected_view</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="sd">""" A view that can only be accessed by logged-in users """</span>
+ <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">'bands/protected.html'</span><span class="p">,</span> <span class="p">{</span><span class="s1">'current_user'</span><span class="p">:</span> <span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="p">})</span>
+</pre></div>
+
+
+<p><code>HttpResponse</code> retorna uma mensagem simples no navegador sem a necessidade de um template.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">message</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="sd">""" Message if is not authenticated. Simple view! """</span>
+ <span class="k">return</span> <span class="n">HttpResponse</span><span class="p">(</span><span class="s1">'Access denied!'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Em urls.py pode descomentar a linha a seguir:</p>
+<div class="highlight"><pre><span></span><span class="n">path</span><span class="p">(</span><span class="s1">'protected/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">protected_view</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'protected'</span><span class="p">),</span>
+<span class="n">path</span><span class="p">(</span><span class="s1">'accounts/login/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">message</span><span class="p">),</span>
+</pre></div>
+
+
+<h2>Comandos básicos do manage.py</h2>
+<p>Para criar novas migrações com base nas alterações feitas nos seus modelos</p>
+<p><code>$ python manage.py makemigrations bands</code></p>
+<p>Obs: talvez dê erro porque está faltando coisas de forms.py, explicado mais abaixo.</p>
+<p>Para aplicar as migrações</p>
+<p><code>$ python manage.py migrate</code></p>
+<p>Para criar um usuário e senha para o admin</p>
+<p><code>$ python manage.py createsuperuser</code></p>
+<p>Para rodar a aplicação localmente</p>
+<p><code>$ python manage.py runserver</code></p>
+<p>Após criar um super usuário você pode entrar em localhost:8000/admin</p>
+<p>Obs: Se você entrar agora em localhost:8000 vai faltar o template home.html. Explicado mais abaixo.</p>
+<h2>shell_plus</h2>
+<p>É o <strong>interpretador interativo do python</strong> rodando <strong>via terminal</strong> direto na aplicação do django.</p>
+<p>Com o comando a seguir abrimos o shell do Django.</p>
+<p><code>$ python manage.py shell</code></p>
+<p>Mas se você está usando o django-extensions (mostrei como configurá-lo no settings.py), então basta digitar</p>
+<p><code>$ python manage.py shell_plus</code></p>
+<p>Veja a seguir como inserir dados direto pelo shell.</p>
+<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="kn">from</span> <span class="nn">myproject.bands.models</span> <span class="kn">import</span> <span class="n">Band</span><span class="p">,</span> <span class="n">Member</span>
+<span class="o">>>></span> <span class="c1"># Com django-extensions não precisa fazer o import</span>
+<span class="o">>>></span> <span class="c1"># criando o objeto e salvando</span>
+<span class="o">>>></span> <span class="n">band</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'Metallica'</span><span class="p">)</span>
+<span class="o">>>></span> <span class="n">band</span><span class="o">.</span><span class="n">name</span>
+<span class="o">>>></span> <span class="n">band</span><span class="o">.</span><span class="n">can_rock</span>
+<span class="o">>>></span> <span class="n">band</span><span class="o">.</span><span class="n">id</span>
+<span class="o">>>></span> <span class="c1"># criando uma instancia da banda a partir do id</span>
+<span class="o">>>></span> <span class="n">b</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">band</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
+<span class="o">>>></span> <span class="c1"># criando uma instancia do Membro e associando o id da banda a ela</span>
+<span class="o">>>></span> <span class="n">m</span> <span class="o">=</span> <span class="n">Member</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'James Hetfield'</span><span class="p">,</span> <span class="n">instrument</span><span class="o">=</span><span class="s1">'b'</span><span class="p">,</span> <span class="n">band</span><span class="o">=</span><span class="n">b</span><span class="p">)</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">name</span>
+<span class="o">>>></span> <span class="c1"># retornando o instrumento</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">instrument</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">get_instrument_display</span><span class="p">()</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">band</span>
+<span class="o">>>></span> <span class="c1"># salvando</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+<span class="o">>>></span> <span class="c1"># listando todas as bandas</span>
+<span class="o">>>></span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+<span class="o">>>></span> <span class="c1"># listando todos os membros</span>
+<span class="o">>>></span> <span class="n">Member</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+<span class="o">>>></span> <span class="c1"># criando mais uma banda</span>
+<span class="o">>>></span> <span class="n">band</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'The Beatles'</span><span class="p">)</span>
+<span class="o">>>></span> <span class="n">band</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'The Beatles'</span><span class="p">)</span>
+<span class="o">>>></span> <span class="n">band</span><span class="o">.</span><span class="n">id</span>
+<span class="o">>>></span> <span class="n">b</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">band</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
+<span class="o">>>></span> <span class="c1"># criando mais um membro</span>
+<span class="o">>>></span> <span class="n">m</span> <span class="o">=</span> <span class="n">Member</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'John Lennon'</span><span class="p">,</span> <span class="n">instrument</span><span class="o">=</span><span class="s1">'v'</span><span class="p">,</span> <span class="n">band</span><span class="o">=</span><span class="n">b</span><span class="p">)</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+<span class="o">>>></span> <span class="c1"># listando tudo novamente</span>
+<span class="o">>>></span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+<span class="o">>>></span> <span class="n">Member</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+<span class="o">>>></span> <span class="n">exit</span><span class="p">()</span>
+</pre></div>
+
+
+<h2>Criando os templates</h2>
+<p>Você pode criar os templates com os comandos a seguir...</p>
+<div class="highlight"><pre><span></span>$ mkdir -p myproject/bands/templates/bands
+$ touch myproject/bands/templates/<span class="o">{</span>menu,base,home<span class="o">}</span>.html
+$ touch myproject/bands/templates/bands/<span class="o">{</span>band_list,band_detail,band_form,band_contact,member_form,protected<span class="o">}</span>.html
+</pre></div>
+
+
+<p>... ou pegar os templates já prontos direto do Github.</p>
+<div class="highlight"><pre><span></span><span class="err">mkdir -p myproject/bands/templates/bands</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/base.html -P myproject/bands/templates/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/home.html -P myproject/bands/templates/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/menu.html -P myproject/bands/templates/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/band_contact.html -P myproject/bands/templates/bands/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/band_detail.html -P myproject/bands/templates/bands/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/band_form.html -P myproject/bands/templates/bands/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/band_list.html -P myproject/bands/templates/bands/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/member_form.html -P myproject/bands/templates/bands/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/protected.html -P myproject/bands/templates/bands/</span>
+</pre></div>
+
+
+<h2>forms.py</h2>
+<p><code>$ touch myproject/bands/forms.py</code></p>
+<p>Edite o forms.py.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django</span> <span class="kn">import</span> <span class="n">forms</span>
+<span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Band</span><span class="p">,</span> <span class="n">Member</span>
+
+
+<span class="k">class</span> <span class="nc">BandContactForm</span><span class="p">(</span><span class="n">forms</span><span class="o">.</span><span class="n">Form</span><span class="p">):</span>
+ <span class="n">subject</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">widget</span><span class="o">=</span><span class="n">forms</span><span class="o">.</span><span class="n">Textarea</span><span class="p">)</span>
+ <span class="n">sender</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">EmailField</span><span class="p">()</span>
+ <span class="n">cc_myself</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">BooleanField</span><span class="p">(</span><span class="n">required</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">BandForm</span><span class="p">(</span><span class="n">forms</span><span class="o">.</span><span class="n">ModelForm</span><span class="p">):</span>
+
+ <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
+ <span class="n">model</span> <span class="o">=</span> <span class="n">Band</span>
+ <span class="n">fields</span> <span class="o">=</span> <span class="s1">'__all__'</span>
+
+
+<span class="k">class</span> <span class="nc">MemberForm</span><span class="p">(</span><span class="n">forms</span><span class="o">.</span><span class="n">ModelForm</span><span class="p">):</span>
+
+ <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
+ <span class="n">model</span> <span class="o">=</span> <span class="n">Member</span>
+ <span class="n">fields</span> <span class="o">=</span> <span class="s1">'__all__'</span>
+</pre></div>
+
+
+<p>Lembra que eu deixei o código comentado em views.py?</p>
+<p>Descomente ele por favor</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">.forms</span> <span class="kn">import</span> <span class="n">BandContactForm</span><span class="p">,</span> <span class="n">BandForm</span><span class="p">,</span> <span class="n">MemberForm</span>
+</pre></div>
+
+
+<h2>admin.py</h2>
+<p>Criamos uma customização para o admin onde em members aparece um filtro por bandas.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.contrib</span> <span class="kn">import</span> <span class="n">admin</span>
+<span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Band</span><span class="p">,</span> <span class="n">Member</span>
+
+
+<span class="k">class</span> <span class="nc">MemberAdmin</span><span class="p">(</span><span class="n">admin</span><span class="o">.</span><span class="n">ModelAdmin</span><span class="p">):</span>
+ <span class="sd">"""Customize the look of the auto-generated admin for the Member model."""</span>
+ <span class="n">list_display</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'name'</span><span class="p">,</span> <span class="s1">'instrument'</span><span class="p">)</span>
+ <span class="n">list_filter</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'band'</span><span class="p">,)</span>
+
+
+<span class="n">admin</span><span class="o">.</span><span class="n">site</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">Band</span><span class="p">)</span> <span class="c1"># Use the default options</span>
+<span class="n">admin</span><span class="o">.</span><span class="n">site</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">Member</span><span class="p">,</span> <span class="n">MemberAdmin</span><span class="p">)</span> <span class="c1"># Use the customized options</span>
+</pre></div>
+
+
+<h2>Carregando dados de um CSV</h2>
+<p>Vamos baixar alguns arquivos para criar os dados no banco a partir de um CSV.</p>
+<div class="highlight"><pre><span></span><span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/create_data.py</span>
+<span class="err">mkdir fix</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/fix/bands.csv -P fix/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/fix/members.csv -P fix/</span>
+</pre></div>
+
+
+<p>Estando na pasta principal, rode o comando</p>
+<div class="highlight"><pre><span></span><span class="err">python create_data.py</span>
+</pre></div>
+
+
+<p>que ele vai carregar alguns dados pra você.</p>
+<p>Veja o código de <a href="https://github.com/rg3915/django2-pythonclub/blob/master/create_data.py">create_data.py</a>.</p>
+<p>Veja o código completo em https://github.com/rg3915/django2-pythonclub</p>
+<p><code>git clone https://github.com/rg3915/django2-pythonclub.git</code></p>
+ 2019-06-25T01:00:00+00:00
+ Regis da Silva
+
+
+ Gabbleblotchits: Scientific Rust #rust2019
+ https://blog.luizirber.org/2019/01/05/rust-2019/
+ <p>The Rust community requested feedback last year for where the language should go
+in 2018, and now they are running it again for 2019.
+Last year I was too new in Rust to organize a blog post,
+but after an year using it I feel more comfortable writing this!</p>
+<p>(Check my previous post about <a href="https://blog.luizirber.org/2018/08/23/sourmash-rust/">replacing the C++ core in sourmash with Rust</a> for more details on how I spend my year in Rust).</p>
+<h2>What counts as "scientific Rust"?</h2>
+<p>Anything that involves doing science using computers counts as
+scientific programming. It includes from embedded software
+<a href="https://www.youtube.com/watch?v=y5Yd3FC-kh8">running on satellites</a> to climate models
+running in supercomputers, from shell scripts running tools in a pipeline to
+data analysis using notebooks.</p>
+<p>It also makes the discussion harder, because it's too general! But it is very
+important to keep in mind, because scientists are not your regular user: they
+are highly qualified in their field of expertise, and they are also pushing the
+boundaries of what we know (and this might need flexibility in their tools).</p>
+<p>In this post I will be focusing more in two areas: array computing (what most
+people consider 'scientific programming' to be) and "data structures".</p>
+<h3>Array computing</h3>
+<p>This one is booming in the last couple of years due to industry interest in data
+sciences and deep learning (where they will talk about tensors instead of arrays),
+and has its roots in models running in supercomputers (a field where Fortran is
+still king!). Data tends to be quite regular (representable with matrices) and
+amenable to parallel processing.</p>
+<p>A good example is the SciPy stack in Python, built on top of NumPy and SciPy.
+The adoption of the SciPy stack (both in academia and industry) is staggering,
+and many <a href="https://github.com/cupy/cupy">alternative implementations</a> try to provide a NumPy-like API to try to
+capture its mindshare.</p>
+<p>This is the compute-intensive side science (be it CPU or GPU/TPU), and also the kind
+of data that pushed CPU evolution and is still very important in defining policy
+in scientific computing funding (see countries competing for the largest
+supercomputers and measuring performance in floating point operations per second).</p>
+<h3>Data structures for efficient data representation</h3>
+<p>For data that is not so regular the situation is a bit different. I'll use
+bioinformatics as an example: the data we get out of nucleotide sequencers is usually
+represented by long strings (of ACGT), and algorithms will do a lot of string processing
+(be it building string-overlap graphs for assembly, or searching for substrings
+in large collections). This is only one example: there are many analyses that
+will work with other types of data, and most of them don't have a
+universal data representation as in the array computing case.</p>
+<p>This is the memory-intensive science, and it's hard to measure performance in
+floating point operations because... most of the time you're not even using
+floating point numbers. It also suffers from limited data locality (which is
+almost a prerequisite for compute-intensive performance).</p>
+<h2>High performance core, interactive API</h2>
+<p>There is something common in both cases: while performance-intensive
+code is implemented in C/C++/Fortran, users usually interact with the API from
+other languages (especially Python or R) because it's faster to iterate and
+explore the data, and many of the tools already available in these languages are
+very helpful for these tasks (think Jupyter/pandas or RStudio/tidyverse).
+These languages are used to define the computation, but it is a lower-level core
+library that drives it (NumPy or Tensorflow follow this idea, for example).</p>
+<h2>How to make Rust better for science?</h2>
+<p>The biggest barrier to learning Rust is the ownership model, and while we can
+agree it is an important feature it is also quite daunting for newcomers,
+especially if they don't have previous programming experience and exposure to
+what bugs are being prevented. I don't see it being the first language we teach
+to scientists any time soon, because the majority of scientists are not system
+programmers, and have very different expectations for a programming language.
+That doesn't mean that they can't benefit from Rust!</p>
+<p>Rust is already great for building the performance-intensive parts,
+and thanks to Cargo it is also a better alternative for sharing this code around,
+since they tend to get 'stuck' inside Python or R packages.
+And the 'easy' approach of vendoring C/C++ instead of having packages make it
+hard to keep track of changes and doesn't encourage reusable code.</p>
+<p>And, of course, if this code is Rust instead of C/C++ it also means that Rust
+users can use them directly, without depending on the other languages. Seems
+like a good way to bootstrap a scientific community in Rust =]</p>
+<h2>What I would like to see in 2019?</h2>
+<h3>An attribute proc-macro like <code>#[wasm_bindgen]</code> but for FFI</h3>
+<p>While FFI is an integral part of Rust goals (interoperability with C/C++), I
+have serious envy of the structure and tooling developed for WebAssembly! (Even
+more now that it works in stable too)</p>
+<p>We already have <code>#[no_mangle]</code> and <code>pub extern "C"</code>, but they are quite
+low-level. I would love to see something closer to what wasm-bindgen does,
+and define some traits (like <a href="https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/convert/trait.IntoWasmAbi.html"><code>IntoWasmAbi</code></a>) to make it easier to
+pass more complex data types through the FFI.</p>
+<p>I know it's not that simple, and there are different design restrictions than
+WebAssembly to take into account... The point here is not having the perfect
+solution for all use cases, but something that serves as an entry point and helps
+to deal with the complexity while you're still figuring out all the quirks and
+traps of FFI. You can still fallback and have more control using the lower-level
+options when the need rises.</p>
+<h3>More -sys and Rust-like crates for interoperability with the larger ecosystems</h3>
+<p>There are new projects bringing more interoperability to <a href="https://arrow.apache.org/">dataframes</a> and <a href="https://xnd.io/">tensors</a>.
+While this ship has already sailed and they are implemented in C/C++,
+it would be great to be a first-class citizen,
+and not reinvent the wheel.
+(Note: the arrow project already have pretty good Rust support!)</p>
+<p>In my own corner (bioinformatics), the <a href="https://github.com/rust-bio">Rust-bio community</a> is doing a
+great job of wrapping <a href="https://github.com/rust-bio/rust-htslib">useful libraries in C/C++</a> and exposing them to
+Rust (and also a shout-out to 10X Genomics for doing this work for
+<a href="https://github.com/10XGenomics/rust-bwa">other tools</a> while also contributing to Rust-bio!).</p>
+<h3>More (bioinformatics) tools using Rust!</h3>
+<p>We already have great examples like <a href="https://github.com/onecodex/finch-rs">finch</a> and <a href="https://github.com/natir/yacrd">yacrd</a>,
+since Rust is great for single binary distribution of programs.
+And with bioinformatics focusing so much in independent tools chained together in workflows,
+I think we can start convincing people to try it out =]</p>
+<h3>A place to find other scientists?</h3>
+<p>Another idea is to draw inspiration from <a href="https://ropensci.org/about/">rOpenSci</a> and have a Rust equivalent,
+where people can get feedback about their projects and how to better integrate it with other crates.
+This is quite close to the working group idea,
+but I think it would serve more as a gateway to other groups,
+more focused on developing entry-level docs and bringing more scientists to the
+community.</p>
+<h2>Final words</h2>
+<p>In the end, I feel like this post ended up turning into my 'wishful TODO list'
+for 2019, but I would love to find more people sharing these goals (or willing
+to take any of this and just run with it, I do have a PhD to finish! =P)</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/101367104235280253">Thread on Mastodon</a></li>
+<li><a href="https://twitter.com/luizirber/status/1081729107170193408">Thread on Twitter</a></li>
+</ul>
+ 2019-01-05T19:00:00+00:00
+ luizirber
+
+
+ PythonClub: Algoritmos de Ordenação
+ https://pythonclub.com.br/algoritmos-ordenacao.html
+ <p>Fala pessoal, tudo bom?</p>
+<p>Nos vídeos abaixo, vamos aprender como implementar alguns dos algoritmos de ordenação usando Python.</p>
+<div class="section" id="bubble-sort">
+<h2>Bubble Sort</h2>
+<p>Como o algoritmo funciona: Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=Doy64STkwlI&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r&t=0s&index=3">https://www.youtube.com/watch?v=Doy64STkwlI</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=B0DFF0fE4rk&index=3&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r">https://www.youtube.com/watch?v=B0DFF0fE4rk</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Código do algoritmo</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">sort</span><span class="p">(</span><span class="n">array</span><span class="p">):</span>
+
+ <span class="k">for</span> <span class="n">final</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">),</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span>
+ <span class="n">exchanging</span> <span class="o">=</span> <span class="kc">False</span>
+
+ <span class="k">for</span> <span class="n">current</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">final</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">array</span><span class="p">[</span><span class="n">current</span><span class="p">]</span> <span class="o">></span> <span class="n">array</span><span class="p">[</span><span class="n">current</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]:</span>
+ <span class="n">array</span><span class="p">[</span><span class="n">current</span> <span class="o">+</span> <span class="mi">1</span><span class="p">],</span> <span class="n">array</span><span class="p">[</span><span class="n">current</span><span class="p">]</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="n">current</span><span class="p">],</span> <span class="n">array</span><span class="p">[</span><span class="n">current</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
+ <span class="n">exchanging</span> <span class="o">=</span> <span class="kc">True</span>
+
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">exchanging</span><span class="p">:</span>
+ <span class="k">break</span>
+</pre></div>
+</div>
+<div class="section" id="selection-sort">
+<h2>Selection Sort</h2>
+<p>Como o algoritmo funciona: Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=vHxtP9BC-AA&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r&index=4">https://www.youtube.com/watch?v=vHxtP9BC-AA</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=0ORfCwwhF_I&index=5&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r&index=5">https://www.youtube.com/watch?v=0ORfCwwhF_I</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Código do algoritmo</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">sort</span><span class="p">(</span><span class="n">array</span><span class="p">):</span>
+ <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">)):</span>
+ <span class="n">min_index</span> <span class="o">=</span> <span class="n">index</span>
+
+ <span class="k">for</span> <span class="n">right</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">index</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">)):</span>
+ <span class="k">if</span> <span class="n">array</span><span class="p">[</span><span class="n">right</span><span class="p">]</span> <span class="o"><</span> <span class="n">array</span><span class="p">[</span><span class="n">min_index</span><span class="p">]:</span>
+ <span class="n">min_index</span> <span class="o">=</span> <span class="n">right</span>
+
+ <span class="n">array</span><span class="p">[</span><span class="n">index</span><span class="p">],</span> <span class="n">array</span><span class="p">[</span><span class="n">min_index</span><span class="p">]</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="n">min_index</span><span class="p">],</span> <span class="n">array</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
+</pre></div>
+</div>
+<div class="section" id="insertion-sort">
+<h2>Insertion Sort</h2>
+<p>Como o algoritmo funciona: Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=O_E-Lj5HuRU&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r&t=0s&index=6">https://www.youtube.com/watch?v=O_E-Lj5HuRU</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=Sy_Z1pqMgko&index=7&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r">https://www.youtube.com/watch?v=Sy_Z1pqMgko</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Código do algoritmo</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">sort</span><span class="p">(</span><span class="n">array</span><span class="p">):</span>
+ <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">)):</span>
+ <span class="n">current_element</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="n">p</span><span class="p">]</span>
+
+ <span class="k">while</span> <span class="n">p</span> <span class="o">></span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">array</span><span class="p">[</span><span class="n">p</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">></span> <span class="n">current_element</span><span class="p">:</span>
+ <span class="n">array</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="n">p</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span>
+ <span class="n">p</span> <span class="o">-=</span> <span class="mi">1</span>
+
+ <span class="n">array</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="n">current_element</span>
+</pre></div>
+</div>
+<div class="section" id="merge-sort">
+<h2>Merge Sort</h2>
+<p>Como o algoritmo funciona: Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=Lnww0ibU0XM&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r&t=0s&index=8">https://www.youtube.com/watch?v=Lnww0ibU0XM</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Como implementar o algoritmo usando Python - Parte I: <a class="reference external" href="https://www.youtube.com/watch?v=cXJHETlYyVk&index=9&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r">https://www.youtube.com/watch?v=cXJHETlYyVk</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Código do algoritmo</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">sort</span><span class="p">(</span><span class="n">array</span><span class="p">):</span>
+ <span class="n">sort_half</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
+
+
+<span class="k">def</span> <span class="nf">sort_half</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">start</span> <span class="o">>=</span> <span class="n">end</span><span class="p">:</span>
+ <span class="k">return</span>
+
+ <span class="n">middle</span> <span class="o">=</span> <span class="p">(</span><span class="n">start</span> <span class="o">+</span> <span class="n">end</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span>
+
+ <span class="n">sort_half</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">middle</span><span class="p">)</span>
+ <span class="n">sort_half</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">middle</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">end</span><span class="p">)</span>
+
+ <span class="n">merge</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">)</span>
+
+
+<span class="k">def</span> <span class="nf">merge</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">):</span>
+ <span class="n">array</span><span class="p">[</span><span class="n">start</span><span class="p">:</span> <span class="n">end</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">array</span><span class="p">[</span><span class="n">start</span><span class="p">:</span> <span class="n">end</span> <span class="o">+</span> <span class="mi">1</span><span class="p">])</span>
+</pre></div>
+</div>
+ 2018-11-29T15:10:00+00:00
+ Lucas Magnum
+
+
+ PythonClub: Trabalhando com operadores ternários
+ https://pythonclub.com.br/trabalhando-com-operadores-ternarios.html
+ <p>Quando estamos escrevendo um código qualquer, possivelmente
+a expressão que mais utilizamos é o <code>if</code>. Para qualquer
+tarefas que buscamos automatizar ou problemas que buscamos
+resolver, sempre acabamos caindo em lógicas como "Se isso
+acontecer, então faça aquilo, senão faça aquele outro...".</p>
+<p>Quando estamos falando de ações a serem executadas, pessoalmente
+gosto da forma com que o código fica organizado em python quando
+usamos este tipo de condições, por exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">vencer_o_thanos</span><span class="p">:</span>
+ <span class="n">restaurar_a_paz</span><span class="p">()</span>
+
+<span class="k">else</span><span class="p">:</span>
+ <span class="n">foo</span><span class="p">()</span>
+</pre></div>
+
+
+<p>Graças a indentação e ao espaçamento, vemos onde onde começa e/ou
+termina o bloco executado caso a varável <code>vencer_o_thanos</code> seja
+<code>True</code>. Quanto mais <code>if</code>'s você aninhar, mais bonito seu
+código fica e em momento algum o mesmo se torna mais confuso
+(ao menos, não deveria se tornar). Entretanto, sempre fico
+extremamente incomodado quando tenho de escrever um bloco apenas
+marcar uma variável, como por exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">vencer_o_thanos</span><span class="p">:</span>
+ <span class="n">paz</span> <span class="o">=</span> <span class="kc">True</span>
+
+<span class="k">else</span><span class="p">:</span>
+ <span class="n">paz</span> <span class="o">=</span> <span class="kc">False</span>
+</pre></div>
+
+
+<p>Por isso, para trabalhar com variáveis que possuem um valor condicional,
+gosto sempre de trabalhar com expressões condicionais, ou como costumam
+ser chamadas, <strong>operadores ternários</strong>.</p>
+<p>Operadores ternários são todos os operadores que podem receber três
+operandos. Como as expressões condicionais costumam ser os operadores
+ternários mais populares nas linguagens em que aparecem, acabamos por
+associar estes nomes e considerar que são a mesma coisa. Cuidado ao tirar
+este tipo de conclusão, mesmo que toda vogal esteja no alfabeto, o
+alfabeto não é composto apenas por vogais.</p>
+<p>A estrutura de uma expressão condicional é algo bem simples, veja só:</p>
+<div class="highlight"><pre><span></span><span class="n">paz</span> <span class="o">=</span> <span class="kc">True</span> <span class="k">if</span> <span class="n">vencer_o_thanos</span> <span class="k">else</span> <span class="kc">False</span>
+<span class="n">tipo_de_x</span> <span class="o">=</span> <span class="s2">"Par"</span> <span class="k">if</span> <span class="n">x</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="s2">"impar"</span>
+</pre></div>
+
+
+<p>Resumidamente, teremos <strong>um valor seguido de uma condição e por fim seu
+valor caso a condição seja falsa</strong>. Pessoalmente acredito que apesar de um
+pouco diferente, essa forma de escrita para casos como o exemplificado acima
+é muito mais clara, mais explicita.</p>
+<p>Se você fizer uma tradução literal das booleanas utilizadas no primeiro exemplo,
+lerá algo como <code>paz é verdadeira caso vencer_o_thanos, caso contrário é Falsa.</code>
+já o segundo exemplo fica mais claro ainda, pois lemos algo como
+<code>tipo_de_x é par caso o resto da divisão de x por 2 seja 0, se não, tipo_de_x é impar.</code>.</p>
+<p>Interpretar código dessa forma pode ser estranho para um programador. Interpretar
+uma abertura de chave ou uma indentação já é algo mas natural. Todavia, para aqueles
+que estão começando, o raciocínio ocorre de forma muito mais parecida com a descrita
+acima. Espero que tenham gostado do texto e que esse conhecimento lhes seja útil.</p>
+ 2018-10-06T12:21:00+00:00
+ Vitor Hugo de Oliveira Vargas
+
+
+ Gabbleblotchits: What open science is about
+ https://blog.luizirber.org/2018/09/24/open-science/
+ <p>Today I got a pleasant surprise: <a href="http://www.olgabotvinnik.com/">Olga Botvinnik</a> posted on <a href="https://twitter.com/olgabot/status/1044292704513839104">Twitter</a>
+about a <a href="https://github.com/czbiohub/kmer-hashing/blob/olgabot/search-compare-ignore-abundance/figures/presentations/2018-09-24_beyond_the_cell_atlas_poster/2018-09-24_Beyond_the_Cell_Atlas.pdf">poster</a> she is presenting at the Beyond the Cell Atlas conference
+and she name-dropped a bunch of people that helped her. The cool thing? They
+are all open source developers, and Olga interacted thru GitHub to <a href="https://github.com/dib-lab/sourmash/pull/543">ask</a> <a href="https://github.com/dib-lab/sourmash/issues/545">for</a> <a href="https://github.com/betteridiot/bamnostic/issues/15">features</a>,
+<a href="https://github.com/betteridiot/bamnostic/issues/20">report bugs</a> and even <a href="https://github.com/dib-lab/sourmash/pull/543">submit</a> <a href="https://github.com/dib-lab/sourmash/pull/539">pull</a> <a href="https://github.com/dib-lab/sourmash/pull/529">requests</a>.</p>
+<p>That's what open science is about: collaboration, good practices, and in the end coming up
+with something that is larger than each individual piece. Now sourmash is better,
+bamnostic is better, reflow is better. I would like to see this becoming more and
+more common =]</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/100783375923088450">Thread on Mastodon</a></li>
+<li><a href="https://twitter.com/luizirber/status/1044369382233665536">Thread on Twitter</a></li>
+</ul>
+ 2018-09-24T20:00:00+00:00
+ luizirber
+
+
+ Gabbleblotchits: New crate: nthash
+ https://blog.luizirber.org/2018/09/13/nthash/
+ <p>A quick announcement: I wrote a <a href="https://github.com/luizirber/nthash">Rust implementation</a> of <a href="https://github.com/bcgsc/ntHash">ntHash</a> and published
+it in <a href="https://crates.io/crates/nthash">crates.io</a>. It implements an <code>Iterator</code> to take advantage of the
+rolling properties of <code>ntHash</code> which make it so useful in bioinformatics (where
+we work a lot with sliding windows over sequences).</p>
+<p>It's a pretty small crate, and probably was a better project to learn Rust than
+doing a <a href="https://blog.luizirber.org/2018/08/23/sourmash-rust/">sourmash implementation</a> because it doesn't involve gnarly FFI
+issues. I also put <a href="https://github.com/luizirber/nthash/blob/d0c16d7deb0a78b8aeb29090db91bba954c14fe8/src/lib.rs#L91">some docs</a>, <a href="https://github.com/luizirber/nthash/blob/d0c16d7deb0a78b8aeb29090db91bba954c14fe8/benches/nthash.rs#L11">benchmarks</a> using <a href="https://japaric.github.io/criterion.rs/">criterion</a>,
+and even an <a href="https://github.com/luizirber/nthash/blob/d0c16d7deb0a78b8aeb29090db91bba954c14fe8/tests/nthash.rs#L80">oracle property-based test</a> with <a href="https://github.com/BurntSushi/quickcheck">quickcheck</a>.</p>
+<p>More info <a href="https://docs.rs/nthash/">in the docs</a>, and if you want an <s>optimization</s> versioning bug
+discussion be sure to check the <a href="https://github.com/luizirber/nthash_bug"><code>ntHash bug?</code></a> repo,
+which has a (slow) Python implementation and a pretty nice <a href="https://nbviewer.jupyter.org/github/luizirber/nthash_bug/blob/master/analysis.ipynb">analysis</a> notebook.</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/100721133117928424">Thread on Mastodon</a></li>
+<li><a href="https://twitter.com/luizirber/status/1040386666089705472">Thread on Twitter</a></li>
+</ul>
+ 2018-09-13T20:00:00+00:00
+ luizirber
+
+
+ Gabbleblotchits: Oxidizing sourmash: WebAssembly
+ https://blog.luizirber.org/2018/08/27/sourmash-wasm/
+ <p>sourmash calculates MinHash signatures for genomic datasets,
+meaning we are reducing the data (via subsampling) to a small
+representative subset (a signature) capable of answering one question:
+how similar is this dataset to another one? The key here is that a dataset with
+10-100 GB will be reduced to something in the megabytes range, and two approaches
+for doing that are:</p>
+<ul>
+<li>The user install our software in their computer.
+ This is not so bad anymore (yay bioconda!), but still requires knowledge
+ about command line interfaces and how to install all this stuff. The user
+ data never leaves their computer, and they can share the signatures later
+ if they want to.</li>
+<li>Provide a web service to calculate signatures. In this case no software
+ need to be installed, but it's up to someone (me?) to maintain a server running with
+ an API and frontend to interact with the users. On top of requiring more
+ maintenance, another drawback is that
+ the user need to send me the data, which is very inefficient network-wise
+ and lead to questions about what I can do with their raw data (and I'm not
+ into surveillance capitalism, TYVM).</li>
+</ul>
+<h2>But... what if there is a third way?</h2>
+<p>What if we could keep the frontend code from the web service (very
+user-friendly) but do all the calculations client-side (and avoid the network
+bottleneck)? The main hurdle
+here is that our software is implemented in Python (and C++), which are not
+supported in browsers. My first solution was to write the core features of
+<a href="https://github.com/luizirber/sourmash-node">sourmash in JavaScript</a>, but that quickly started hitting annoying things
+like JavaScript not supporting 64-bit integers. There is also the issue of
+having another codebase to maintain and keep in sync with the original sourmash,
+which would be a relevant burden for us. I gave a <a href="https://drive.google.com/open?id=1JvXiDaEA4J3hmEKw6sV-VHMpuHG_sxls3fLxJOht28E">lab meeting</a> about this
+approach, using a <a href="https://soursigs-dnd-luizirber.hashbase.io/">drag-and-drop UI as proof of concept</a>. It did work but it
+was finicky (dealing with the 64-bit integer hashes is not fun). The good thing
+is that at least I had a working UI for further testing<sup id="sf-sourmash-wasm-1-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-1" class="simple-footnote" title="even if horrible, I need to get some design classes =P">1</a></sup></p>
+<p>In "<a href="https://blog.luizirber.org/2018/08/23/sourmash-rust/">Oxidizing sourmash: Python and FFI</a>" I described my road to learn Rust,
+but something that I omitted was that around the same time the <code>WebAssembly</code>
+support in Rust started to look better and better and was a huge influence in
+my decision to learn Rust. Reimplementing the sourmash C++ extension in Rust and
+use the same codebase in the browser sounded very attractive,
+and now that it was working I started looking into how to use the WebAssembly
+target in Rust.</p>
+<h2>WebAssembly?</h2>
+<p>From the <a href="https://webassembly.org/">official site</a>,</p>
+<blockquote>
+<p>WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine.
+Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust,
+enabling deployment on the web for client and server applications.</p>
+</blockquote>
+<p>You can write WebAssembly by hand, but the goal is to have it as lower level
+target for other languages. For me the obvious benefit is being able to use
+something that is not JavaScript in the browser, even though the goal is not to replace
+JS completely but complement it in a big pain point: performance. This also
+frees JavaScript from being the target language for other toolchains,
+allowing it to grow into other important areas (like language ergonomics).</p>
+<p>Rust is not the only language targeting WebAssembly: Go 1.11 includes
+<a href="https://golang.org/doc/go1.11#wasm">experimental support for WebAssembly</a>, and there are even projects bringing
+the <a href="https://github.com/iodide-project/pyodide">scientific Python to the web</a> using WebAssembly. </p>
+<h2>But does it work?</h2>
+<p>With the <a href="https://github.com/luizirber/sourmash-rust">Rust implementation in place</a> and with all tests working on sourmash, I
+added the finishing touches using <a href="https://github.com/rustwasm/wasm-bindgen"><code>wasm-bindgen</code></a> and built an NPM package using
+<a href="https://github.com/rustwasm/wasm-pack"><code>wasm-pack</code></a>: <a href="https://www.npmjs.com/package/sourmash">sourmash</a> is a Rust codebase compiled to WebAssembly and ready
+to use in JavaScript projects.</p>
+<p>(Many thanks to Madicken Munk, who also presented during SciPy about how they used
+<a href="https://munkm.github.io/2018-07-13-scipy/">Rust and WebAssembly to do interactive visualization in Jupyter</a>
+and helped with a good example on how to do this properly =] )</p>
+<p>Since I already had the working UI from the previous PoC, I <a href="https://github.com/luizirber/wort-dnd">refactored the code</a>
+to use the new WebAssembly module and voilà! <a href="https://wort-dnd.hashbase.io/">It works!</a><sup id="sf-sourmash-wasm-2-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-2" class="simple-footnote" title="the first version of this demo only worked in Chrome because they implemented the BigInt proposal, which is not in the official language yet. The funny thing is that BigInt would have made the JS implementation of sourmash viable, and I probably wouldn't have written the Rust implementation =P. Turns out that I didn't need the BigInt support if I didn't expose any 64-bit integers to JS, and that is what I'm doing now.">2</a></sup>.
+<sup id="sf-sourmash-wasm-3-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-3" class="simple-footnote" title="Along the way I ended up writing a new FASTQ parser... because it wouldn't be bioinformatics if it didn't otherwise, right? =P">3</a></sup>
+But that was the demo from a year ago with updated code and I got a bit
+better with frontend development since then, so here is the new demo:</p>
+<div id="files" class="box">
+ <h2>sourmash + Wasm</h2>
+ <div id="drag-container">
+ <p><b>Drag & drop</b> a FASTA or FASTQ file here to calculate the sourmash signature.</p>
+ </div>
+
+ <div id="progress-container">
+ <div id="progress-bar"></div>
+ </div>
+ <div class="columns">
+ <fieldset class="box input-button" id="params">
+ <label for="ksize-input">k-mer size:</label>
+ <input id="ksize-input" type="number" value="21" />
+ <label for="scaled-input">scaled:</label>
+ <input id="scaled-input" type="number" value="0" />
+ <label for="num-input">number of hashes:</label>
+ <input id="num-input" type="number" value="500" />
+ <label for="dna-protein-group">Input type:</label>
+ <div id="dna-protein-group">
+ <input id="dna-input" name="dna-protein-input" type="radio" value="DNA/RNA" checked="checked" />
+ <label for="dna-input">DNA/RNA</label>
+ <input id="protein-input" name="dna-protein-input" type="radio" value="Protein" />
+ <label for="protein-input">Protein</label>
+ </div>
+ <label for="track-abundance-input">Track abundance?</label>
+ <input id="track-abundance-input" type="checkbox" checked="checked" />
+ </fieldset>
+ <div class="box" id="download">
+ <button id="download_btn" type="button" disabled="disabled">Download</button>
+ </div>
+ </div>
+</div>
+
+<p>
+</p>
+<p>For the source code for this demo, check the <a href="https://blog.luizirber.org/static/sourmash-wasm/index.html">sourmash-wasm</a> directory.</p>
+<h2>Next steps</h2>
+<p>The proof of concept works, but it is pretty useless right now.
+I'm thinking about building it as a <a href="https://www.webcomponents.org/">Web Component</a> and making it really easy
+to add to any webpage<sup id="sf-sourmash-wasm-4-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-4" class="simple-footnote" title="or maybe a React component? I really would like to have something that works independent of framework, but not sure what is the best option in this case...">4</a></sup>.</p>
+<p>Another interesting feature would be supporting more input formats (the GMOD
+project implemented a lot of those!), but more features are probably better
+after something simple but functional is released =P</p>
+<h2>Next time!</h2>
+<p>Where we will go next? Maybe explore some decentralized web technologies like
+IPFS and dat, hmm? =]</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/100624574917435477">Thread on Mastodon</a></li>
+<li><a href="https://www.reddit.com/r/rust/comments/9atie8/blog_post_clientside_bioinformatics_in_the/">Thread on reddit</a></li>
+<li><a href="https://twitter.com/luizirber/status/1034206952773935104">Thread on Twitter</a></li>
+</ul>
+<h2>Updates</h2>
+<ul>
+<li>2018-08-30: Added a demo in the blog post.</li>
+</ul><hr /><h2>Footnotes</h2><ol><li id="sf-sourmash-wasm-1"><p>even if horrible, I
+need to get some design classes =P <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-1-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-wasm-2"><p>the first version
+of this demo only worked in Chrome because they implemented the <a href="https://github.com/tc39/proposal-bigint">BigInt proposal</a>,
+which is not in the official language yet. The funny thing is that BigInt would
+have made the JS implementation of sourmash viable, and I probably wouldn't have
+written the Rust implementation =P.
+Turns out that I didn't need the BigInt support if I didn't expose any 64-bit
+integers to JS, and that is what I'm doing now. <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-2-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-wasm-3"><p>Along the way I ended up writing a new FASTQ parser... because it wouldn't
+be bioinformatics if it didn't otherwise, right? =P <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-3-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-wasm-4"><p>or maybe a React component? I really would like to
+have something that works independent of framework, but not sure what is the
+best option in this case... <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-4-back" class="simple-footnote-back">↩</a></p></li></ol>
+ 2018-08-27T18:30:00+00:00
+ luizirber
+
+
+ Gabbleblotchits: Oxidizing sourmash: Python and FFI
+ https://blog.luizirber.org/2018/08/23/sourmash-rust/
+ <p>I think the first time I heard about Rust was because Frank Mcsherry chose it to
+write a <a href="https://github.com/frankmcsherry/timely-dataflow">timely dataflow</a> implementation.
+Since then it started showing more and more in my news sources,
+leading to Armin Ronacher publishing a post in the Sentry blog last November about
+writing <a href="https://blog.sentry.io/2017/11/14/evolving-our-rust-with-milksnake">Python extensions in Rust</a>.</p>
+<p>Last December I decided to give it a run:
+I spent some time porting the C++ bits of <a href="https://github.com/dib-lab/sourmash">sourmash</a>
+to Rust.
+The main advantage here is that it's a problem I know well,
+so I know what the code is supposed to do and can focus on figuring out
+syntax and the mental model for the language.
+I started digging into the <code>symbolic</code> codebase and understanding what they did,
+and tried to mirror or improve it for my use cases.</p>
+<p>(About the post title: The process of converting a codebase to Rust is referred as <a href="https://wiki.mozilla.org/Oxidation">"Oxidation"</a> in
+the Rust community, following the codename Mozilla chose for the process of
+integrating Rust components into the Firefox codebase.
+<sup id="sf-sourmash-rust-1-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-1" class="simple-footnote" title="The creator of the language is known to keep making up different explanations for the name of the language, but in this case ">1</a></sup>
+Many of these components were tested and derived in Servo, an experimental
+browser engine written in Rust, and are being integrated into Gecko,
+the current browser engine (mostly written in C++).)</p>
+<h2>Why Rust?</h2>
+<p>There are other programming languages more focused on scientific software
+that could be used instead, like Julia<sup id="sf-sourmash-rust-2-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-2" class="simple-footnote" title="Even more now that it hit 1.0, it is a really nice language">2</a></sup>. Many programming languages start from a
+specific niche (like R and statistics,
+or Maple and mathematics) and grow into larger languages over time.
+While Rust goal is not to be a scientific language,
+its focus on being a general purpose language allows a phenomenon similar
+to what happened with Python, where people from many areas pushed the
+language in different directions (system scripting, web development,
+numerical programming...) allowing developers to combine all these things
+in their systems.</p>
+<p>But by far my interest in Rust is for the many best practices it brings to the default experience:
+integrated package management (with Cargo),
+documentation (with rustdoc), testing and benchmarking.
+It's understandable that older languages like C/C++ need
+more effort to support some of these features (like modules and an unified
+build system), since they are designed by standard and need to keep backward
+compatibility with codebases that already exist.
+Nonetheless, the lack of features increase the effort needed to have good
+software engineering practices, since you need to choose a solution that might
+not be compatible with other similar but slightly different options,
+leading to fragmentation and increasing the impedance to use these features.</p>
+<p>Another big reason is that Rust doesn't aim to completely replace what already
+exists, but complement and extend it. Two very good talks about how to do this,
+one by <a href="https://ashleygwilliams.github.io/rustfest-2017/#1">Ashley Williams</a>, another by <a href="http://talks.edunham.net/lca2018/should-you-rewrite-in-rust/beamer.pdf">E. Dunham</a>.</p>
+<h2>Converting from a C++ extension to Rust</h2>
+<p>The current implementation of the core data structures in sourmash is in a
+C++ extension wrapped with Cython. My main goals for converting the code are:</p>
+<ul>
+<li>
+<p>support additional languages and platforms. sourmash is available as a Python
+ package and CLI, but we have R users in the lab that would benefit from having
+ an R package, and ideally we wouldn't need to rewrite the software every time
+ we want to support a new language.</p>
+</li>
+<li>
+<p>reducing the number of wheel packages necessary (one for each OS/platform).</p>
+</li>
+<li>
+<p>in the long run, use the Rust memory management concepts (lifetimes, borrowing)
+to increase parallelism in the code.</p>
+</li>
+</ul>
+<p>Many of these goals are attainable with our current C++ codebase, and
+"rewrite in a new language" is rarely the best way to solve a problem.
+But the reduced burden in maintenance due to better tooling,
+on top of features that would require careful planning to execute
+(increasing the parallelism without data races) while maintaining compatibility
+with the current codebase are promising enough to justify this experiment.
+<a href="https://github.com/luizirber/2018-python-rust/blob/master/03.current_impl.md"><img src="https://blog.luizirber.org/images/arch_cpp.png" title="" /></a></p>
+<p>Cython provides a nice gradual path to migrate code from Python to C++,
+since it is a superset of the Python syntax. It also provides low overhead
+for many C++ features, especially the STL containers, with makes it easier
+to map C++ features to the Python equivalent.
+For research software this also lead to faster exploration of solutions before
+having to commit to lower level code, but without a good process it might also
+lead to code never crossing into the C++ layer and being stuck in the Cython
+layer. This doesn't make any difference for a Python user, but it becomes
+harder from users from other languages to benefit from this code (since your
+language would need some kind of support to calling Python code, which is not
+as readily available as calling C code).</p>
+<p>Depending on the requirements, a downside is that Cython is tied to the CPython API,
+so generating the extension requires a development environment set up with
+the appropriate headers and compiler. This also makes the extension specific
+to a Python version: while this is not a problem for source distributions,
+generating wheels lead to one wheel for each OS and Python version supported.</p>
+<h2>The new implementation</h2>
+<p>This is the overall architecture of the Rust implementation:
+<a href="https://github.com/luizirber/2018-python-rust/blob/master/04.rust_impl.md"><img src="https://blog.luizirber.org/images/arch_rust.png" title="" /></a>
+It is pretty close to what <code>symbolic</code> does,
+so let's walk through it.</p>
+<h3>The Rust code</h3>
+<p>If you take a look at my Rust code, you will see it is very... C++. A lot of the
+code is very similar to the original implementation, which is both a curse and a
+blessing: I'm pretty sure that are more idiomatic and performant ways of doing
+things, but most of the time I could lean on my current mental model for C++ to
+translate code. The biggest exception was the <code>merge</code> function, were I was doing
+something on the C++ implementation that the borrow checker didn't like.
+Eventually I found it was because it couldn't keep track of the lifetime
+correctly and putting braces around it fixed the problem,
+which was both an epiphany and a WTF moment. <a href="https://play.rust-lang.org/?gist=eae9de12950d1b2a7699cd49a3571c37&version=stable">Here</a> is an example that triggers
+the problem, and the <a href="https://play.rust-lang.org/?gist=c8733c5125766930a589c8d0412af99c&version=stable">solution</a>.</p>
+<p>"Fighting the borrow checker" seems to be a common theme while learning Rust,
+but the compiler really tries to help you to understand what is happening and
+(most times) how to fix it. A lot of people grow to hate the borrow checker,
+but I see it more as a 'eat your vegetables' situation: you might not like it
+at first, but it's better in the long run. Even though I don't have a big
+codebase in Rust yet, it keeps you from doing things that will come back to bite
+you hard later.</p>
+<h3>Generating C headers for Rust code: cbindgen</h3>
+<p>With the Rust library working, the next step was taking the Rust code and generate C headers describing the
+functions and structs we expose with the <code>#[no_mangle]</code> attribute in Rust
+(these are defined in the <a href="https://github.com/luizirber/sourmash-rust/blob/ead9ae0ed3b2d16c9e3b8379919f3bfd2efd21ae/src/ffi.rs"><code>ffi.rs</code></a> module in <code>sourmash-rust</code>).
+This attribute tells the Rust compiler to generate names that are compatible
+with the C ABI, and so can be called from other languages that implement FFI
+mechanisms. FFI (the foreign function interface) is quite low-level,
+and pretty much defines things that C can represent: integers, floats, pointers
+and structs. It doesn't support higher level concepts like objects or generics,
+so in a sense it looks like a feature funnel.
+This might sound bad, but ends up being something that other languages can
+understand without needing too much extra functionality in their runtimes,
+which means that most languages have support to calling code through an FFI.</p>
+<p>Writing the C header by hand is possible, but is very error prone.
+A better solution is to use <a href="https://github.com/eqrion/cbindgen"><code>cbindgen</code></a>,
+a program that takes Rust code and generate a C header file automatically.
+<code>cbindgen</code> is developed primarily to generate the C headers for <a href="https://github.com/servo/webrender/">webrender</a>,
+the GPU-based renderer for servo,
+so it's pretty likely that if it can handle a complex codebase it will work
+just fine for the majority of projects.</p>
+<h3>Interfacing with Python: CFFI and Milksnake</h3>
+<p>Once we have the C headers, we can use the FFI to
+call Rust code in Python. Python has a FFI module in the standard library: <code>ctypes</code>,
+but the Pypy developers also created CFFI, which has more features.</p>
+<p>The C headers generated by cbindgen can be interpreted by CFFI to generate
+a low-level Python interface for the code. This is the equivalent of declaring
+the functions/methods and structs/classes in a <code>pxd</code> file (in the Cython
+world): while the code is now usable in Python, it is not well adapted to
+the features and idioms available in the language.</p>
+<p>Milksnake is the package developed by Sentry that takes care of running cargo
+for the Rust compilation and generating the CFFI boilerplate,
+making it easy to load the low-level CFFI bindings in Python.
+With this low-level binding available we can now write something more Pythonic
+(the <code>pyx</code> file in Cython), and I ended up just renaming the <code>_minhash.pyx</code> file
+back to <code>minhash.py</code> and doing one-line fixes to replace the Cython-style code
+with the equivalent CFFI calls.</p>
+<p>All of these changes should be transparent to the Python code, and to guarantee
+that I made sure that all the current tests that we have (both for the Python
+module and the command line interface) are still working after the changes.
+It also led to finding some quirks in the implementation,
+and even improvements in the current C++ code (because we were moving a lot of
+data from C++ to Python).</p>
+<h2>Where I see this going</h2>
+<p>It seems it worked as an experiment,
+and I presented a <a href="https://github.com/luizirber/2018-python-rust">poster</a> at <a href="https://gccbosc2018.sched.com/event/FEWp/b23-oxidizing-python-writing-extensions-in-rust">GCCBOSC 2018</a> and <a href="https://scipy2018.scipy.org/ehome/index.php?eventid=299527&tabid=712461&cid=2233543&sessionid=21618890&sessionchoice=1&">SciPy 2018</a> that
+was met with excitement by many people.
+Knowing that it is possible,
+I want to reiterate some points why Rust is pretty exciting for bioinformatics
+and science in general.</p>
+<h3>Bioinformatics as libraries (and command line tools too!)</h3>
+<p>Bioinformatics is an umbrella term for many different methods, depending on
+what analysis you want to do with your data (or model).
+In this sense, it's distinct from other scientific areas where it is possible
+to rely on a common set of libraries (numpy in linear algebra, for example), since a
+library supporting many disjoint methods tend to grow too big and hard to
+maintain.</p>
+<p>The environment also tends to be very diverse, with different languages being
+used to implement the software. Because it is hard to interoperate,
+the methods tend to be implemented in command line programs that are stitched
+together in pipelines, a workflow describing how to connect the input and output of many different tools to
+generate results.
+Because the basic unit is a command-line tool,
+pipelines tend to rely on standard operating system abstractions like
+files and pipes to make the tools communicate with each other. But since tools
+might have input requirements distinct from what the previous tool provides,
+many times it is necessary to do format conversion or other adaptations to make the
+pipeline work.</p>
+<p>Using tools as blackboxes, controllable through specific parameters at the
+command-line level, make exploratory analysis and algorithm reuse harder:
+if something needs to be investigated the user needs to resort to perturbations
+of the parameters or the input data, without access to the more feature-rich and
+meaningful abstraction happening inside the tool.</p>
+<p>Even if many languages are used for writing the software, most of the time there
+is some part written in C or C++ for performance reasons, and these tend to be
+the core data structures of the computational method. Because it is not easy to
+package your C/C++ code in a way that other people can readily use it,
+most of this code is reinvented over and over again, or is copy and pasted into
+codebases and start diverging over time. Rust helps solve this problem with the
+integrated package management, and due to the FFI it can also be reused inside
+other programs written in other languages.</p>
+<p>sourmash is not going to be Rust-only and abandon Python,
+and it would be crazy to do so when it has so many great exploratory tools
+for scientific discovery. But now we can also use our method in other languages
+and environment, instead of having our code stuck in one language.</p>
+<h3>Don't rewrite it all!</h3>
+<p>I could have gone all the way and rewrite sourmash in Rust<sup id="sf-sourmash-rust-3-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-3" class="simple-footnote" title="and risk being kicked out of the lab =P">3</a></sup>, but it would be incredibly disruptive for
+the current sourmash users and it would take way longer to pull off. Because
+Rust is so focused in supporting existing code, you can do a slow transition and
+reuse what you already have while moving into more and more Rust code.
+A great example is this one-day effort by Rob Patro to bring <a href="https://github.com/COMBINE-lab/cqf-rust">CQF</a> (a C
+codebase) into Rust, using <code>bindgen</code> (a generator of C bindings for Rust).
+Check <a href="https://blog.luizirber.org/2018/08/27/sourmash-wasm/">the Twitter thread</a> for more =]</p>
+<h3>Good scientific citizens</h3>
+<p>There is another MinHash implementation already written in Rust, <a href="https://github.com/onecodex/finch-rs">finch</a>.
+Early in my experiment I got an email from them asking if I wanted to work
+together, but since I wanted to learn the language I kept doing my thing. (They
+were totally cool with this, by the way). But the fun thing is that Rust has a
+pair of traits called <a href="https://doc.rust-lang.org/rust-by-example/conversion/from_into.html"><code>From</code> and <code>Into</code></a> that you can implement for your
+type, and so I <a href="https://github.com/luizirber/sourmash-rust/pull/1">did that</a> and now we can have interoperable
+implementations. This synergy allows <code>finch</code> to use <code>sourmash</code> methods,
+and vice versa.</p>
+<p>Maybe this sounds like a small thing, but I think it is really exciting. We can
+stop having incompatible but very similar methods, and instead all benefit from
+each other advances in a way that is supported by the language.</p>
+<h2>Next time!</h2>
+<p>Turns out Rust supports WebAssembly as a target,
+so... what if we run sourmash in the browser?
+That's what I'm covering in the <a href="https://blog.luizirber.org/2018/08/27/sourmash-wasm/">next blog post</a>,
+so stay tuned =]</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/100602525575698239">Thread on Mastodon</a></li>
+<li><a href="https://www.reddit.com/r/rust/comments/99vakd/blog_post_converting_c_to_rust_and_interoperate/">Thread on reddit</a></li>
+<li><a href="https://twitter.com/luizirber/status/1032779995129597952">Thread on Twitter</a></li>
+</ul>
+<!-- rust libs -->
+
+<!-- why rust -->
+
+<!-- WASM and demos -->
+
+<!-- python and FFI -->
+
+<!-- rust examples -->
+
+<!-- cbindgen -->
+
+<!-- rust for pythonistas -->
+
+<!-- understanding rust -->
+
+<!-- rust and bio -->
+
+<!-- rust error handling -->
+
+<!-- other notes
+
+- Implement Default trait for defaults (less verbose than ::new...)
+ https://doc.rust-lang.org/std/default/trait.Default.html
+
+--><hr /><h2>Footnotes</h2><ol><li id="sf-sourmash-rust-1"><p>The creator of the language is known to keep making up different
+explanations for the <a href="https://www.reddit.com/r/rust/comments/27jvdt/internet_archaeology_the_definitive_endall_source/">name of the language</a>,
+but in this case "oxidation" refers to the chemical process that creates
+rust, and rust is the closest thing to metal (metal being the hardware).
+There are many terrible puns in the Rust community. <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-1-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-rust-2"><p>Even more now that it hit 1.0,
+it is a really nice language <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-2-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-rust-3"><p>and risk
+being kicked out of the lab =P <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-3-back" class="simple-footnote-back">↩</a></p></li></ol>
+ 2018-08-23T20:00:00+00:00
+ luizirber
+
+
+ PythonClub: Upload de Arquivos com Socket e Struct
+ https://pythonclub.com.br/upload-de-arquivos-com-socket-e-struct.html
+ <p>Apesar de termos muitas formas de enviarmos arquivos para servidores hoje em dia, como por exemplo o <em>scp</em> e <em>rsync</em>, podemos usar o python com seus módulos <em>built-in</em> para enviar arquivos a servidores usando struct para serializar os dados e socket para criar uma conexão cliente/servidor.</p>
+<h3><em>Struct</em></h3>
+<p>O módulo <a href="https://docs.python.org/3/library/struct.html">struct</a> é usado para converter bytes no python em formatos do struct em C.
+Com ele podemos enviar num único conjunto de dados o nome de um arquivo e os bytes referentes ao seus dados.</p>
+<p>Struct também é utilizado para serializar diversos tipos de dados diferentes, como bytes, inteiros, floats além de outros, no nosso caso usaremos apenas bytes.</p>
+<p>Vamos criar um arquivo para serializar.</p>
+<div class="highlight"><pre><span></span><span class="err">!</span><span class="n">echo</span> <span class="s2">"Upload de arquivos com sockets e struct</span><span class="se">\n</span><span class="s2">Criando um arquivo para serializar."</span> <span class="o">></span> <span class="n">arquivo_para_upload</span>
+</pre></div>
+
+
+<p>Agora em posse de um arquivo vamos criar nossa estrutura de bytes para enviar.</p>
+<div class="highlight"><pre><span></span><span class="n">arquivo</span> <span class="o">=</span> <span class="s2">"arquivo_para_upload"</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">arquivo</span><span class="p">,</span> <span class="s1">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">arq</span><span class="p">:</span>
+ <span class="n">dados_arquivo</span> <span class="o">=</span> <span class="n">arq</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
+ <span class="n">serializar</span> <span class="o">=</span> <span class="n">struct</span><span class="o">.</span><span class="n">Struct</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">s </span><span class="si">{}</span><span class="s2">s"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">arquivo</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">dados_arquivo</span><span class="p">)))</span>
+ <span class="n">dados_upload</span> <span class="o">=</span> <span class="n">serializar</span><span class="o">.</span><span class="n">pack</span><span class="p">(</span><span class="o">*</span><span class="p">[</span><span class="n">arquivo</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="n">dados_arquivo</span><span class="p">])</span>
+</pre></div>
+
+
+<p>Por padrão, struct usa caracteres no início da sequência dos dados para definir a ordem dos bytes, tamanho e alinhamento dos bytes nos dados empacotados.
+Esses caracteres podem ser vistos na <a href="https://docs.python.org/3/library/struct.html#byte-order-size-and-alignment">seção 7.1.2.1</a> da documentação.
+Como não definimos, será usado o <strong>@</strong> que é o padrão.</p>
+<p>Nessa linha:</p>
+<div class="highlight"><pre><span></span><span class="n">serializar</span> <span class="o">=</span> <span class="n">struct</span><span class="o">.</span><span class="n">Struct</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">s </span><span class="si">{}</span><span class="s2">s"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">arquivo</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">dados_arquivo</span><span class="p">)))</span>
+</pre></div>
+
+
+<p>definimos que nossa estrutura serializada seria de dois conjuntos de caracteres, a primeira com o tamanho do nome do arquivo, e a segunda com o tamanho total dos dados lidos em</p>
+<div class="highlight"><pre><span></span><span class="n">dados_arquivo</span> <span class="o">=</span> <span class="n">arq</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
+</pre></div>
+
+
+<p>Se desempacotarmos os dados, teremos uma lista com o nome do arquivo e os dados lidos anteriormente.</p>
+<div class="highlight"><pre><span></span><span class="n">serializar</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">dados_upload</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="err">b'arquivo_para_upload'</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="n">serializar</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">dados_upload</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="err">b'Upload de arquivos com sockets e struct\\nCriando um arquivo para serializar.\n'</span>
+</pre></div>
+
+
+<p>Agora de posse dos nossos dados já serializados, vamos criar um cliente e um servidor com socket para transferirmos nosso arquivo.</p>
+<h3><em>Socket</em></h3>
+<p>O modulo <a href="https://docs.python.org/3/library/socket.html">socket</a> prove interfaces de socket BSD, disponiveis em praticamente todos os sistemas operacionais.</p>
+<h4>Familias de sockets</h4>
+<p>Diversas famílias de sockets podem ser usadas para termos acessos a objetos que nos permitam fazer chamadas de sistema.
+Mais informações sobre as famílias podem ser encontradas na <a href="https://docs.python.org/3/library/socket.html#socket-families">seção 18.1.1</a> da documentação. No nosso exemplo usaremos a AF_INET.</p>
+<h4>AF_INET</h4>
+<p><strong>AF_INET</strong> precisa basicamente de um par de dados, contendo um endereço IPv4 e uma porta para ser instanciada.
+Para endereços IPv6 o modulo disponibiliza o <strong>AF_INET6</strong></p>
+<h4>Constante [SOCK_STREAM]</h4>
+<p>As constantes representam as famílias de sockets, como a constante AF_INET e os protocolos usados como parâmetros para o modulo socket.
+Um dos protocolos mais usados encontrados na maioria dos sistemas é o SOCK_STREAM.</p>
+<p>Ele é um protocolo baseado em comunicação que permite que duas partes estabeleçam uma conexão e conversem entre si.</p>
+<h3><em>Servidor e cliente socket</em></h3>
+<p>Como vamos usar um protocolo baseado em comunicação, iremos construir o servidor e cliente paralelamente para um melhor entendimento.</p>
+<blockquote>
+<p>Servidor</p>
+</blockquote>
+<p>Para esse exemplo eu vou usar a porta 6124 para o servidor, ela esta fora da range reservada pela IANA para sistemas conhecidos, que vai de 0-1023.</p>
+<p>Vamos importar a bilioteca socket e definir um host e porta para passarmos como parametro para a constante AF_INET.</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">socket</span>
+<span class="n">host</span> <span class="o">=</span> <span class="s2">"127.0.0.1"</span>
+<span class="n">porta</span> <span class="o">=</span> <span class="mi">6124</span>
+<span class="n">sock</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Agora usaremos o metodo bind para criarmos um ponto de conexão para nosso cliente. Esse método espera por uma tupla contento o host e porta como parâmetros.</p>
+<div class="highlight"><pre><span></span><span class="n">sock</span><span class="o">.</span><span class="n">bind</span><span class="p">((</span><span class="n">host</span><span class="p">,</span> <span class="n">porta</span><span class="p">))</span>
+</pre></div>
+
+
+<p>Agora vamos colocar nosso servidor socket em modo escuta com o metodo listen. Esse método recebe como parâmetro um número inteiro (<strong>backlog</strong>) definindo qual o tamanho da fila que será usada para receber pacotes SYN até dropar a conexão. Usaremos um valor baixo o que evita SYN flood na rede. Mais informações sobre <em>backlog</em> podem ser encontradas na <a href="https://tools.ietf.org/html/rfc7413">RFC 7413</a>.</p>
+<div class="highlight"><pre><span></span><span class="n">sock</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Agora vamos colocar o nosso socket em um loop esperando por uma conexão e um início de conversa. Pra isso vamos usar o metodo <em>accept</em> que nos devolve uma tupla, onde o primeiro elemento é um novo objeto socket para enviarmos e recebermos informações, e o segundo contendo informações sobre o endereço de origem e porta usada pelo cliente.</p>
+<p><strong>Vamos criar um diretório para salvar nosso novo arquivo.</strong></p>
+<div class="highlight"><pre><span></span><span class="err">!</span><span class="n">mkdir</span> <span class="n">arquivos_recebidos</span>
+</pre></div>
+
+
+<p><em>Os dados são enviados sempre em bytes</em>. <strong>Leia os comentários</strong></p>
+<div class="highlight"><pre><span></span><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="n">novo_sock</span><span class="p">,</span> <span class="n">cliente</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span>
+ <span class="k">with</span> <span class="n">novo_sock</span><span class="p">:</span> <span class="c1"># Caso haja uma nova conexão</span>
+ <span class="n">ouvir</span> <span class="o">=</span> <span class="n">novo_sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> <span class="c1"># Colocamos nosso novo objeto socket para ouvir</span>
+ <span class="k">if</span> <span class="n">ouvir</span> <span class="o">!=</span> <span class="sa">b</span><span class="s2">""</span><span class="p">:</span> <span class="c1"># Se houver uma mensagem...</span>
+ <span class="sd">"""</span>
+<span class="sd"> Aqui usaremos os dados enviados na mensagem para criar nosso serielizador.</span>
+
+<span class="sd"> Com ele criado poderemos desempacotar os dados assim que recebermos.</span>
+<span class="sd"> Veja no cliente mais abaixo qual a primeira mensagem enviada.</span>
+<span class="sd"> """</span>
+ <span class="n">mensagem</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">dados</span> <span class="o">=</span> <span class="n">ouvir</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">":"</span><span class="p">)</span>
+ <span class="n">serializar</span> <span class="o">=</span> <span class="n">struct</span><span class="o">.</span><span class="n">Struct</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">s </span><span class="si">{}</span><span class="s2">s"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">nome</span><span class="o">.</span><span class="n">split</span><span class="p">()[</span><span class="mi">0</span><span class="p">]),</span> <span class="nb">int</span><span class="p">(</span><span class="n">dados</span><span class="o">.</span><span class="n">split</span><span class="p">()[</span><span class="mi">0</span><span class="p">])))</span>
+ <span class="n">novo_sock</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"Pode enviar!"</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span> <span class="c1"># Enviaremos uma mensagem para o cliente enviar os dados.</span>
+ <span class="n">dados</span> <span class="o">=</span> <span class="n">novo_sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> <span class="c1"># Agora iremos esperar por eles.</span>
+ <span class="n">nome</span><span class="p">,</span> <span class="n">arquivo</span> <span class="o">=</span> <span class="n">serializar</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">dados</span><span class="p">)</span> <span class="c1"># Vamos desempacotar os dados</span>
+ <span class="sd">"""</span>
+<span class="sd"> Agora enviamos uma mensagem dizendo que o arquivo foi recebido.</span>
+
+<span class="sd"> E iremos salva-lo no novo diretório criado.</span>
+<span class="sd"> """</span>
+ <span class="n">novo_sock</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"Os dados do arquivo </span><span class="si">{}</span><span class="s2"> foram enviados."</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">nome</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span>
+ <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s2">"arquivos_recebidos/</span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">nome</span><span class="o">.</span><span class="n">decode</span><span class="p">()),</span> <span class="s1">'wb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">novo_arquivo</span><span class="p">:</span>
+ <span class="n">novo_arquivo</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">arquivo</span><span class="p">)</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s2">"Arquivo </span><span class="si">{}</span><span class="s2"> salvo em arquivos_recebidos."</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">nome</span><span class="o">.</span><span class="n">decode</span><span class="p">()))</span>
+
+ <span class="n">Arquivo</span> <span class="n">arquivo_para_upload</span> <span class="n">salvo</span> <span class="n">em</span> <span class="n">arquivos_recebidos</span><span class="o">.</span>
+</pre></div>
+
+
+<blockquote>
+<p>Cliente</p>
+</blockquote>
+<p>Nosso cliente irá usar o metodo <em>connect</em> para se conectar no servidor e a partir dai começar enviar e receber mensagens. Ele também recebe como parâmetros uma tupla com o host e porta de conexão do servidor.</p>
+<div class="highlight"><pre><span></span><span class="n">host</span> <span class="o">=</span> <span class="s1">'127.0.0.1'</span>
+<span class="n">porta</span> <span class="o">=</span> <span class="mi">6124</span>
+<span class="n">sock</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span> <span class="c1"># Cria nosso objeto socket</span>
+<span class="n">sock</span><span class="o">.</span><span class="n">connect</span><span class="p">((</span><span class="n">host</span><span class="p">,</span> <span class="n">porta</span><span class="p">))</span>
+<span class="n">sock</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"Enviarei um arquivo chamado: </span><span class="si">{}</span><span class="s2"> contendo: </span><span class="si">{}</span><span class="s2"> bytes"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
+ <span class="n">arquivo</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">dados_arquivo</span><span class="p">))</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span> <span class="c1"># Enviamos a mensagem com o nome e tamanho do arquivo.</span>
+<span class="n">ouvir</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> <span class="c1"># Aguardamos uma mensagem de confirmação do servidor.</span>
+<span class="k">if</span> <span class="n">ouvir</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> <span class="o">==</span> <span class="s2">"Pode enviar!"</span><span class="p">:</span>
+ <span class="n">sock</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">dados_upload</span><span class="p">)</span> <span class="c1"># Enviamos os dados empacotados.</span>
+ <span class="n">resposta</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> <span class="c1"># Aguardamos a confirmação de que os dados foram enviados.</span>
+ <span class="nb">print</span><span class="p">(</span><span class="n">resposta</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span>
+
+ <span class="n">Os</span> <span class="n">dados</span> <span class="n">do</span> <span class="n">arquivo</span> <span class="n">arquivo_para_upload</span> <span class="n">foram</span> <span class="n">enviados</span><span class="o">.</span>
+</pre></div>
+
+
+<p>Agora podemos checar nossos arquivos e ver se eles foram salvos corretamente.</p>
+<div class="highlight"><pre><span></span><span class="err">!</span><span class="n">md5sum</span> <span class="n">arquivo_para_upload</span><span class="p">;</span> <span class="n">md5sum</span> <span class="n">arquivos_recebidos</span><span class="o">/</span><span class="n">arquivos_para_upload</span>
+
+ <span class="mf">605e99</span><span class="n">b3d873df0b91d8834ff292d320</span> <span class="n">arquivo_para_upload</span>
+ <span class="mf">605e99</span><span class="n">b3d873df0b91d8834ff292d320</span> <span class="n">arquivos_recebidos</span><span class="o">/</span><span class="n">arquivo_para_upload</span>
+</pre></div>
+
+
+<p>Com base nesse exemplo, podem ser enviados diversos arquivos, sendo eles texto, arquivos compactados ou binários.</p>
+<p>Sem mais delongas, fiquem com Cher e até a próxima!</p>
+<p>Abraços.</p>
+ 2018-05-17T22:24:00+00:00
+ Silvio Ap Silva
+
+
+ PythonClub: Monitorando Ips Duplicados na Rede
+ https://pythonclub.com.br/monitorando-ips-duplicados-na-rede.html
+ <p>Muitos administradores de redes e sysadmins encontram problemas de conectividade nos ambientes que administram e por muitas vezes o problema é um simples IP duplicado causando todo mal estar. Agora veremos como usar o scapy e defaultdict da lib collections para monitorar esses IPs.</p>
+<h3>Scapy</h3>
+<p>O Scapy é uma poderosa biblioteca de manipulação de pacotes interativa, com abrangencia a uma enorme quantidade de protocolos provenientes da suite TCP/IP.
+Mais informações sobre o scpay pode ser encontrada na <a href="http://scapy.readthedocs.io/en/latest/index.html"><strong>documentação oficial</strong></a>.
+Nesse caso em especifico iremos utilizar do scapy a metaclasse <em>ARP</em> e a função <em>sniff</em>.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">scapy.all</span> <span class="kn">import</span> <span class="n">ARP</span><span class="p">,</span> <span class="n">sniff</span>
+</pre></div>
+
+
+<h4>sniff</h4>
+<p>Vamos usar a função sniff para monitorar os pacotes que trafegam na rede usando o protocolo ARP.
+Pra isso vamos utilizar dela quatro parametros basicos:</p>
+<div class="highlight"><pre><span></span><span class="n">sniff</span><span class="p">(</span><span class="n">prn</span><span class="o">=</span><span class="n">pacotes</span><span class="p">,</span> <span class="nb">filter</span><span class="o">=</span><span class="s2">"arp"</span><span class="p">,</span> <span class="n">iface</span><span class="o">=</span><span class="n">interface</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
+</pre></div>
+
+
+<ul>
+<li>
+<p>prn, chama uma função para ser aplicada a cada pacote capturado pelo sniff.</p>
+</li>
+<li>
+<p>filter, irá filtrar todos os pacotes que contiverem o protocolo ARP.</p>
+</li>
+<li>
+<p>iface, determina a interface de rede que será monitorada.</p>
+</li>
+<li>
+<p>timeout, irá determinar que nosso monitoramento da rede se dara por 60 segundos.</p>
+</li>
+</ul>
+<h4>ARP</h4>
+<p>ARP é uma metaclasse de pacotes com dados sobre o protocolo arp pertencente a camada de enlace de dados.
+Iremos utilizar essa metaclasse para filtrar os pacotes com informações de pacotes com respostas a requisições arp. (opcode == 2 [is at])
+As informações sobre o protocolo ARP podem serm encontradas na <a href="https://tools.ietf.org/html/rfc826">rfc826</a> no site do IETF.</p>
+<h3>collections.defaultdict</h3>
+<p>defaultdict é uma subclasse de dict que prove uma instancia de variavel para a chamada de uma chave inexistente.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">defaultdict</span>
+<span class="n">list_ips</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">set</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Basicamente nossa função irá monitorar por um certo tempo o trafego de pacotes pela rede adicionar a nossa variavel <em>list_ips</em> o endereço ou endereços MAC encontrados.</p>
+<h3>Definindo a função que será passada como parametro para o sniff.</h3>
+<p>Para cada pacote capturado pela função sniff, será checado se o opcode corresponde a um response do protocolo arp.
+Caso seja, sera adicionado a nossa defaultdict.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">pacotes</span><span class="p">(</span><span class="n">pacote</span><span class="p">):</span>
+ <span class="sd">"""Checa se o valor do opcode dentro do protocolo arp é igual a 2."""</span>
+ <span class="k">if</span> <span class="n">pacote</span><span class="p">[</span><span class="n">ARP</span><span class="p">]</span><span class="o">.</span><span class="n">op</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
+ <span class="c1"># Se for adiciona o ip de origem e seu mac à dict list_ips</span>
+ <span class="n">list_ips</span><span class="p">[</span><span class="n">pacote</span><span class="p">[</span><span class="n">ARP</span><span class="p">]</span><span class="o">.</span><span class="n">psrc</span><span class="p">]</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">pacote</span><span class="p">[</span><span class="n">ARP</span><span class="p">]</span><span class="o">.</span><span class="n">hwsrc</span><span class="p">)</span>
+</pre></div>
+
+
+<h3>Limpando a tabela arp</h3>
+<p>Para que seja feita novas requisições arp, iremos limpar nossa tabela arp e iniciar o monitoramento da rede.
+Pra isso iremos usar o comando arp, dentro do shell do sistema. <em>(Como uso <a href="https://www.freebsd.org">FreeBSD</a> vou definir uma função chamando um comando pelo csh)</em></p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">os</span>
+<span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s1">'which arp'</span><span class="p">)</span>
+<span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">sbin</span><span class="o">/</span><span class="n">arp</span>
+</pre></div>
+
+
+<p>Com posse do caminho do comando arp, irei definir uma função que limpe a tabela e inicie o monitore a rede por 60 segundos.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">monitorar</span><span class="p">(</span><span class="n">interface</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> O comando arp no FreeBSD usa os parametros:</span>
+
+<span class="sd"> -d para deletar as entradas</span>
+<span class="sd"> -i para declarar a interface</span>
+<span class="sd"> -a para representar todas entradas a serem deletas.</span>
+<span class="sd"> """</span>
+ <span class="n">cmd</span> <span class="o">=</span> <span class="s2">"/usr/sbin/arp -d -i </span><span class="si">{}</span><span class="s2"> -a"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">interface</span><span class="p">)</span>
+ <span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="n">cmd</span><span class="p">)</span>
+ <span class="n">sniff</span><span class="p">(</span><span class="n">prn</span><span class="o">=</span><span class="n">pacotes</span><span class="p">,</span> <span class="nb">filter</span><span class="o">=</span><span class="s2">"arp"</span><span class="p">,</span> <span class="n">iface</span><span class="o">=</span><span class="n">interface</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
+</pre></div>
+
+
+<p>E por ultimo chamar a função de monitoramento.
+No meu caso eu vou monitorar a interface <strong>em0</strong>.</p>
+<div class="highlight"><pre><span></span><span class="n">monitorar</span><span class="p">(</span><span class="s2">"em0"</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Agora só conferir as entradas em nossa dict.</p>
+<div class="highlight"><pre><span></span><span class="k">for</span> <span class="n">ip</span> <span class="ow">in</span> <span class="n">list_ips</span><span class="p">:</span>
+ <span class="nb">print</span> <span class="s2">"IP: </span><span class="si">{}</span><span class="s2"> -> MACs: </span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ip</span><span class="p">,</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">list_ips</span><span class="p">[</span><span class="n">ip</span><span class="p">])))</span>
+
+<span class="n">IP</span><span class="p">:</span> <span class="mf">192.168</span><span class="o">.</span><span class="mf">213.1</span> <span class="o">-></span> <span class="n">MACs</span><span class="p">:</span> <span class="mi">00</span><span class="p">:</span><span class="mi">90</span><span class="p">:</span><span class="mi">0</span><span class="n">b</span><span class="p">:</span><span class="mi">49</span><span class="p">:</span><span class="mi">3</span><span class="n">d</span><span class="p">:</span><span class="mi">0</span><span class="n">a</span>
+<span class="n">IP</span><span class="p">:</span> <span class="mf">192.168</span><span class="o">.</span><span class="mf">213.10</span> <span class="o">-></span> <span class="n">MACs</span><span class="p">:</span> <span class="mi">08</span><span class="p">:</span><span class="mi">00</span><span class="p">:</span><span class="mi">27</span><span class="p">:</span><span class="n">bf</span><span class="p">:</span><span class="mi">52</span><span class="p">:</span><span class="mi">6</span><span class="n">d</span><span class="p">,</span> <span class="n">a0</span><span class="p">:</span><span class="n">f3</span><span class="p">:</span><span class="n">c1</span><span class="p">:</span><span class="mi">03</span><span class="p">:</span><span class="mi">74</span><span class="p">:</span><span class="mi">6</span><span class="n">a</span>
+</pre></div>
+
+
+<p>Eu uso um script rodando nos switchs e gateway da rede que me enviam mensagens assim que ips duplicados são encontrados na rede.
+Também da pra usar o <strong>arping</strong> do scpay para fazer as requisições arp e coletar os responses.</p>
+<p>Abraços.</p>
+ 2018-05-15T13:24:00+00:00
+ Silvio Ap Silva
+
+
+ Lauro Moura: C++ para Pythonistas – Introdução e iteradores
+
+ C++ pode ter a fama de ser uma linguagem com complexidade comparável à legislação tributária brasileira, mas ao mesmo tempo é uma linguagem extremamente poderosa, podendo ser usada tanto desde microcontroladores até super computadores, sem contar satélites espaciais. Tamanha complexidade pode assustar quem vem de linguagens mais simples como Python (malloc? rvalue references?). Uma ajuda … <a class="more-link" href="https://lauro.wordpress.com/2018/04/30/c-para-pythonistas-introducao-e-iteradores/">Continuar lendo <span class="meta-nav">→</span></a>
+ 2018-05-01T02:10:28+00:00
+
+
+ Magnun Leno: Algumas Razões Para Amar o PostgresSQL
+ http://mindbending.org/pt/algumas-razoes-para-amar-o-postgressql
+ <p>Geralmente, quando se fala em SGBDs OpenSource, a primeira resposta que se ouve é MySQL/MariaDB. Eu sempre torço meu nariz para respostas como essa… Implicancia pessoal? Talvez um pouco, mas existem <a class="reference external" href="https://www.cybertec-postgresql.com/en/why-favor-postgresql-over-mariadb-mysql/" target="_blank">muitos fundamentos</a>…</p>
+<p><a href="http://mindbending.org/pt/algumas-razoes-para-amar-o-postgressql">Algumas Razões Para Amar o PostgresSQL</a> é um artigo original de <a href="http://mindbending.org/pt">Mind Bending</a></p>
+ 2018-04-19T18:02:00+00:00
+
+
+ Python Help: Preservando a ordem de sequências ao remover duplicatas
+
+ Imagine que você tenha uma lista com as URLs extraídas de uma página web e queira eliminar as duplicatas da mesma. Transformar a lista em um conjunto (set) talvez seja a forma mais comum de se fazer isso. Tipo assim: Porém, observe que perdemos a ordem original dos elementos. Esse é um efeito colateral indesejado […]
+ 2018-02-26T01:31:38+00:00
+
+
+ PythonClub: Django Rest Framework - #3 Class Based Views
+ https://pythonclub.com.br/django-rest-framework-class-based-views.html
+ <ul>
+<li>0 - <a href="http://pythonclub.com.br/django-rest-framework-quickstart.html">Quickstart</a></li>
+<li>1 - <a href="http://pythonclub.com.br/django-rest-framework-serialization.html">Serialization</a></li>
+<li>2 - <a href="http://pythonclub.com.br/django-rest-framework-requests-responses.html">Requests & Responses</a></li>
+<li>3 - <strong>Class based views</strong></li>
+</ul>
+<p>Este post é continuação do post <a href="http://pythonclub.com.br/django-rest-framework-requests-responses.html">Django Rest Framework Requests & Responses</a>.</p>
+<p>Finalmente chegamos as views baseadas em classes. A grande vantagem é que com poucas linhas de código já temos nossa API pronta.</p>
+<p>Veja como fica a <a href="https://github.com/rg3915/drf/blob/b0aa989ffc756e6dc5f65e172dfb43d47127d743/core/views.py">views.py</a>:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.http</span> <span class="kn">import</span> <span class="n">Http404</span>
+<span class="kn">from</span> <span class="nn">rest_framework.views</span> <span class="kn">import</span> <span class="n">APIView</span>
+<span class="kn">from</span> <span class="nn">rest_framework.response</span> <span class="kn">import</span> <span class="n">Response</span>
+<span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">status</span>
+<span class="kn">from</span> <span class="nn">core.models</span> <span class="kn">import</span> <span class="n">Person</span>
+<span class="kn">from</span> <span class="nn">core.serializers</span> <span class="kn">import</span> <span class="n">PersonSerializer</span>
+
+
+<span class="k">class</span> <span class="nc">PersonList</span><span class="p">(</span><span class="n">APIView</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> List all persons, or create a new person.</span>
+<span class="sd"> """</span>
+
+ <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+ <span class="n">persons</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">persons</span><span class="p">,</span> <span class="n">many</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">serializer</span><span class="o">.</span><span class="n">is_valid</span><span class="p">():</span>
+ <span class="n">serializer</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_201_CREATED</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">errors</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_400_BAD_REQUEST</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">PersonDetail</span><span class="p">(</span><span class="n">APIView</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Retrieve, update or delete a person instance.</span>
+<span class="sd"> """</span>
+
+ <span class="k">def</span> <span class="nf">get_object</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">pk</span><span class="p">):</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">pk</span><span class="o">=</span><span class="n">pk</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">Person</span><span class="o">.</span><span class="n">DoesNotExist</span><span class="p">:</span>
+ <span class="k">raise</span> <span class="n">Http404</span>
+
+ <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+ <span class="n">person</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_object</span><span class="p">(</span><span class="n">pk</span><span class="p">)</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">person</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">put</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+ <span class="n">person</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_object</span><span class="p">(</span><span class="n">pk</span><span class="p">)</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">person</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">serializer</span><span class="o">.</span><span class="n">is_valid</span><span class="p">():</span>
+ <span class="n">serializer</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">errors</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_400_BAD_REQUEST</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">delete</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+ <span class="n">person</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_object</span><span class="p">(</span><span class="n">pk</span><span class="p">)</span>
+ <span class="n">person</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_204_NO_CONTENT</span><span class="p">)</span>
+</pre></div>
+
+
+<p>E <code>urls.py</code>:</p>
+<div class="highlight"><pre><span></span><span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
+ <span class="n">path</span><span class="p">(</span><span class="s1">'persons/'</span><span class="p">,</span> <span class="n">views</span><span class="o">.</span><span class="n">PersonList</span><span class="o">.</span><span class="n">as_view</span><span class="p">()),</span>
+ <span class="n">path</span><span class="p">(</span><span class="s1">'persons/<int:pk>/'</span><span class="p">,</span> <span class="n">views</span><span class="o">.</span><span class="n">PersonDetail</span><span class="o">.</span><span class="n">as_view</span><span class="p">()),</span>
+<span class="p">]</span>
+</pre></div>
+
+
+<h2>Usando Mixins</h2>
+<p>Repare que no exemplo anterior tivemos que definir os métodos <code>get()</code>, <code>post()</code>, <code>put()</code> e <code>delete()</code>. Podemos reduzir ainda mais esse código com o uso de mixins.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">mixins</span>
+<span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">generics</span>
+<span class="kn">from</span> <span class="nn">core.models</span> <span class="kn">import</span> <span class="n">Person</span>
+<span class="kn">from</span> <span class="nn">core.serializers</span> <span class="kn">import</span> <span class="n">PersonSerializer</span>
+
+
+<span class="k">class</span> <span class="nc">PersonList</span><span class="p">(</span><span class="n">mixins</span><span class="o">.</span><span class="n">ListModelMixin</span><span class="p">,</span>
+ <span class="n">mixins</span><span class="o">.</span><span class="n">CreateModelMixin</span><span class="p">,</span>
+ <span class="n">generics</span><span class="o">.</span><span class="n">GenericAPIView</span><span class="p">):</span>
+ <span class="n">queryset</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">PersonSerializer</span>
+
+ <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">PersonDetail</span><span class="p">(</span><span class="n">mixins</span><span class="o">.</span><span class="n">RetrieveModelMixin</span><span class="p">,</span>
+ <span class="n">mixins</span><span class="o">.</span><span class="n">UpdateModelMixin</span><span class="p">,</span>
+ <span class="n">mixins</span><span class="o">.</span><span class="n">DestroyModelMixin</span><span class="p">,</span>
+ <span class="n">generics</span><span class="o">.</span><span class="n">GenericAPIView</span><span class="p">):</span>
+ <span class="n">queryset</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">PersonSerializer</span>
+
+ <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">retrieve</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">put</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">delete</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">destroy</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+</pre></div>
+
+
+<h2>Usando generic class-based views</h2>
+<p>E para finalizar usamos <code>ListCreateAPIView</code> e <code>RetrieveUpdateDestroyAPIView</code> que já tem todos os métodos embutidos.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">generics</span>
+<span class="kn">from</span> <span class="nn">core.models</span> <span class="kn">import</span> <span class="n">Person</span>
+<span class="kn">from</span> <span class="nn">core.serializers</span> <span class="kn">import</span> <span class="n">PersonSerializer</span>
+
+
+<span class="k">class</span> <span class="nc">PersonList</span><span class="p">(</span><span class="n">generics</span><span class="o">.</span><span class="n">ListCreateAPIView</span><span class="p">):</span>
+ <span class="n">queryset</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">PersonSerializer</span>
+
+
+<span class="k">class</span> <span class="nc">PersonDetail</span><span class="p">(</span><span class="n">generics</span><span class="o">.</span><span class="n">RetrieveUpdateDestroyAPIView</span><span class="p">):</span>
+ <span class="n">queryset</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">PersonSerializer</span>
+</pre></div>
+
+
+<p>Versão final de <a href="https://github.com/rg3915/drf/blob/263ca63e5c8e2dd2e0f5d6c88c5733fcfdab4f74/core/views.py">views.py</a>.</p>
+<p>Abraços.</p>
+ 2018-02-16T01:00:00+00:00
+ Regis da Silva
+
+
+ PythonClub: Django Rest Framework - #2 Requests and Responses
+ https://pythonclub.com.br/django-rest-framework-requests-responses.html
+ <ul>
+<li>0 - <a href="http://pythonclub.com.br/django-rest-framework-quickstart.html">Quickstart</a></li>
+<li>1 - <a href="http://pythonclub.com.br/django-rest-framework-serialization.html">Serialization</a></li>
+<li>2 - <strong>Requests & Responses</strong></li>
+<li>3 - <a href="http://pythonclub.com.br/django-rest-framework-class-based-views.html">Class based views</a></li>
+</ul>
+<p>Este post é continuação do post <a href="http://pythonclub.com.br/django-rest-framework-serialization.html">Django Rest Framework Serialization</a>.</p>
+<p>O uso de <em>requests</em> e <em>responses</em> torna nossa api mais flexível. A funcionalidade principal do objeto <strong>Request</strong> é o atributo <code>request.data</code>, que é semelhante ao <code>request.POST</code>, mas é mais útil para trabalhar com APIs.</p>
+<h2>Objeto Response</h2>
+<p>Introduzimos aqui um objeto <code>Response</code>, que é um tipo de <code>TemplateResponse</code> que leva conteúdo não renderizado e usa a negociação de conteúdo para determinar o tipo de conteúdo correto para retornar ao cliente.</p>
+<div class="highlight"><pre><span></span><span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="c1"># Renderiza para o tipo de conteúdo conforme solicitado pelo cliente.</span>
+</pre></div>
+
+
+<p>Repare também no uso de <em>status code</em> pré definidos, exemplo: <code>status.HTTP_400_BAD_REQUEST</code>.</p>
+<p>E usamos o decorador <code>@api_view</code> para trabalhar com funções. Ou <code>APIView</code> para classes.</p>
+<p>Nosso código ficou assim:</p>
+<div class="highlight"><pre><span></span><span class="c1"># views.py</span>
+<span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">status</span>
+<span class="kn">from</span> <span class="nn">rest_framework.decorators</span> <span class="kn">import</span> <span class="n">api_view</span>
+<span class="kn">from</span> <span class="nn">rest_framework.response</span> <span class="kn">import</span> <span class="n">Response</span>
+<span class="kn">from</span> <span class="nn">core.models</span> <span class="kn">import</span> <span class="n">Person</span>
+<span class="kn">from</span> <span class="nn">core.serializers</span> <span class="kn">import</span> <span class="n">PersonSerializer</span>
+
+
+<span class="nd">@api_view</span><span class="p">([</span><span class="s1">'GET'</span><span class="p">,</span> <span class="s1">'POST'</span><span class="p">])</span>
+<span class="k">def</span> <span class="nf">person_list</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> List all persons, or create a new person.</span>
+<span class="sd"> """</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'GET'</span><span class="p">:</span>
+ <span class="n">persons</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">persons</span><span class="p">,</span> <span class="n">many</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+
+ <span class="k">elif</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'POST'</span><span class="p">:</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">serializer</span><span class="o">.</span><span class="n">is_valid</span><span class="p">():</span>
+ <span class="n">serializer</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_201_CREATED</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">errors</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_400_BAD_REQUEST</span><span class="p">)</span>
+
+
+<span class="nd">@api_view</span><span class="p">([</span><span class="s1">'GET'</span><span class="p">,</span> <span class="s1">'PUT'</span><span class="p">,</span> <span class="s1">'DELETE'</span><span class="p">])</span>
+<span class="k">def</span> <span class="nf">person_detail</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Retrieve, update or delete a person instance.</span>
+<span class="sd"> """</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">person</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">pk</span><span class="o">=</span><span class="n">pk</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">Person</span><span class="o">.</span><span class="n">DoesNotExist</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_404_NOT_FOUND</span><span class="p">)</span>
+
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'GET'</span><span class="p">:</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">person</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+
+ <span class="k">elif</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'PUT'</span><span class="p">:</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">person</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">serializer</span><span class="o">.</span><span class="n">is_valid</span><span class="p">():</span>
+ <span class="n">serializer</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">errors</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_400_BAD_REQUEST</span><span class="p">)</span>
+
+ <span class="k">elif</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'DELETE'</span><span class="p">:</span>
+ <span class="n">person</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_204_NO_CONTENT</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Veja no <a href="https://github.com/rg3915/drf/commit/69205da9262415eaf83ff04f22a635e912880a60">GitHub</a>.</p>
+<h2>Usando sufixo opcional</h2>
+<p>Em <code>core/urls.py</code> acrescente</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">rest_framework.urlpatterns</span> <span class="kn">import</span> <span class="n">format_suffix_patterns</span>
+
+<span class="o">...</span>
+
+<span class="n">urlpatterns</span> <span class="o">=</span> <span class="n">format_suffix_patterns</span><span class="p">(</span><span class="n">urlpatterns</span><span class="p">)</span>
+</pre></div>
+
+
+<p>E em <code>views.py</code> acrescente <code>format=None</code> como parâmetro das funções a seguir:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">person_list</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+
+<span class="k">def</span> <span class="nf">person_detail</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+</pre></div>
+
+
+<p>Com isso você pode chamar a api da seguinte forma:</p>
+<div class="highlight"><pre><span></span>http http://127.0.0.1:8000/persons.json <span class="c1"># ou</span>
+http http://127.0.0.1:8000/persons.api
+</pre></div>
+
+
+<p>Até a próxima.</p>
+ 2018-02-15T01:00:00+00:00
+ Regis da Silva
+
+
+ PythonClub: Programação funcional com Python #2 - Iteraveis e iteradores
+ https://pythonclub.com.br/progrmacao-funcional-com-python-2.html
+ <h1>2. Iteráveis e iteradores</h1>
+<p>O que são iteráveis? Basicamente e a grosso modo, iteráveis em python são todos os objetos que implementam o método <code>__getitem__</code> ou <code>__iter__</code>. Beleza, vamos partir do simples.</p>
+<p>Quase todos os tipos de dados em python são iteráveis, por exemplo: listas, strings, tuplas, dicionários, conjuntos, etc...</p>
+<p>Vamos aos exemplos, eles são sempre mágicos:</p>
+<div class="highlight"><pre><span></span><span class="n">lista</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">]</span>
+
+<span class="c1"># iteração</span>
+<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">lista</span><span class="p">:</span>
+ <span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
+
+<span class="c1"># 1</span>
+<span class="c1"># 2</span>
+<span class="c1"># 3</span>
+<span class="c1"># 4</span>
+<span class="c1"># 5</span>
+</pre></div>
+
+
+<p>Era só isso? Sim, nem doeu, fala a verdade.</p>
+<p>Em python, o comando <code>for</code> nos fornece um iterador implícito. O que? Não entendi.</p>
+<p>O laço <code>for</code> em python itera em cada elemento da sequência. Como no exemplo, o <code>for</code>, ou <code>foreach</code>, no caso vai passando por cada elemento da sequência. Não é necessária a implementação de um index como na linguagem C, onde a iteração é explícita:</p>
+<div class="highlight"><pre><span></span><span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">></span> <span class="mi">10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
+ <span class="n">sequencia</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<h2>2.1 <code>__getitem__</code></h2>
+<p>O padrão de projeto iterator em python já vem implementado por padrão, como já foi dito antes. Basta que um objeto tenha os métodos <code>__iter__</code> ou <code>__getitem__</code> para que um laço possa ser utilizado.</p>
+<p>Vamos exemplificar:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">iteravel</span><span class="p">:</span>
+ <span class="sd">"""</span>
+<span class="sd"> Um objeto que implementa o `__getitem__` pode ser acessado por posição</span>
+<span class="sd"> """</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sequencia</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">seq</span> <span class="o">=</span> <span class="n">sequencia</span>
+
+ <span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">posicao</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Por exemplo, quando tentamos acessar um elemento da sequência usando</span>
+<span class="sd"> slice:</span>
+<span class="sd"> >>> iteravel[2]</span>
+
+<span class="sd"> O interpretador python chama o __getitem__ do objeto e nos retorna</span>
+<span class="sd"> a posição solicitada</span>
+
+<span class="sd"> um exemplo:</span>
+<span class="sd"> IN: lista = [1, 2, 3, 4]</span>
+<span class="sd"> IN: lista[0]</span>
+<span class="sd"> OUT: 1</span>
+<span class="sd"> """</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">seq</span><span class="p">[</span><span class="n">posicao</span><span class="p">]</span>
+</pre></div>
+
+
+<p>Então, pode-se compreender, sendo bem rústico, que todos os objetos que implementam <code>__getitem__</code> são iteráveis em python.</p>
+<h2>2.2 <code>__iter__</code></h2>
+<p>Agora os objetos que implementam <code>__iter__</code> tem algumas peculiaridades. Por exemplo, quando o iterável (vamos pensar no <code>for</code>) chamar a sequência, ela vai pedir o <code>__iter__</code> que vai retornar uma instância de si mesmo para o <code>for</code> e ele vai chamar o <code>__next__</code> até que a exceção <code>StopIteration</code> aconteça.</p>
+<p>Uma classe que implementa <code>__iter__</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">iteravel</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sequencia</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">sequencia</span>
+
+ <span class="k">def</span> <span class="fm">__next__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Neste caso, como o método pop do objeto data é chamado</span>
+<span class="sd"> (vamos pensar em uma lista) ele vai retornar o primeiro elemento</span>
+<span class="sd"> (o de index 0) e vai remove-lo da lista</span>
+
+<span class="sd"> E quando a sequência estiver vazia ele vai nos retornar um StopIteration</span>
+<span class="sd"> O que vai fazer com que a iteração acabe.</span>
+
+<span class="sd"> Porém, estamos iterando com pop, o que faz a nossa iteração ser a única,</span>
+<span class="sd"> pois ela não pode ser repetida, dado que os elementos foram</span>
+<span class="sd"> removidos da lista</span>
+<span class="sd"> """</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">sequencia</span><span class="p">:</span>
+ <span class="k">raise</span> <span class="ne">StopIteration</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">sequencia</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span>
+</pre></div>
+
+
+<p>Como é possível notar, o objeto com <code>__iter__</code> não necessita de um <code>__getitem__</code> e vise-versa. As diferenças partem do ponto em que um pode ser acessado por index/slice e outro não. Também um bom ponto é que nesse nosso caso, removemos os elementos da sequência, ou seja, ela se torna descartável.</p>
+<p>Esse conceito, de ser descartado, pode parecer um pouco estranho no início, mas economiza muita memória. E, como estamos falando de programação funcional, pode-se dizer que nossa sequência se torna imutável, pois não existe uma maneira de mudar os valores contidos na lista do objeto.</p>
+<p>Seguem dois links maravilhosos explicando sobre iteração em python:</p>
+<ul>
+<li>
+<p><a href="https://www.python.org/dev/peps/pep-0234/">PEP0243</a></p>
+</li>
+<li>
+<p><a href="https://www.youtube.com/watch?v=ULj7ejvuzI8">Iteração em Python: do básico ao genial</a></p>
+</li>
+</ul>
+<p>O primeiro é a PEP sobre as estruturas dos iteráveis e o segundo um video do Guru Luciano Ramalho explicando tudo sobre iteradores.</p>
+<p>Ah... Ia quase me esquecendo, se você não entendeu muita coisa sobre os dunders, você pode ler o <a href="https://docs.python.org/3/reference/datamodel.html#special-method-names">Python data model</a>. Obs: não me responsabilizo pelo programador melhor que você sairá desta página.</p>
+<p>Embora esse tópico seja talvez o mais curto, ele vai ser de fundamental importância para o entendimento de um pouco de tudo nesse 'Curso'. É sério. Vamos entender como trabalhar com iteráveis de uma maneira bonita no próximo tópico.</p>
+ 2017-12-24T00:50:00+00:00
+ Eduardo Mendes
+
+
+ PythonClub: Programação funcional com Python #1 - Funções
+ https://pythonclub.com.br/progrmacao-funcional-com-python-1.html
+ <h1>1. Funções</h1>
+<p>Como nem tudo são flores, vamos começar do começo e entender algumas características das funções do python (o objeto função) e dar uma revisada básica em alguns conceitos de função só pra gente não se perder no básico depois. Então o primeiro tópico vai se limitar a falar da estrutura básica das funções em python, sem entrar profundamente em cada um dos tópicos. Será uma explanação de código e abrir a cabeça para novas oportunidades de código mais pythonicos e que preferencialmente gere menos efeito colateral. Mas calma, não vamos ensinar a fazer funções, você já está cheio disso.</p>
+<h2>1.1 Funções como objeto de primeira classe</h2>
+<p>Funções como objeto de primeira classe, são funções que se comportam como qualquer tipo nativo de uma determinada linguagem. Por exemplo:</p>
+<div class="highlight"><pre><span></span><span class="c1"># uma lista</span>
+
+<span class="n">lista</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="s1">'str'</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">],</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">),</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">},</span> <span class="p">{</span><span class="mi">1</span><span class="p">:</span> <span class="s1">'um'</span><span class="p">}]</span>
+</pre></div>
+
+
+<p>Todos esses exemplos são tipos de objetos de primeira classe em Python, mas no caso as funções também são. Como assim? Pode-se passar funções como parâmetro de uma outra função, podemos armazenar funções em variáveis, pode-se definir funções em estruturas de dados:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Funções como objeto de primeira classe</span>
+
+<span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="c1"># a função anônima, lambda, foi armazenada em uma variável</span>
+
+<span class="k">def</span> <span class="nf">func_2</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">2</span>
+
+<span class="n">lista</span> <span class="o">=</span> <span class="p">[</span><span class="n">func</span><span class="p">,</span> <span class="n">func_2</span><span class="p">]</span> <span class="c1"># a variável que armazena a função foi inserida em uma estrutura, assim como uma função gerada com def</span>
+
+<span class="n">lista_2</span> <span class="o">=</span> <span class="p">[</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="c1"># aqui as funções foram definidas dentro de uma estrutura</span>
+</pre></div>
+
+
+<p>Como é possível notar, em python, as funções podem ser inseridas em qualquer contexto e também geradas em tempo de execução. Com isso nós podemos, além de inserir funções em estruturas, retornar funções, passar funções como parâmetro (HOFs), definir funções dentro de funções(closures) e assim por diante. Caso você tenha aprendido a programar usando uma linguagem em que as funções não são objetos de primeira classe, não se assuste. Isso faz parte da rotina comum do python. Preferencialmente, e quase obrigatoriamente, é melhor fazer funções simples, pequenas e de pouca complexidade para que elas não sofram interferência do meio externo, gerem menos manutenção e o melhor de tudo, possam ser combinadas em outras funções. Então vamos lá!</p>
+<h2>1.2 Funções puras</h2>
+<p>Funções puras são funções que não sofrem interferência do meio externo. Vamos começar pelo exemplo ruim:</p>
+<div class="highlight"><pre><span></span><span class="n">valor</span> <span class="o">=</span> <span class="mi">5</span>
+
+<span class="k">def</span> <span class="nf">mais_cinco</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">valor</span>
+
+<span class="k">assert</span> <span class="n">mais_cinco</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="mi">10</span> <span class="c1"># True</span>
+
+<span class="n">valor</span> <span class="o">=</span> <span class="mi">7</span>
+
+<span class="k">assert</span> <span class="n">mais_cinco</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="mi">10</span> <span class="c1"># AssertionError</span>
+</pre></div>
+
+
+<p><code>mais_cinco()</code> é o exemplo claro de uma função que gera efeito colateral. Uma função pura deve funcionar como uma caixa preta, todas as vezes em que o mesmo input for dado nela, ela terá que retornar o mesmo valor. Agora vamos usar o mesmo exemplo, só alterando a linha do return:</p>
+<div class="highlight"><pre><span></span><span class="n">valor</span> <span class="o">=</span> <span class="mi">5</span>
+
+<span class="k">def</span> <span class="nf">mais_cinco</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">5</span>
+
+<span class="k">assert</span> <span class="n">mais_cinco</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="mi">10</span> <span class="c1"># True</span>
+
+<span class="n">valor</span> <span class="o">=</span> <span class="mi">7</span>
+
+<span class="k">assert</span> <span class="n">mais_cinco</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="mi">10</span> <span class="c1"># True</span>
+</pre></div>
+
+
+<p>Pode parecer trivial, mas muitas vezes por comodidade deixamos o meio influenciar no comportamento de uma função. Por definição o Python só faz possível, e vamos falar disso em outro tópico, a leitura de variáveis externas. Ou seja, dentro do contexto da função as variáveis externas não podem ser modificadas, mas isso não impede que o contexto externo a modifique. Se você for uma pessoa inteligente como o Jaber deve saber que nunca é uma boa ideia usar valores externos. Mas, caso seja necessário, você pode sobrescrever o valor de uma variável no contexto global usando a palavra reservada <code>global</code>. O que deve ficar com uma cara assim:</p>
+<div class="highlight"><pre><span></span><span class="n">valor</span> <span class="o">=</span> <span class="mi">5</span>
+
+<span class="k">def</span> <span class="nf">teste</span><span class="p">():</span>
+ <span class="k">global</span> <span class="n">valor</span> <span class="c1"># aqui é feita a definição</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="mi">7</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">valor</span><span class="p">)</span> <span class="c1"># 7</span>
+</pre></div>
+
+
+<p>Só lembre-se de ser sempre coerente quando fizer isso, as consequências podem ser imprevisíveis. Nessa linha de funções puras e pequeninas, podemos caracterizar, embora isso não as defina, funções de ordem superior, que são funções que recebem uma função como argumento, ou as devolvem, e fazem a chamada das mesmas dentro do contexto da função que a recebeu como parâmetro. Isso resulta em uma composição de funções, o que agrega muito mais valor caso as funções não gerem efeitos colaterais.</p>
+<h2>1.3 Funções de ordem superior (HOFs)</h2>
+<p>Funções de ordem superior são funções que recebem funções como argumento(s) e/ou retornam funções como resposta. Existem muitas funções embutidas em python de ordem superior, como: <code>map, filter, zip</code> e praticamente todo o módulo functools <code>import functools</code>. Porém, nada impede de criarmos novas funções de ordem superior. Um ponto a ser lembrado é que map e filter não tem mais a devida importância em python com a entrada das comprehensions (embora eu as adore), o que nos faz escolher única e exclusivamente por gosto, apesar de comprehensions serem mais legíveis (vamos falar disso em outro contexto), existem muitos casos onde elas ainda fazem sentido. Mas sem me estender muito, vamos ao código:</p>
+<div class="highlight"><pre><span></span><span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">+</span><span class="mi">2</span> <span class="c1"># uma função simples, soma mais 2 a qualquer inteiro</span>
+
+<span class="k">def</span> <span class="nf">func_mais_2</span><span class="p">(</span><span class="n">funcao</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Executa a função passada por parâmetro e retorna esse valor somado com dois</span>
+
+<span class="sd"> Ou seja, é uma composição de funções:</span>
+
+<span class="sd"> Dado que func(valor) é processado por func_func:</span>
+<span class="sd"> func_mais_2(func(valor)) == f(g(x))</span>
+<span class="sd"> """</span>
+ <span class="k">return</span> <span class="n">funcao</span><span class="p">(</span><span class="n">valor</span><span class="p">)</span> <span class="o">+</span> <span class="mi">2</span>
+</pre></div>
+
+
+<p>Um ponto a tocar, e o que eu acho mais bonito, é que a função vai retornar diferentes respostas para o mesmo valor, variando a entrada da função. Nesse caso, dada a entrada de um inteiro ele será somado com 2 e depois com mais dois. Mas, vamos estender este exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">2</span> <span class="c1"># uma função simples, soma mais 2 a qualquer inteiro</span>
+
+<span class="k">def</span> <span class="nf">func_mais_2</span><span class="p">(</span><span class="n">funcao</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Função que usamos antes.</span>
+<span class="sd"> """</span>
+ <span class="k">return</span> <span class="n">funcao</span><span class="p">(</span><span class="n">valor</span><span class="p">)</span> <span class="o">+</span> <span class="mi">2</span>
+
+<span class="k">assert</span> <span class="n">func_mais_2</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">6</span> <span class="c1"># true</span>
+
+<span class="k">def</span> <span class="nf">func_quadrada</span><span class="p">(</span><span class="n">val</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Eleva o valor de entrada ao quadrado.</span>
+<span class="sd"> """</span>
+ <span class="k">return</span> <span class="n">val</span> <span class="o">*</span> <span class="n">val</span>
+
+<span class="k">assert</span> <span class="n">func_mais_2</span><span class="p">(</span><span class="n">func_quadrada</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">6</span> <span class="c1"># true</span>
+</pre></div>
+
+
+<h3>1.3.1 Um exemplo usando funções embutidas:</h3>
+<p>Muitas das funções embutidas em python são funções de ordem superior (HOFs) como a função map, que é uma das minhas preferidas. Uma função de map recebe uma função, que recebe um único argumento e devolve para nós uma nova lista com a função aplicada a cada elemento da lista:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="n">arg</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">arg</span> <span class="o">+</span> <span class="mi">2</span>
+
+<span class="n">lista</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">]</span>
+
+<span class="nb">list</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="n">lista</span><span class="p">))</span> <span class="o">==</span> <span class="p">[</span><span class="mi">4</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span> <span class="c1"># true</span>
+</pre></div>
+
+
+<p>Mas fique tranquilo, falaremos muito mais sobre isso.</p>
+<h2>1.4 <code>__call__</code></h2>
+<p>Por que falar de classes? Lembre-se, Python é uma linguagem construída em classes, e todos os objetos que podem ser chamados/invocados implementam o método <code>__call__</code>:</p>
+<p>Em uma função anônima:</p>
+<div class="highlight"><pre><span></span><span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span>
+
+<span class="s1">'__call__'</span> <span class="ow">in</span> <span class="nb">dir</span><span class="p">(</span><span class="n">func</span><span class="p">)</span> <span class="c1"># True</span>
+</pre></div>
+
+
+<p>Em funções tradicionais:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">x</span>
+
+<span class="s1">'__call__'</span> <span class="ow">in</span> <span class="nb">dir</span><span class="p">(</span><span class="n">func</span><span class="p">)</span> <span class="c1"># True</span>
+</pre></div>
+
+
+<p>Isso quer dizer que podemos gerar classes que se comportam como funções?</p>
+<p>SIIIIIM. Chupa Haskell</p>
+<p>Essa é uma parte interessante da estrutura de criação do Python a qual veremos mais em outro momento sobre introspecção de funções, mas vale a pena dizer que classes, funções nomeadas, funções anônimas e funções geradoras usam uma base comum para funcionarem, essa é uma das coisas mais bonitas em python e que em certo ponto fere a ortogonalidade da linguagem, pois coisas iguais tem funcionamentos diferentes, mas facilita o aprendizado da linguagem, mas não é nosso foco agora.</p>
+<h2>1.5 Funções geradoras</h2>
+<p>Embora faremos um tópico extremamente focado em funções geradoras, não custa nada dar uma palinha, não?</p>
+<p>Funções geradoras são funções que nos retornam um iterável. Mas ele é lazy (só é computado quando invocado). Para exemplo de uso, muitos conceitos precisam ser esclarecidos antes de entendermos profundamente o que acontece com elas, mas digo logo: são funções lindas <3</p>
+<p>Para que uma função seja geradora, em tese, só precisamos trocar o return por yield:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">gen</span><span class="p">(</span><span class="n">lista</span><span class="p">):</span>
+ <span class="k">for</span> <span class="n">elemento</span> <span class="ow">in</span> <span class="n">lista</span><span class="p">:</span>
+ <span class="k">yield</span> <span class="n">elemento</span>
+
+<span class="n">gerador</span> <span class="o">=</span> <span class="n">gen</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">])</span>
+
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># 1</span>
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># 2</span>
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># 3</span>
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># 4</span>
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># 5</span>
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># StopIteration</span>
+</pre></div>
+
+
+<p>Passando bem por cima, uma função geradora nos retorna um iterável que é preguiçoso. Ou seja, ele só vai efetuar a computação quando for chamado.</p>
+<h2>1.6 Funções anônimas (lambda)</h2>
+<p>Funções anônimas, ou funções lambda, são funções que podem ser declaradas em qualquer contexto. Tá... Todo tipo de função em python pode ser declarada em tempo de execução. Porém funções anônimas podem ser atribuídas a variáveis, podem ser definidas dentro de sequências e declaradas em um argumento de função. Vamos olhar sua sintaxe:</p>
+<div class="highlight"><pre><span></span><span class="k">lambda</span> <span class="n">argumento</span><span class="p">:</span> <span class="n">argumento</span>
+</pre></div>
+
+
+<p>A palavra reservada <code>lambda</code> define a função, assim como uma <code>def</code>. Porém em uma <code>def</code> quase que instintivamente sempre quebramos linha:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func</span><span class="p">():</span>
+ <span class="k">pass</span>
+</pre></div>
+
+
+<p>Uma das diferenças triviais em python é que as funções anônimas não tem nome. Tá, isso era meio óbvio, mas vamos averiguar:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func</span><span class="p">():</span>
+ <span class="k">pass</span>
+
+<span class="n">func</span><span class="o">.</span><span class="vm">__name__</span> <span class="c1"># func</span>
+
+<span class="n">lambda_func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">arg</span><span class="p">:</span> <span class="n">arg</span>
+
+<span class="n">lambda_func</span><span class="o">.</span><span class="vm">__name__</span> <span class="c1"># '<lambda>'</span>
+</pre></div>
+
+
+<p>O resultado <code>'<lambda>'</code> será o mesmo para qualquer função. Isso torna sua depuração praticamente impossível em python. Por isso os usuários de python (e nisso incluo todos os usuários, até aqueles que gostam de funcional) não encorajam o uso de funções lambda a todos os contextos da linguagem. Mas, em funções que aceitam outra funções isso é meio que uma tradição, caso a função (no caso a que executa o código a ser usado pelo lambda) não esteja definida e nem seja reaproveitada em outro contexto. Eu gosto de dizer que lambdas são muito funcionais em aplicações parciais de função. Porém, os lambdas não passam de açúcar sintático em Python, pois não há nada que uma função padrão (definida com <code>def</code>), não possa fazer de diferente. Até a introspecção retorna o mesmo resultado:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func</span><span class="p">():</span>
+ <span class="k">pass</span>
+
+<span class="nb">type</span><span class="p">(</span><span class="n">func</span><span class="p">)</span> <span class="c1"># function</span>
+
+<span class="n">lambda_func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">arg</span><span class="p">:</span> <span class="n">arg</span>
+
+<span class="nb">type</span><span class="p">(</span><span class="n">lambda_func</span><span class="p">)</span> <span class="c1"># function</span>
+</pre></div>
+
+
+<p>Uma coisa que vale ser lembrada é que funções anônimas em python só executam uma expressão. Ou seja, não podemos usar laços de repetição (<code>while</code>, <code>for</code>), tratamento de exceções (<code>try</code>, <code>except</code>, <code>finally</code>). Um simples <code>if</code> com uso de <code>elif</code> também não pode ser definido. Como sintaticamente só são aceitas expressões, o único uso de um <code>if</code> é o ternário:</p>
+<div class="highlight"><pre><span></span><span class="n">valor_1</span> <span class="k">if</span> <span class="n">condicao</span> <span class="k">else</span> <span class="n">valor_2</span>
+</pre></div>
+
+
+<p>O que dentro de um lambda teria essa aparência:</p>
+<div class="highlight"><pre><span></span><span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">argumento</span><span class="p">:</span> <span class="n">argumento</span> <span class="o">+</span> <span class="mi">2</span> <span class="k">if</span> <span class="n">argumento</span> <span class="o">></span> <span class="mi">0</span> <span class="k">else</span> <span class="n">argumento</span> <span class="o">-</span> <span class="mi">2</span>
+</pre></div>
+
+
+<p>Funções lambda também podem ter múltiplos argumentos, embora seu processamento só possa ocorrer em uma expressão:</p>
+<div class="highlight"><pre><span></span><span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span><span class="p">,</span> <span class="n">arg_3</span><span class="p">:</span> <span class="kc">True</span> <span class="k">if</span> <span class="nb">sum</span><span class="p">([</span><span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span><span class="p">,</span> <span class="n">arg_3</span><span class="p">])</span> <span class="o">></span> <span class="mi">7</span> <span class="k">else</span> <span class="nb">min</span><span class="p">([</span><span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span><span class="p">,</span> <span class="n">arg_3</span><span class="p">])</span>
+</pre></div>
+
+
+<p>Embora essa seja uma explanação inicial sobre as funções anônimas, grande parte dos tópicos fazem uso delas e vamos poder explorar melhor sua infinitude.</p>
+<p>Mas por hoje é só e no tópico seguinte vamos discutir, mesmo que superficialmente, iteradores e iteráveis e suas relações com a programação funcional.</p>
+ 2017-12-08T15:30:00+00:00
+ Eduardo Mendes
+
+
+ PythonClub: Programação funcional com Python #0 - Saindo da zona de conforto
+ https://pythonclub.com.br/progrmacao-funcional-com-python-0.html
+ <h1>0. Saindo da zona de conforto</h1>
+<p>Sinta-se um vencedor, se você chegou até aqui, isso significa que quer aprender mais sobre o mundo da programação.</p>
+<p>Aprender novos paradígmas podem te trazer muitas coisas positivas, assim como aprender linguagens diferentes, pois paradígmas e linguagens transpõem maneiras, estruturas e métodos de implementação completamente diferentes. Com isso você pode ter mais ferramentas para usar no dia a dia. Você pode aumentar sua capacidade de expressar ideias de diferentes maneiras. Eu penso que o maior limitador de um programador é a linguagem de programação em que ele tem domínio. Quando você aprende linguagens imperativas, como C, Python, Java e etc..., você se vê limitado ao escopo de criar e manipular variáveis. Não que isso seja uma coisa ruim, porém existem outras maneiras de resolver problemas e quando você tem conhecimento disso consegue avaliar melhor quando implementar cada tipo de coisa.</p>
+<p>Você pode me dizer que aprender diferentes tipos de estruturas e maneiras de computar é uma coisa negativa pois tudo é variável nesse contexto. Mas eu penso exatamente o contrário, quanto mais você aprender da sua língua nativa, no caso estamos falando em português, maior o campo de domínio que você tem sobre como se comunicar e expressar ideias. Assim como aprender outras línguas te darão mais fundamentos para expressar ideias em outros idiomas, que não são melhores que os seu, mas diferentes e compõem diferentes estruturas, e isso pode ser libertador. Não quero me prolongar nesse assunto, mas dizer que isso pode acrescentar muito na suas habilidades cognitivas, até mesmo para usar ferramentas que você já usa no seu dia a dia.</p>
+<p>Vamos começar fazendo uma tentativa de entender os paradígmas de programação, sem muito falatório e complicações. Um exemplo muito legal é do David Mertz em "Functional Programming in Python":</p>
+<ul>
+<li>
+<p>Usa-se programação funcional quando se programa em Lisp, Haskell, Scala, Erlang, F# etc..</p>
+</li>
+<li>
+<p>Do mesmo modo que se usa programação imperativa quando se programada C/C++, Pascal, Java, Python etc...</p>
+</li>
+<li>
+<p>Também quando se programa Prolog estamos programando usando o paradígma lógico.</p>
+</li>
+</ul>
+<p>Apesar de não ser uma definição muito elegante, talvez seja a melhor a ser dada em muitas ocasiões. Vamos tentar ser um pouco mais objetivos em relação ao estilo de computação, embora essa discussão não tenha fim:</p>
+<ul>
+<li>
+<p>O foco de usar programação imperativa está no ato de mudar variáveis. A computação se dá pela modificação dos estados das variáveis iniciais. Sendo assim, vamos pensar que tudo é definido no início e vai se modificando até que o resultado esperado seja obtido.</p>
+</li>
+<li>
+<p>Na programação funcional, se tem a noção de que o estado deve ser substituído, no caso da avaliação, para criação de um novo 'objeto' que no caso são funções.</p>
+</li>
+</ul>
+<h2>0.1 Mas de onde vem a programação funcional?</h2>
+<p>O florescer da programação funcional nasce no Lisp (acrônomo para List Processing) para tentar resolver alguns problemas de inteligência artificial que eram provenientes da linguística, que tinha foco em processamento de linguagem natural que por sua vez eram focados em processamento de listas em geral. Isso justifica uma grande parte do conteúdo que vamos ver aqui e seus tipos de dados variam somente entre listas e átomos. E assim foi mantido o foco de processamento de listas em todas as linguagens funcionais e suas funções e abstrações para resolver problemas relativos a listas e estruturas iteráveis. Uma curiosidade é que para quem não sabe porque em lisp existem tantos parênteses é que ele é baseado em s-expression, uma coisa que temos um "equivalente" evoluído em python, que parte dos teoremas de gramáticas livres de contexto:</p>
+<div class="highlight"><pre><span></span><span class="p">(</span><span class="nb">+ </span><span class="mi">4</span> <span class="mi">5</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Sim, isso é uma soma em lisp. Diferente das linguagens imperativas como costumamos ver:</p>
+<div class="highlight"><pre><span></span><span class="mi">4</span> <span class="o">+</span> <span class="mi">5</span>
+</pre></div>
+
+
+<p>Uma assertiva pode ser feita dessa maneira:</p>
+<ul>
+<li>Funcional (ou declarativa)</li>
+</ul>
+<div class="highlight"><pre><span></span><span class="p">(</span><span class="nb">= </span><span class="mi">4</span> <span class="p">(</span><span class="nb">+ </span><span class="mi">2</span> <span class="mi">2</span><span class="p">))</span>
+</pre></div>
+
+
+<ul>
+<li>Imperativa</li>
+</ul>
+<div class="highlight"><pre><span></span><span class="p">(</span><span class="mi">2</span> <span class="o">+</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">4</span>
+</pre></div>
+
+
+<p>Chega de enrolação e vamos correr com essa introdução, não viemos aqui para aprender Lisp ou C. Mas acho que parte desse contexto pode nos ajudar e muito quando formos nos aprofundar em alguns tópicos. Pretendo sempre que iniciar uma nova ferramenta da programação funcional ao menos explicar em que contexto ela foi desenvolvida e para resolver cada tipo de problema.</p>
+<h2>0.2 Técnicas usadas por linguagens funcionais</h2>
+<p>Vamos tentar mapear o que as linguagens funcionais fazem de diferente das linguagens imperativas, mas não vamos nos aprofundar nesse tópicos agora, pois são coisas às vezes complexas sem o entendimento prévio de outros contextos, mas vamos tentar só explanar pra que você se sinta empolgado por estar aqui:</p>
+<ul>
+<li>
+<p>Funções como objetos de primeira classe:</p>
+<ul>
+<li>São funções que podem estar em qualquer lugar (em estruturas, declaradas em tempo de execução).</li>
+</ul>
+</li>
+<li>
+<p>Funções de ordem superior:</p>
+<ul>
+<li>São funções que podem receber funções como argumentos e retornar funções.</li>
+</ul>
+</li>
+<li>
+<p>Funções puras:</p>
+<ul>
+<li>São funções que não sofrem interferências de meios externos (variáveis de fora). Evita efeitos colaterais.</li>
+</ul>
+</li>
+<li>
+<p>Recursão, como oposição aos loops:</p>
+<ul>
+<li>Frequentemente a recursão na matemática é uma coisa mais intuitiva e é só chamar tudo outra vez, no lugar de ficar voltando ao ponto inicial da iteração.</li>
+</ul>
+</li>
+<li>
+<p>Foco em processamento de iteráveis:</p>
+<ul>
+<li>Como dito anteriormente, pensar em como as sequências podem nos ajudar a resolver problemas.</li>
+</ul>
+</li>
+<li>
+<p>O que deve ser computado, não como computar:</p>
+<ul>
+<li>Não ser tão expressivo e aceitar que as intruções não tem necessidade de estar explicitas todas as vezes, isso ajuda em legibilidade.</li>
+</ul>
+</li>
+<li>
+<p>Lazy evaluation:</p>
+<ul>
+<li>Criar sequências infinitas sem estourar nossa memória.</li>
+</ul>
+</li>
+</ul>
+<h2>0.3 Python é uma linguagem funcional?</h2>
+<h4>Não. Mas é uma linguagem que implementa muitos paradígmas e porque não usar todos de uma vez?</h4>
+<p>O objetivo desse 'conjunto de vídeos' é escrever código que gere menos efeito colateral e código com menos estados. Só que isso tudo feito na medida do possível, pois Python não é uma linguagem funcional. Porém, podemos contar o máximo possível com as features presentes do paradígma em python.</p>
+<p>Exemplos de funcional (básicos) em python:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Gerar uma lista da string # Imperativo</span>
+<span class="n">string</span> <span class="o">=</span> <span class="s1">'Python'</span>
+<span class="n">lista</span> <span class="o">=</span> <span class="p">[]</span> <span class="c1"># estado inicial</span>
+
+<span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="n">string</span><span class="p">:</span>
+ <span class="n">lista</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">l</span><span class="p">)</span> <span class="c1"># cada iteração gera um novo estado</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">lista</span><span class="p">)</span> <span class="c1"># ['P', 'y', 't', 'h', 'o', 'n']</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="c1"># Gerar uma lista da string # Funcional</span>
+<span class="n">string</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span>
+
+<span class="n">lista</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="n">string</span><span class="p">(</span><span class="s1">'Python'</span><span class="p">)))</span> <span class="c1"># atribuição a um novo objeto</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">lista</span><span class="p">)</span> <span class="c1"># ['P', 'y', 't', 'h', 'o', 'n']</span>
+</pre></div>
+
+
+<p>Como você pode ver, depois de uma explanação básica das técnicas, a segunda implementação não sofre interferência do meio externo (Funções puras), evita loops e sua saída sem o construtor de list é lazy. Mas não se assuste, vamos abordar tudo isso com calma.</p>
+<h2>0.4 A quem esse 'curso' é destinado?</h2>
+<p>Primeiramente gostaria de dizer que roubei essa ideia dos infinitos livros da O’Reilly, que sempre exibem esse tópico. Mas vamos ao assunto. Este curso é para você que sabe o básico de Python, e quando digo básico quero dizer que consegue fazer qualquer coisa com um pouco de pesquisa na internet. O básico de programação se reduz a isso. Vamos falar sobre coisas simples e coisas mais complexas, mas pretendo manter o bom senso para que todos possam absorver o máximo de conteúdo possível.</p>
+<p>Então, caso você venha do Python (OO ou procedural) você vai encontrar aqui uma introdução a programação funcional descontraída e sem uma tonelada de material difícil de entender. Caso você venha de linguagens funcionais como Haskell e Lisp, você pode se sentir um pouco estranho com tantas declarações, mas aprenderá a se expressar em Python. Caso você venha de linguagens funcionais modernas como Clojure e Scala, as coisas são bem parecidas por aqui.</p>
+<p>Então tente tirar o máximo de proveito. Vamos nos divertir.</p>
+<h2>0.5 Apresentando o Jaber</h2>
+<p>Jaber é nosso aluno de mentira, mas vamos pensar que ele é um aluno que senta na primeira fileira e pergunta de tudo, sempre que acha necessário. Roubei essa ideia do livro de expressões regulares do Aurélio. Ele tem um personagem, Piazinho, e acho que toda interação com ele é sempre graciosa e tira dúvidas quando tudo parece impossível.</p>
+<h2>0.6 Sobre as referências</h2>
+<p>Não gosto muito de citar referências pois procurei não copiar texto dos livros, mas muita coisa contida neles serve de base para o entendimento de certos tópicos. Outro motivo é o nível de complexidade dos exemplos ou explicações que tentei reduzir ao máximo enquanto escrevia esses roteiros. Para um exemplo, você pode olhar o livro do Steven Lott, cheio de fórmulas e abstrações matemáticas que em certo ponto acabam comprometendo o entendimento de quem não tem uma sólida base em computação teórica ou matemática.</p>
+<p>Como um todo, as referências serviram como guia, foi o que lí quando dúvidas para explicações surgiram. Não tiro nenhum crédito delas e as exponho para que todos saibam que existem muitos livros bons e que boa parte do que é passado aqui, foi aprendido neles.</p>
+<h2>0.7 Mais sobre o histórico das linguagens funcionais</h2>
+<p>Se você pretende realmente se aprofundar no assunto enquanto acompanha esse curso, fazer uma imersão ou coisa parecida. Tudo começa com o cálculo lambda mentalizado pelo incrível <a href="https://en.wikipedia.org/wiki/Alonzo_Church">Alonzo Church</a>. Caso você não o conheça, ele foi um matemático fantástico e teve uma carreira acadêmica brilhante. Foi o orientador de pessoas incríveis como Alan Turing, Raymond Smullyan etc...</p>
+<p>Outro grande homem e que vale a pena mencionar e ser buscado é o <a href="https://pt.wikipedia.org/wiki/Haskell_Curry">Haskell Curry</a>, um lógico que trouxe excelentes contribuições para o que chamamos hoje de programação funcional.</p>
+<p>A primeira linguagem funcional 'oficial' (não gosto muito de dizer isso) é o Lisp (List Processing) criada pelo fenomenal <a href="https://pt.wikipedia.org/wiki/John_McCarthy">John McCarthy</a> que também vale a pena ser pesquisado e estudado.</p>
+<p>Veremos o básico sobre os tipos de função no próximo tópico.</p>
+<p>OBS: <a href="https://github.com/z4r4tu5tr4/python-funcional/blob/master/referencias.md">Referências</a> usadas durante todos os tópicos.</p>
+ 2017-11-20T21:43:00+00:00
+ Eduardo Mendes
+
+
+ PythonClub: Peewee - Um ORM Python minimalista
+ https://pythonclub.com.br/peewee-um-orm-python-minimalista.html
+ <p><a href="http://peewee.readthedocs.io/en/latest/index.html">Peewee</a> é um ORM destinado a criar e gerenciar tabelas de banco de dados relacionais através de objetos Python. Segundo a <a href="https://pt.wikipedia.org/wiki/Mapeamento_objeto-relacional">wikipedia</a>, um ORM é:</p>
+<blockquote>
+<p>Mapeamento objeto-relacional (ou ORM, do inglês: Object-relational mapping) é uma técnica de desenvolvimento > utilizada para reduzir a impedância da programação orientada aos objetos utilizando bancos de dados relacionais. As tabelas do banco de dados são representadas através de classes e os registros de cada tabela são representados como instâncias das classes correspondentes.</p>
+</blockquote>
+<p>O que um ORM faz é, basicamente, transformar classes Python em tabelas no banco de dados, além de permitir construir <em>querys</em> usando diretamente objetos Python ao invés de SQL.</p>
+<p>O Peewee é destinado a projetos de pequeno/médio porte e se destaca pela simplicidade quando comparado a outros ORM mais conhecidos, como o SQLAlchemy. Uma analogia utilizada pelo autor da API e que acho muito interessante é que Peewee está para o SQLAlchemy assim como SQLite está para o PostgreSQL.</p>
+<p>Em relação aos recursos por ele oferecidos, podemos citar que ele possui suporte nativo a SQLite, PostgreSQL e MySQL, embora seja necessário a instalação de <em>drivers</em> para utilizá-lo com PostgreSQL e MySQL e suporta tanto Python 2.6+ quanto Python 3.4+.</p>
+<p>Neste tutorial, utilizaremos o SQLite, por sua simplicidade de uso e pelo Python possuir suporte nativo ao mesmo (usaremos o Python 3.5).</p>
+<h2>Instalação</h2>
+<p>O Peewee pode ser facilmente instalado com o gerenciador de pacotes <em>pip</em> (recomendo a instalação em um virtualenv):</p>
+<div class="highlight"><pre><span></span><span class="err">pip install peewee</span>
+</pre></div>
+
+
+<h2>Criando o banco de dados</h2>
+<p>Para criar o banco de dados é bem simples. Inicialmente passamos o nome do nosso banco de dados (a extensão <code>*.db</code> indica um arquivo do SQLite).</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">peewee</span>
+
+<span class="c1"># Aqui criamos o banco de dados</span>
+<span class="n">db</span> <span class="o">=</span> <span class="n">peewee</span><span class="o">.</span><span class="n">SqliteDatabase</span><span class="p">(</span><span class="s1">'codigo_avulso.db'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Diferente de outros bancos de dados que funcionam através um servidor, o SQLite cria um arquivo de extensão <code>*.db</code>, onde todos os nossos dados são armazenados.</p>
+<blockquote>
+<p>Caso deseje ver as tabelas existentes no arquivo <code>codigo_avulso.db</code>, instale o aplicativo <code>SQLiteBrowser</code>. Com ele fica fácil monitorar as tabelas criadas e acompanhar o tutorial.</p>
+</blockquote>
+<div class="highlight"><pre><span></span> sudo apt-get install sqlitebrowser
+</pre></div>
+
+
+<p>A título de exemplo, vamos criar um banco destinado a armazenar nomes de livros e de seus respectivos autores. Iremos chamá-lo de <code>models.py</code>.</p>
+<p>Inicialmente, vamos criar a classe base para todos os nossos <code>models</code>. Esta é uma abordagem recomendada pela documentação e é considerada uma boa prática. Também adicionaremos um log para acompanharmos as mudanças que são feitas no banco:</p>
+<div class="highlight"><pre><span></span><span class="c1"># models.py</span>
+
+<span class="kn">import</span> <span class="nn">peewee</span>
+
+<span class="c1"># Criamos o banco de dados</span>
+<span class="n">db</span> <span class="o">=</span> <span class="n">peewee</span><span class="o">.</span><span class="n">SqliteDatabase</span><span class="p">(</span><span class="s1">'codigo_avulso.db'</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">BaseModel</span><span class="p">(</span><span class="n">peewee</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
+ <span class="sd">"""Classe model base"""</span>
+
+ <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
+ <span class="c1"># Indica em qual banco de dados a tabela</span>
+ <span class="c1"># 'author' sera criada (obrigatorio). Neste caso,</span>
+ <span class="c1"># utilizamos o banco 'codigo_avulso.db' criado anteriormente</span>
+ <span class="n">database</span> <span class="o">=</span> <span class="n">db</span>
+</pre></div>
+
+
+<p>A class <code>BaseModel</code> é responsável por criar a conexão com nosso banco de dados.</p>
+<p>Agora, vamos criar a model que representa os autores:</p>
+<div class="highlight"><pre><span></span><span class="c1"># models.py</span>
+
+<span class="k">class</span> <span class="nc">Author</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
+
+ <span class="sd">"""</span>
+<span class="sd"> Classe que representa a tabela Author</span>
+<span class="sd"> """</span>
+ <span class="c1"># A tabela possui apenas o campo 'name', que receberá o nome do autor sera unico</span>
+ <span class="n">name</span> <span class="o">=</span> <span class="n">peewee</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Se observamos a model <code>Author</code>, veremos que não foi especificado nenhuma coluna como <em>primary key</em> (chave primaria), sendo assim o Peewee irá criar um campo chamado <code>id</code> do tipo inteiro com auto incremento para funcionar como chave primária.
+Em seguida, no mesmo arquivo <code>models.py</code> criamos a classe que representa os livros. Ela possui uma relação de "muitos para um" com a tabela de autores, ou seja, cada livro possui apenas um autor, mas um autor pode possuir vários livros.</p>
+<div class="highlight"><pre><span></span><span class="c1"># models.py</span>
+
+<span class="k">class</span> <span class="nc">Book</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Classe que representa a tabela Book</span>
+<span class="sd"> """</span>
+
+ <span class="c1"># A tabela possui apenas o campo 'title', que receberá o nome do livro</span>
+ <span class="n">title</span> <span class="o">=</span> <span class="n">peewee</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
+
+ <span class="c1"># Chave estrangeira para a tabela Author</span>
+ <span class="n">author</span> <span class="o">=</span> <span class="n">peewee</span><span class="o">.</span><span class="n">ForeignKeyField</span><span class="p">(</span><span class="n">Author</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Agora, adicionamos o código que cria as tabelas <code>Author</code> e <code>Book</code>.</p>
+<div class="highlight"><pre><span></span><span class="c1"># models.py</span>
+
+<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">Author</span><span class="o">.</span><span class="n">create_table</span><span class="p">()</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s2">"Tabela 'Author' criada com sucesso!"</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">peewee</span><span class="o">.</span><span class="n">OperationalError</span><span class="p">:</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s2">"Tabela 'Author' ja existe!"</span><span class="p">)</span>
+
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">Book</span><span class="o">.</span><span class="n">create_table</span><span class="p">()</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s2">"Tabela 'Book' criada com sucesso!"</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">peewee</span><span class="o">.</span><span class="n">OperationalError</span><span class="p">:</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s2">"Tabela 'Book' ja existe!"</span><span class="p">)</span>
+</pre></div>
+
+
+<p>excerpt
+Agora executamos o <code>models.py</code>:</p>
+<div class="highlight"><pre><span></span><span class="err">python models.py</span>
+</pre></div>
+
+
+<p>A estrutura do diretório ficou assim:</p>
+<div class="highlight"><pre><span></span>.
+├── codigo_avulso.db
+├── models.py
+</pre></div>
+
+
+<p>Após executarmos o código, será criado um arquivo de nome <code>codigo_avulso.db</code> no mesmo diretório do nosso arquivo <code>models.py</code>, contendo as tabelas <code>Author</code> e <code>Book</code>.</p>
+<h2>Realizando o CRUD</h2>
+<p>Agora vamos seguir com as 4 principais operações que podemos realizar em um banco de dados, também conhecida como CRUD.</p>
+<p>A sigla <code>CRUD</code> é comumente utilizada para designar as quatro operações básicas que pode-se executar em um banco de dados, sendo elas: </p>
+<div class="highlight"><pre><span></span><span class="err">- Create (criar um novo registro no banco)</span>
+<span class="err">- Read (ler/consultar um registro)</span>
+<span class="err">- Update (atualizar um registro)</span>
+<span class="err">- Delete (excluir um registro do banco)</span>
+</pre></div>
+
+
+<p>Iremos abordar cada uma dessas operações.</p>
+<h3>Create: Inserindo dados no banco</h3>
+<p>Agora, vamos popular nosso banco com alguns autores e seus respectivos livros. Para isso criamos um arquivo <code>create.py</code>. A estrutura do diretório ficou assim:</p>
+<div class="highlight"><pre><span></span>.
+├── codigo_avulso.db
+├── models.py
+├── create.py
+</pre></div>
+
+
+<p>A criação dos registros no banco pode ser feito através do método <code>create</code>, quando desejamos inserir um registro apenas; ou pelo método <code>insert_many</code>, quando desejamos inserir vários registros de uma vez em uma mesma tabela.</p>
+<div class="highlight"><pre><span></span><span class="c1"># create.py</span>
+
+<span class="kn">from</span> <span class="nn">models</span> <span class="kn">import</span> <span class="n">Author</span><span class="p">,</span> <span class="n">Book</span>
+
+<span class="c1"># Inserimos um autor de nome "H. G. Wells" na tabela 'Author'</span>
+<span class="n">author_1</span> <span class="o">=</span> <span class="n">Author</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'H. G. Wells'</span><span class="p">)</span>
+
+<span class="c1"># Inserimos um autor de nome "Julio Verne" na tabela 'Author'</span>
+<span class="n">author_2</span> <span class="o">=</span> <span class="n">Author</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'Julio Verne'</span><span class="p">)</span>
+
+<span class="n">book_1</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'title'</span><span class="p">:</span> <span class="s1">'A Máquina do Tempo'</span><span class="p">,</span>
+ <span class="s1">'author_id'</span><span class="p">:</span> <span class="n">author_1</span><span class="p">,</span>
+<span class="p">}</span>
+
+<span class="n">book_2</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'title'</span><span class="p">:</span> <span class="s1">'Guerra dos Mundos'</span><span class="p">,</span>
+ <span class="s1">'author_id'</span><span class="p">:</span> <span class="n">author_1</span><span class="p">,</span>
+<span class="p">}</span>
+
+<span class="n">book_3</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'title'</span><span class="p">:</span> <span class="s1">'Volta ao Mundo em 80 Dias'</span><span class="p">,</span>
+ <span class="s1">'author_id'</span><span class="p">:</span> <span class="n">author_2</span><span class="p">,</span>
+<span class="p">}</span>
+
+<span class="n">book_4</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'title'</span><span class="p">:</span> <span class="s1">'Vinte Mil Leguas Submarinas'</span><span class="p">,</span>
+ <span class="s1">'author_id'</span><span class="p">:</span> <span class="n">author_1</span><span class="p">,</span>
+<span class="p">}</span>
+
+<span class="n">books</span> <span class="o">=</span> <span class="p">[</span><span class="n">book_1</span><span class="p">,</span> <span class="n">book_2</span><span class="p">,</span> <span class="n">book_3</span><span class="p">,</span> <span class="n">book_4</span><span class="p">]</span>
+
+<span class="c1"># Inserimos os quatro livros na tabela 'Book'</span>
+<span class="n">Book</span><span class="o">.</span><span class="n">insert_many</span><span class="p">(</span><span class="n">books</span><span class="p">)</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span>
+</pre></div>
+
+
+<h3>Read: Consultando dados no banco</h3>
+<p>O Peewee possui comandos destinados a realizar consultas no banco. De maneira semelhante ao conhecido <code>SELECT</code>. Podemos fazer essa consulta de duas maneiras. Se desejamos o primeiro registro que corresponda a nossa pesquisa, podemos utilizar o método <code>get()</code>.</p>
+<div class="highlight"><pre><span></span><span class="c1"># read.py</span>
+
+<span class="kn">from</span> <span class="nn">models</span> <span class="kn">import</span> <span class="n">Author</span><span class="p">,</span> <span class="n">Book</span>
+
+<span class="n">book</span> <span class="o">=</span> <span class="n">Book</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">Book</span><span class="o">.</span><span class="n">title</span> <span class="o">==</span> <span class="s2">"Volta ao Mundo em 80 Dias"</span><span class="p">)</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">book</span><span class="o">.</span><span class="n">title</span><span class="p">)</span>
+
+<span class="c1"># Resultado</span>
+<span class="c1"># * Volta ao Munto em 80 Dias</span>
+</pre></div>
+
+
+<p>Porém, se desejamos mais de um registro, utilizamos o método <code>select</code>. Por exemplo, para consultar todos os livros escritos pelo autor "H. G. Wells".</p>
+<div class="highlight"><pre><span></span><span class="c1"># read.py</span>
+
+<span class="n">books</span> <span class="o">=</span> <span class="n">Book</span><span class="o">.</span><span class="n">select</span><span class="p">()</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">Author</span><span class="p">)</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="n">Author</span><span class="o">.</span><span class="n">name</span><span class="o">==</span><span class="s1">'H. G. Wells'</span><span class="p">)</span>
+
+<span class="c1"># Exibe a quantidade de registros que corresponde a nossa pesquisa</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">books</span><span class="o">.</span><span class="n">count</span><span class="p">())</span>
+
+<span class="k">for</span> <span class="n">book</span> <span class="ow">in</span> <span class="n">books</span><span class="p">:</span>
+ <span class="nb">print</span><span class="p">(</span><span class="n">book</span><span class="o">.</span><span class="n">title</span><span class="p">)</span>
+
+<span class="c1"># Resultado:</span>
+<span class="c1"># * A Máquina do Tempo</span>
+<span class="c1"># * Guerra dos Mundos</span>
+<span class="c1"># * Vinte Mil Leguas Submarinas</span>
+</pre></div>
+
+
+<p>Também podemos utilizar outras comandos do SQL como <code>limit</code> e <code>group</code> (para mais detalhes, ver a documentação <a href="http://peewee.readthedocs.io/en/latest/index.html">aqui</a>).</p>
+<p>A estrutura do diretório ficou assim:</p>
+<div class="highlight"><pre><span></span>.
+├── codigo_avulso.db
+├── models.py
+├── create.py
+├── read.py
+</pre></div>
+
+
+<h3>Update: Alterando dados no banco</h3>
+<p>Alterar dados também é bem simples. No exemplo anterior, se observarmos o resultado da consulta dos livros do autor "H. G. Wells", iremos nos deparar com o livro de título "Vinte Mil Léguas Submarinas". Se você, caro leitor, gosta de contos de ficção-científica, sabe que esta obra foi escrito por "Julio Verne", coincidentemente um dos autores que também estão cadastrados em nosso banco. Sendo assim, vamos corrigir o autor do respectivo livro.</p>
+<p>Primeiro vamos buscar o registro do autor e do livro:</p>
+<div class="highlight"><pre><span></span><span class="c1"># update.py</span>
+
+<span class="kn">from</span> <span class="nn">models</span> <span class="kn">import</span> <span class="n">Author</span><span class="p">,</span> <span class="n">Book</span>
+
+<span class="n">new_author</span> <span class="o">=</span> <span class="n">Author</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">Author</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s1">'Julio Verne'</span><span class="p">)</span>
+<span class="n">book</span> <span class="o">=</span> <span class="n">Book</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">Book</span><span class="o">.</span><span class="n">title</span><span class="o">==</span><span class="s2">"Vinte Mil Leguas Submarinas"</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Agora vamos alterar o autor e gravar essa alteração no banco.</p>
+<div class="highlight"><pre><span></span><span class="c1"># update.py</span>
+
+<span class="c1"># Alteramos o autor do livro</span>
+<span class="n">book</span><span class="o">.</span><span class="n">author</span> <span class="o">=</span> <span class="n">new_author</span>
+
+<span class="c1"># Salvamos a alteração no banco</span>
+<span class="n">book</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+</pre></div>
+
+
+<p>A estrutura do diretório ficou assim:</p>
+<div class="highlight"><pre><span></span>.
+├── codigo_avulso.db
+├── models.py
+├── create.py
+├── read.py
+├── update.py
+</pre></div>
+
+
+<h3>Delete: Deletando dados do banco</h3>
+<p>Assim como as operações anteriores, também podemos deletar registros do banco de maneira bem prática. Como exemplo, vamos deletar o livro "Guerra dos Mundos" do nosso banco de dados.</p>
+<div class="highlight"><pre><span></span><span class="c1"># delete.py</span>
+
+<span class="kn">from</span> <span class="nn">models</span> <span class="kn">import</span> <span class="n">Author</span><span class="p">,</span> <span class="n">Book</span>
+
+<span class="c1"># Buscamos o livro que desejamos excluir do banco</span>
+<span class="n">book</span> <span class="o">=</span> <span class="n">Book</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">Book</span><span class="o">.</span><span class="n">title</span><span class="o">==</span><span class="s2">"Guerra dos Mundos"</span><span class="p">)</span>
+
+<span class="c1"># Excluimos o livro do banco</span>
+<span class="n">book</span><span class="o">.</span><span class="n">delete_instance</span><span class="p">()</span>
+</pre></div>
+
+
+<p>Simples não?</p>
+<p>A estrutura do diretório ficou assim:</p>
+<div class="highlight"><pre><span></span>.
+├── codigo_avulso.db
+├── models.py
+├── create.py
+├── read.py
+├── update.py
+├── delete.py
+</pre></div>
+
+
+<h2>Conclusão</h2>
+<p>É isso pessoal. Este tutorial foi uma introdução bem enxuta sobre o Peewee. Ainda existem muitos tópicos que não abordei aqui, como a criação de <em>primary_key</em>, de campos <em>many2many</em> entre outros recursos, pois foge do escopo deste tutorial. Se você gostou do ORM, aconselho a dar uma olhada também na sua documentação, para conseguir extrair todo o potencial da ferramenta. A utilização de um ORM evita que o desenvolvedor perca tempo escrevendo <em>query</em> SQL e foque totalmente no desenvolvimento de código.</p>
+<h2>Referências</h2>
+<ul>
+<li><a href="http://peewee.readthedocs.io/en/latest/index.html">Documentação do Peewee (em inglês)</a></li>
+<li><a href="https://www.blog.pythonlibrary.org/2014/07/17/an-intro-to-peewee-another-python-orm/">An Intro to peewee – Another Python ORM</a></li>
+<li><a href="http://jonathansoma.com/tutorials/webapps/intro-to-peewee/">Introduction to peewee</a></li>
+<li><a href="https://www.novatec.com.br/livros/introducao-sql/">Introdução à Linguagem SQL</a></li>
+</ul>
+ 2017-07-21T02:45:24+00:00
+ Michell Stuttgart
+
+
+ Flávio Coelho: Curso de introdução a criptomoedas - Aula 01
+ http://pyinsci.blogspot.com/2017/06/curso-de-introducao-criptomoedas-aula-01.html
+ For the Portuguese speaking readers of this blog, I am starting an Introductory course on Cryptocurrencies and applications on the blockchain which is an online version of a standard classroom course I am starting now at FGV.
+
+This is the first lecture which is basically an intro to the topic and the structure of the course.
+
+The online version should have the main content of the lectures on
+ 2017-06-17T13:50:44+00:00
+ Anonymous
+
+
+
diff --git a/rss20.xml b/rss20.xml
new file mode 100644
index 0000000..f090e6d
--- /dev/null
+++ b/rss20.xml
@@ -0,0 +1,8807 @@
+
+
+
+
+ Planeta PythonBrasil
+ http://planet.python.org.br/
+ en
+ Planeta PythonBrasil - http://planet.python.org.br/
+
+
+ Rodrigo Delduca: Building and Publishing Games to Steam Directly from GitHub Actions
+ https://nullonerror.org/2025/03/23/building-and-publishing-games-to-steam-directly-from-gitHub-actions
+ https://nullonerror.org/2025/03/23/building-and-publishing-games-to-steam-directly-from-gitHub-actions/
+ <p>I have been using GitHub Actions extensively both at work and in personal projects, as shown in this post <a href="https://nullonerror.org/2023/11/01/what-i-ve-been-automating-with-github-actions-an-automated-life/">What I’ve been automating with GitHub Actions, an automated life</a>.</p>
+
+<p>In my free time, I’m working on a 2D hide-and-seek game, and as you might expect, I’ve automated the entire release pipeline for publishing on Steam. After a few attempts, when it finally worked, it felt like magic: all I had to do was create a new tag, and within minutes, the Steam client was downloading the update.</p>
+
+<p>As I mentioned earlier, I have a 2D engine that, while simple, is quite comprehensive. With each new tag, I compile it in parallel for Windows, macOS, Linux, and WebAssembly. Once compilation is complete, I create a release and publish it on GitHub. <a href="https://github.com/willtobyte/carimbo/releases">Releases · willtobyte/carimbo</a></p>
+
+<p>This way</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Release</span>
+
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">push</span><span class="pi">:</span>
+ <span class="na">tags</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s2">"</span><span class="s">v*.*.*"</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">release</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">${{ matrix.config.os }}</span>
+
+ <span class="na">permissions</span><span class="pi">:</span>
+ <span class="na">contents</span><span class="pi">:</span> <span class="s">write</span>
+
+ <span class="na">strategy</span><span class="pi">:</span>
+ <span class="na">fail-fast</span><span class="pi">:</span> <span class="no">true</span>
+
+ <span class="na">matrix</span><span class="pi">:</span>
+ <span class="na">config</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">macOS</span>
+ <span class="na">os</span><span class="pi">:</span> <span class="s">macos-latest</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Ubuntu</span>
+ <span class="na">os</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">WebAssembly</span>
+ <span class="na">os</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Windows</span>
+ <span class="na">os</span><span class="pi">:</span> <span class="s">windows-latest</span>
+
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Cache Dependencies</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/cache@v4</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">path</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">~/.conan2/p</span>
+ <span class="s">C:/Users/runneradmin/.conan2/p</span>
+ <span class="na">key</span><span class="pi">:</span> <span class="s">${{ matrix.config.name }}-${{ hashFiles('**/conanfile.py') }}</span>
+ <span class="na">restore-keys</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">${{ matrix.config.name }}-</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Prepare Build Directory</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">mkdir build</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup Python</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-python@v5</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">python-version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3.12"</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Conan</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">pip install conan</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Detect Conan Profile</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">conan profile detect --force</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set Conan Center</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">conan remote update conancenter --url https://center2.conan.io</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Detect WebAssembly Conan Profile</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'WebAssembly'</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">cat > ~/.conan2/profiles/webassembly <<EOF</span>
+ <span class="s">include(default)</span>
+
+ <span class="s">[settings]</span>
+ <span class="s">arch=wasm</span>
+ <span class="s">os=Emscripten</span>
+
+ <span class="s">[tool_requires]</span>
+ <span class="s">*: emsdk/3.1.73</span>
+ <span class="s">EOF</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Windows Or macOS Dependencies</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'Windows' || matrix.config.name == 'macOS'</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">conan install . --output-folder=build --build=missing --settings compiler.cppstd=20 --settings build_type=Release</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Ubuntu Dependencies</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'Ubuntu'</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">conan install . --output-folder=build --build=missing --settings compiler.cppstd=20 --settings build_type=Release --conf "tools.system.package_manager:mode=install" --conf "tools.system.package_manager:sudo=True"</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install WebAssembly Dependencies</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'WebAssembly'</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">conan install . --output-folder=build --build=missing --profile=webassembly --settings compiler.cppstd=20 --settings build_type=Release</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Configure</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">cmake .. -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">cmake --build . --parallel 8 --config Release --verbose</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Create Artifacts Directory</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">mkdir artifacts</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Compress Artifacts</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'macOS'</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">chmod -R a+rwx carimbo</span>
+ <span class="s">tar -cpzvf macOS.tar.gz carimbo</span>
+ <span class="s">mv macOS.tar.gz ../artifacts</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Compress Artifacts</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'Ubuntu'</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">chmod +x carimbo</span>
+ <span class="s">tar -czvf Ubuntu.tar.gz --mode='a+rwx' carimbo</span>
+ <span class="s">mv Ubuntu.tar.gz ../artifacts</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Compress Artifacts</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'WebAssembly'</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">zip -jr WebAssembly.zip carimbo.wasm carimbo.js</span>
+ <span class="s">mv WebAssembly.zip ../artifacts</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Compress Artifacts</span>
+ <span class="na">if</span><span class="pi">:</span> <span class="s">matrix.config.name == 'Windows'</span>
+ <span class="na">working-directory</span><span class="pi">:</span> <span class="s">build</span>
+ <span class="na">shell</span><span class="pi">:</span> <span class="s">powershell</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">Compress-Archive -LiteralPath 'Release/carimbo.exe' -DestinationPath "../artifacts/Windows.zip"</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Release</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">softprops/action-gh-release@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">tag_name</span><span class="pi">:</span> <span class="s">${{ github.event.inputs.tagName }}</span>
+ <span class="na">prerelease</span><span class="pi">:</span> <span class="s">${{ github.events.inputs.prerelease }}</span>
+ <span class="na">files</span><span class="pi">:</span> <span class="s">artifacts/*</span>
+</code></pre></div></div>
+
+<p>Publishing on Steam is quite simple. First, you need a developer account with the correct documentation and fees paid.</p>
+
+<p>After that, you’ll need to generate some secret keys as follows:</p>
+
+<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>steamcmd +login <username> <password> +quit
+</code></pre></div></div>
+
+<p>If you don’t have the steamcmd application installed, you’ll need to install it using:</p>
+
+<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cast <span class="nb">install</span> <span class="nt">--cask</span> steamcmd
+</code></pre></div></div>
+
+<p>Copy the contents of the authentication file:</p>
+
+<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> ~/Library/Application<span class="se">\ </span>Support/Steam/config/config.vdf | <span class="nb">base64</span> | pbcopy
+</code></pre></div></div>
+
+<p><strong>Note:</strong> You must have MFA enabled. After logging in, run the command below and copy the output into a GitHub Action variable named <code class="language-plaintext highlighter-rouge">STEAM_CONFIG_VDF</code>.</p>
+
+<p>Also, create the variables <code class="language-plaintext highlighter-rouge">STEAM_USERNAME</code> with your username and <code class="language-plaintext highlighter-rouge">STEAM_APP_ID</code> with your game’s ID.</p>
+
+<p>Additionally, the Action downloads the latest Carimbo release for Windows only (sorry Linux and macOS users, my time is limited). Ideally, I should pin the runtime version (the Carimbo version) using something like a runtime.txt file. Maybe I’ll implement this in the future, but for now, everything runs on the bleeding edge. :-)</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">on</span><span class="pi">:</span>
+ <span class="na">push</span><span class="pi">:</span>
+ <span class="na">tags</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s2">"</span><span class="s">v*.*.*"</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">publish</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">CARIMBO_TAG</span><span class="pi">:</span> <span class="s2">"</span><span class="s">v1.0.65"</span>
+
+ <span class="na">permissions</span><span class="pi">:</span>
+ <span class="na">contents</span><span class="pi">:</span> <span class="s">write</span>
+
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Clone the repository</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install 7zip</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">sudo apt install p7zip-full</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Create bundle</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">7z a -xr'!.git/*' -xr'!.git' -xr'!.*' -t7z -m0=lzma -mx=6 -mfb=64 -md=32m -ms=on bundle.7z .</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Download Carimbo runtime</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">GITHUB_TOKEN</span><span class="pi">:</span> <span class="s">${{ secrets.GITHUB_TOKEN }}</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">gh release download ${{ env.CARIMBO_TAG }} --repo willtobyte/carimbo --pattern "Windows.zip"</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Extract Carimbo runtime</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">7z x Windows.zip -o.</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Copy files</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">mkdir -p output</span>
+ <span class="s">mv bundle.7z output/</span>
+ <span class="s">mv carimbo.exe output/</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Upload build to Steam</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">game-ci/steam-deploy@v3</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">username</span><span class="pi">:</span> <span class="s">${{ secrets.STEAM_USERNAME }}</span>
+ <span class="na">configVdf</span><span class="pi">:</span> <span class="s">${{ secrets.STEAM_CONFIG_VDF }}</span>
+ <span class="na">appId</span><span class="pi">:</span> <span class="s">${{ secrets.STEAM_APP_ID }}</span>
+ <span class="na">rootPath</span><span class="pi">:</span> <span class="s">output</span>
+ <span class="na">depot1Path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">."</span>
+ <span class="na">releaseBranch</span><span class="pi">:</span> <span class="s">prerelease</span>
+</code></pre></div></div>
+
+<p>Boom! If everything is correct, your game should appear in your Steam client under the list of owned games.</p>
+ Sun, 23 Mar 2025 00:00:00 +0000
+ rodrigo@delduca.org (Rodrigo Delduca)
+
+
+ Nilo Ney Coutinho Menezes: Advent of code 2024 - Day 15 of 25
+ https://blog.nilo.pro.br/en/posts/2024-12-16-adventofcode2024-day15/
+ https://blog.nilo.pro.br/en/posts/2024-12-16-adventofcode2024-day15/
+ <p>The fifteenth day of Advent of Code was yesterday 15/12/2024!
+If you&rsquo;re here for the first time today, don&rsquo;t forget to read the posts about
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-06-adventofcode2024-day01/">Day 1</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-06-adventofcode2024-day02/">Day 2</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-06-adventofcode2024-day03/">Day 3</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-06-adventofcode2024-day04/">Day 4</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-06-adventofcode2024-day05/">Day 5</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-07-adventofcode2024-day06/">Day 6</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-08-adventofcode2024-day07/">Day 7</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-09-adventofcode2024-day08/">Day 8</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-10-adventofcode2024-day09/">Day 9</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-11-adventofcode2024-day10/">Day 10</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-12-adventofcode2024-day11/">Day 11</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-13-adventofcode2024-day12/">Day 12</a>,
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-14-adventofcode2024-day13/">Day 13</a> and
+<a href="https://blog.nilo.pro.br/en/posts/2024-12-15-adventofcode2024-day14/">Day 14</a>.</p>
+<p>I classified the <a href="https://adventofcode.com/2024/day/15">fifteenth problem</a> as medium (part 1) and medium (part 2).</p>
+<h1 id="tips">Tips</h1>
+<ul>
+<li>The first part of the problem is to read the maze (warehouse) and the robot commands.</li>
+<li>Use the example inputs to test the solution.</li>
+<li>You need to detect in the maze input where the walls, robot, and boxes are.</li>
+<li>The commands are on multiple lines, it&rsquo;s easier if you concatenate them into a single string.</li>
+<li>You can implement robot directions (commands) using vectors.</li>
+<li>Vectors can also be used to mark box and wall positions.</li>
+<li>The walls surround the entire warehouse, you don&rsquo;t need to worry about the robot leaving the warehouse.</li>
+<li>To check if the robot can move, verify if the new position is free. In this case, move the robot by updating its position.</li>
+<li>If the next position is occupied by a wall, the robot cannot move and stays in the same place.</li>
+<li>To know if boxes can be moved, you can recursively try all boxes from the movement direction. For each box, check if it can move (blank space in the movement direction). If one of the boxes cannot be moved, the movement is canceled.</li>
+<li>Remember that the robot can push multiple boxes at once. A recursive function can help solve this problem.</li>
+<li>In part 1, boxes are the same size as walls, meaning each box occupies one position. In part 2, each box occupies two positions.</li>
+<li>Part 2 is a bit more complex because you can move more than columns and rows of boxes, but entire parts of the warehouse.</li>
+<li>Horizontal movement is similar to part 1, as each box has a height of only one character, like in part 1.</li>
+<li>Each movement in part 2 can move up to 2 boxes at once. But this movement is done in cascade, so these two boxes can move other 2, 3, or more boxes. Each box movement can move zero, one, or two boxes at each step, but can cause the movement of many others.</li>
+<li>In part 2, you need to verify if the entire movement is valid. For example, when the boxes being pushed form a V or get stuck on a wall, they all have to stop moving. If you implement the movement function as in part 1, you&rsquo;ll see that the boxes will move in several pieces, which leads to the wrong answer.</li>
+</ul>
+<h1 id="how-much-python-do-you-need-to-know">How much Python do you need to know?</h1>
+<p>The chapters refer to my book <a href="https://python.nilo.pro.br/">Introduction to Programming with Python</a>.</p>
+<ul>
+<li>Lists - Chapter 6</li>
+<li>Strings - Chapter 7</li>
+<li>Recursive Functions - Chapter 8</li>
+<li>Classes, inheritance - Chapter 10</li>
+</ul>
+<h1 id="answer">Answer</h1>
+<p>Here&rsquo;s the code that solves the problem. You&rsquo;ll see the scars in the code from switching between part 1 and part 2, but the code works relatively well.</p>
+<p>You can use the Warehouse, Boxes, Wall, Robot, Problem, and Problem2 classes to debug your solution. The <code>draw</code> and <code>prompt</code> methods can help interpret and draw the commands step by step. Although they&rsquo;re not called in the answer, they&rsquo;re in the code if you need them.</p>
+<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> sys
+</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> helpers <span style="color:#f92672">import</span> read_file_without_blank_lines, Vector2d, Maze
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>TEST_INPUT <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;&#34;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O.O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##@.O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#...O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.#.O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#...O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#......#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;^^&gt;&gt;&gt;vv&lt;v&gt;&gt;v&lt;&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>INPUT <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;&#34;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O..O.O#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#......O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.OO..O.O#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O@..O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#O#..O...#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#O..O..O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.OO.O.OO#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#....O...#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;vv&gt;^&lt;v^&gt;v&gt;^vv^v&gt;v&lt;&gt;v^v&lt;v&lt;^vv&lt;&lt;&lt;^&gt;&lt;&lt;&gt;&lt;&gt;&gt;v&lt;vvv&lt;&gt;^v^&gt;^&lt;&lt;&lt;&gt;&lt;&lt;v&lt;&lt;&lt;v^vv^v&gt;^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">vvv&lt;&lt;^&gt;^v^^&gt;&lt;&lt;&gt;&gt;&gt;&lt;&gt;^&lt;&lt;&gt;&lt;^vv^^&lt;&gt;vvv&lt;&gt;&gt;&lt;^^v&gt;^&gt;vv&lt;&gt;v&lt;&lt;&lt;&lt;v&lt;^v&gt;^&lt;^^&gt;&gt;&gt;^&lt;v&lt;v
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&gt;&lt;&gt;vv&gt;v^v^&lt;&gt;&gt;&lt;&gt;&gt;&gt;&gt;&lt;^^&gt;vv&gt;v&lt;^^^&gt;&gt;v^v^&lt;^^&gt;v^^&gt;v^&lt;^v&gt;v&lt;&gt;&gt;v^v^&lt;v&gt;v^^&lt;^^vv&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;&lt;v&lt;^&gt;&gt;^^^^&gt;&gt;&gt;v^&lt;&gt;vvv^&gt;&lt;v&lt;&lt;&lt;&gt;^^^vv^&lt;vvv&gt;^&gt;v&lt;^^^^v&lt;&gt;^&gt;vvvv&gt;&lt;&gt;&gt;v^&lt;&lt;^^^^^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^&gt;&lt;^&gt;&lt;&gt;&gt;&gt;&lt;&gt;^^&lt;&lt;^^v&gt;&gt;&gt;&lt;^&lt;v&gt;^&lt;vv&gt;&gt;v&gt;&gt;&gt;^v&gt;&lt;&gt;^v&gt;&lt;&lt;&lt;&lt;v&gt;&gt;v&lt;v&lt;v&gt;vvv&gt;^&lt;&gt;&lt;&lt;&gt;^&gt;&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^&gt;&gt;&lt;&gt;^v&lt;&gt;&lt;^vvv&lt;^^&lt;&gt;&lt;v&lt;&lt;&lt;&lt;&lt;&gt;&lt;^v&lt;&lt;&lt;&gt;&lt;&lt;&lt;^^&lt;v&lt;^^^&gt;&lt;^&gt;&gt;^&lt;v^&gt;&lt;&lt;&lt;^&gt;&gt;^v&lt;v^v&lt;v^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&gt;^&gt;&gt;^v&gt;vv&gt;^&lt;&lt;^v&lt;&gt;&gt;&lt;&lt;&gt;&lt;&lt;v&lt;&lt;v&gt;&lt;&gt;v&lt;^vv&lt;&lt;&lt;&gt;^^v^&gt;^^&gt;&gt;&gt;&lt;&lt;^v&gt;&gt;v^v&gt;&lt;^^&gt;&gt;^&lt;&gt;vv^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;&gt;&lt;^^&gt;^^^&lt;&gt;&lt;vvvvv^v&lt;v&lt;&lt;&gt;^v&lt;v&gt;v&lt;&lt;^&gt;&lt;&lt;&gt;&lt;&lt;&gt;&lt;&lt;&lt;^^&lt;&lt;&lt;^&lt;&lt;&gt;&gt;&lt;&lt;&gt;&lt;^^^&gt;^^&lt;&gt;^&gt;v&lt;&gt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^^&gt;vv&lt;^v^v&lt;vv&gt;^&lt;&gt;&lt;v&lt;^v&gt;^^^&gt;&gt;&gt;^^vvv^&gt;vvv&lt;&gt;&gt;&gt;^&lt;^&gt;&gt;&gt;&gt;&gt;^&lt;&lt;^v&gt;^vvv&lt;&gt;^&lt;&gt;&lt;&lt;v&gt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">v^^&gt;&gt;&gt;&lt;&lt;^^&lt;&gt;&gt;^v^&lt;v^vv&lt;&gt;v^&lt;&lt;&gt;^&lt;^v^v&gt;&lt;^&lt;&lt;&lt;&gt;&lt;&lt;^&lt;v&gt;&lt;v&lt;&gt;vv&gt;&gt;v&gt;&lt;v^&lt;vv&lt;&gt;v^&lt;&lt;^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">read_input</span>(input: str) <span style="color:#f92672">-&gt;</span> tuple[str, str]:
+</span></span><span style="display:flex;"><span> maze <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> commands <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> line <span style="color:#f92672">in</span> input<span style="color:#f92672">.</span>splitlines():
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> line<span style="color:#f92672">.</span>strip():
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> line<span style="color:#f92672">.</span>startswith(<span style="color:#e6db74">&#34;#&#34;</span>):
+</span></span><span style="display:flex;"><span> maze<span style="color:#f92672">.</span>append(line)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>:
+</span></span><span style="display:flex;"><span> commands<span style="color:#f92672">.</span>append(line)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> maze, commands
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Vector2dWithSymbol</span>(Vector2d):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x, symbol):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>symbol <span style="color:#f92672">=</span> symbol
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __add__(self, other):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> Vector2dWithSymbol(self<span style="color:#f92672">.</span>y <span style="color:#f92672">+</span> other<span style="color:#f92672">.</span>y, self<span style="color:#f92672">.</span>x <span style="color:#f92672">+</span> other<span style="color:#f92672">.</span>x, self<span style="color:#f92672">.</span>symbol)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __sub__(self, other):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> Vector2dWithSymbol(self<span style="color:#f92672">.</span>y <span style="color:#f92672">-</span> other<span style="color:#f92672">.</span>y, self<span style="color:#f92672">.</span>x <span style="color:#f92672">-</span> other<span style="color:#f92672">.</span>x, self<span style="color:#f92672">.</span>symbol)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Box</span>(Vector2dWithSymbol):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x, symbol<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;O&#34;</span>):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, symbol)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Wall</span>(Vector2dWithSymbol):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, <span style="color:#e6db74">&#34;#&#34;</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Robot</span>(Vector2dWithSymbol):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, <span style="color:#e6db74">&#34;@&#34;</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Warehouse</span>(Maze):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">init_drawing</span>(self, background<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;.&#34;</span>):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>background <span style="color:#f92672">=</span> background
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>drawing <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> _ <span style="color:#f92672">in</span> range(self<span style="color:#f92672">.</span>max_y):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>drawing<span style="color:#f92672">.</span>append([background] <span style="color:#f92672">*</span> self<span style="color:#f92672">.</span>max_x)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">draw</span>(self, objects: list[Vector2dWithSymbol]):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> obj <span style="color:#f92672">in</span> objects:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>drawing[obj<span style="color:#f92672">.</span>y][obj<span style="color:#f92672">.</span>x] <span style="color:#f92672">=</span> obj<span style="color:#f92672">.</span>symbol
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">dump_drawing</span>(self, output<span style="color:#f92672">=</span>sys<span style="color:#f92672">.</span>stdout):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> line <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>drawing:
+</span></span><span style="display:flex;"><span> print(<span style="color:#e6db74">&#34;&#34;</span><span style="color:#f92672">.</span>join(line), file<span style="color:#f92672">=</span>output)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __iter__(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> iter(self<span style="color:#f92672">.</span>maze)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Problem</span>:
+</span></span><span style="display:flex;"><span> DIRECTIONS <span style="color:#f92672">=</span> {
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;&lt;&#34;</span>: Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;^&#34;</span>: Vector2d(<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;&gt;&#34;</span>: Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;v&#34;</span>: Vector2d(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>),
+</span></span><span style="display:flex;"><span> }
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, maze: list[str], commands: list[str]):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>commands <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;</span><span style="color:#f92672">.</span>join(commands)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse <span style="color:#f92672">=</span> Warehouse(maze)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>setup()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">setup</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>walls <span style="color:#f92672">=</span> set()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes <span style="color:#f92672">=</span> dict()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> y, line <span style="color:#f92672">in</span> enumerate(self<span style="color:#f92672">.</span>warehouse):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> x, char <span style="color:#f92672">in</span> enumerate(line):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;#&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>walls<span style="color:#f92672">.</span>add(Wall(y, x))
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;O&#34;</span>:
+</span></span><span style="display:flex;"><span> box <span style="color:#f92672">=</span> Box(y, x)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes[box] <span style="color:#f92672">=</span> box
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;@&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">=</span> Robot(y, x)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">draw</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>init_drawing()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>draw(self<span style="color:#f92672">.</span>walls)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>draw(self<span style="color:#f92672">.</span>boxes)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>draw([self<span style="color:#f92672">.</span>robot])
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>dump_drawing()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">execute_commands</span>(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> command <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>commands:
+</span></span><span style="display:flex;"><span> direction <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>DIRECTIONS[command]
+</span></span><span style="display:flex;"><span> new_position <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">+</span> direction
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> new_position <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> self<span style="color:#f92672">.</span>push(new_position, direction):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">elif</span> new_position <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>walls:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">+=</span> direction
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">push</span>(self, position: Vector2d, direction: Vector2d, execute: bool <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>):
+</span></span><span style="display:flex;"><span> new_position <span style="color:#f92672">=</span> position <span style="color:#f92672">+</span> direction
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> new_position <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>walls:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> new_position <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> can_push <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>push(new_position, direction, execute)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> can_push:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> execute:
+</span></span><span style="display:flex;"><span> box <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>boxes<span style="color:#f92672">.</span>pop(position)
+</span></span><span style="display:flex;"><span> box<span style="color:#f92672">.</span>y <span style="color:#f92672">=</span> new_position<span style="color:#f92672">.</span>y
+</span></span><span style="display:flex;"><span> box<span style="color:#f92672">.</span>x <span style="color:#f92672">=</span> new_position<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes[box] <span style="color:#f92672">=</span> box
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">gps_boxes</span>(self):
+</span></span><span style="display:flex;"><span> total <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> box <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> total <span style="color:#f92672">+=</span> box<span style="color:#f92672">.</span>y <span style="color:#f92672">*</span> <span style="color:#ae81ff">100</span> <span style="color:#f92672">+</span> box<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> total
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">prompt</span>(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">while</span> <span style="color:#66d9ef">True</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>draw()
+</span></span><span style="display:flex;"><span> commands <span style="color:#f92672">=</span> input(<span style="color:#e6db74">&#34;Commands: &#34;</span>)<span style="color:#f92672">.</span>lower()<span style="color:#f92672">.</span>strip()
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> commands:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>commands <span style="color:#f92672">=</span> commands
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Problem2</span>(Problem):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, maze: list[str], commands: list[str]):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(maze, commands)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>warehouse<span style="color:#f92672">.</span>max_x <span style="color:#f92672">*=</span> <span style="color:#ae81ff">2</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">setup</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>walls <span style="color:#f92672">=</span> set()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes <span style="color:#f92672">=</span> dict()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> y, line <span style="color:#f92672">in</span> enumerate(self<span style="color:#f92672">.</span>warehouse):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> x, char <span style="color:#f92672">in</span> enumerate(line):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;#&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>walls<span style="color:#f92672">.</span>add(Wall(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>))
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>walls<span style="color:#f92672">.</span>add(Wall(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>))
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;O&#34;</span>:
+</span></span><span style="display:flex;"><span> box1 <span style="color:#f92672">=</span> Box(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>, <span style="color:#e6db74">&#34;[&#34;</span>)
+</span></span><span style="display:flex;"><span> box2 <span style="color:#f92672">=</span> Box(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>, <span style="color:#e6db74">&#34;]&#34;</span>)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes[box1] <span style="color:#f92672">=</span> box1
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes[box2] <span style="color:#f92672">=</span> box2
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;@&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robot <span style="color:#f92672">=</span> Robot(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">push</span>(self, position: Vector2d, direction: Vector2d, execute: bool <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>):
+</span></span><span style="display:flex;"><span> new_position <span style="color:#f92672">=</span> position <span style="color:#f92672">+</span> direction
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> direction <span style="color:#f92672">in</span> [Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>), Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>)] <span style="color:#f92672">or</span> new_position <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>walls:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> super()<span style="color:#f92672">.</span>push(position, direction, execute)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> position <span style="color:#f92672">not</span> <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> box1, box2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>find_box(position)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> can_push1 <span style="color:#f92672">=</span> super()<span style="color:#f92672">.</span>push(box1, direction, <span style="color:#66d9ef">False</span>)
+</span></span><span style="display:flex;"><span> can_push2 <span style="color:#f92672">=</span> super()<span style="color:#f92672">.</span>push(box2, direction, <span style="color:#66d9ef">False</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> can_push1 <span style="color:#f92672">and</span> can_push2:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> execute:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>push_v(box1, box2, direction)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">find_box</span>(self, position):
+</span></span><span style="display:flex;"><span> box1 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>boxes[position]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> box1<span style="color:#f92672">.</span>symbol <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;[&#34;</span>:
+</span></span><span style="display:flex;"><span> box2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>boxes[position <span style="color:#f92672">+</span> Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>)]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>:
+</span></span><span style="display:flex;"><span> box2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>boxes[position <span style="color:#f92672">+</span> Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> box1, box2
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">push_v</span>(self, position1, position2, direction):
+</span></span><span style="display:flex;"><span> np1 <span style="color:#f92672">=</span> position1 <span style="color:#f92672">+</span> direction
+</span></span><span style="display:flex;"><span> np2 <span style="color:#f92672">=</span> position2 <span style="color:#f92672">+</span> direction
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> np1 <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>push_v(<span style="color:#f92672">*</span>self<span style="color:#f92672">.</span>find_box(np1), direction)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> np2 <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>push_v(<span style="color:#f92672">*</span>self<span style="color:#f92672">.</span>find_box(np2), direction)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> position, new_position <span style="color:#f92672">in</span> zip([position1, position2], [np1, np2]):
+</span></span><span style="display:flex;"><span> box <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>boxes<span style="color:#f92672">.</span>pop(position)
+</span></span><span style="display:flex;"><span> box<span style="color:#f92672">.</span>y <span style="color:#f92672">=</span> new_position<span style="color:#f92672">.</span>y
+</span></span><span style="display:flex;"><span> box<span style="color:#f92672">.</span>x <span style="color:#f92672">=</span> new_position<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>boxes[box] <span style="color:#f92672">=</span> box
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">gps_boxes</span>(self):
+</span></span><span style="display:flex;"><span> total <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> box <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>boxes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> box<span style="color:#f92672">.</span>symbol <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;[&#34;</span>:
+</span></span><span style="display:flex;"><span> total <span style="color:#f92672">+=</span> box<span style="color:#f92672">.</span>y <span style="color:#f92672">*</span> <span style="color:#ae81ff">100</span> <span style="color:#f92672">+</span> box<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> total
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problem <span style="color:#f92672">=</span> Problem(<span style="color:#f92672">*</span>read_input(TEST_INPUT))
+</span></span><span style="display:flex;"><span>problem<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problem<span style="color:#f92672">.</span>gps_boxes()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Part 1 - small input test:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">2028</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problem <span style="color:#f92672">=</span> Problem(<span style="color:#f92672">*</span>read_input(INPUT))
+</span></span><span style="display:flex;"><span>problem<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problem<span style="color:#f92672">.</span>gps_boxes()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Part 1 - large input test:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">10092</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problem <span style="color:#f92672">=</span> Problem(<span style="color:#f92672">*</span>read_input(read_file_without_blank_lines(<span style="color:#e6db74">&#34;15/input.txt&#34;</span>)))
+</span></span><span style="display:flex;"><span>problem<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problem<span style="color:#f92672">.</span>gps_boxes()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Part 1 - large input:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">1552463</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#75715e"># Part 2</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problem <span style="color:#f92672">=</span> Problem2(<span style="color:#f92672">*</span>read_input(INPUT))
+</span></span><span style="display:flex;"><span>problem<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problem<span style="color:#f92672">.</span>gps_boxes()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Part 2 - large input test:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">9021</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problem <span style="color:#f92672">=</span> Problem2(<span style="color:#f92672">*</span>read_input(read_file_without_blank_lines(<span style="color:#e6db74">&#34;15/input.txt&#34;</span>)))
+</span></span><span style="display:flex;"><span>problem<span style="color:#f92672">.</span>execute_commands()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problem<span style="color:#f92672">.</span>gps_boxes()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Part 2 - large input:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">1554058</span>
+</span></span></code></pre></div>
+ Mon, 16 Dec 2024 00:49:05 +0000
+
+
+ Nilo Ney Coutinho Menezes: Advent of code 2024 - Dia 15 de 25
+ https://blog.nilo.pro.br/posts/2024-12-16-adventofcode2024-dia15/
+ https://blog.nilo.pro.br/posts/2024-12-16-adventofcode2024-dia15/
+ <p>O décimo quinto dia do Advent of Code foi ontem 15/12/2024!
+Se você caiu aqui hoje pela primeira vez, não deixe de ler os posts sobre o
+<a href="https://blog.nilo.pro.br/posts/2024-12-02-adventofcode2024-dia01/">Dia 1</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-03-adventofcode2024-dia02/">Dia 2</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-04-adventofcode2024-dia03/">Dia 3</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-05-adventofcode2024-dia04/">Dia 4</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-06-adventofcode2024-dia05/">Dia 5</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-07-adventofcode2024-dia06/">Dia 6</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-08-adventofcode2024-dia07/">Dia 7</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-09-adventofcode2024-dia08/">Dia 8</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-10-adventofcode2024-dia09/">Dia 9</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-11-adventofcode2024-dia10/">Dia 10</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-12-adventofcode2024-dia11/">Dia 11</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-13-adventofcode2024-dia12/">Dia 12</a>,
+<a href="https://blog.nilo.pro.br/posts/2024-12-14-adventofcode2024-dia13/">Dia 13</a> e
+<a href="https://blog.nilo.pro.br/posts/2024-12-15-adventofcode2024-dia14/">Dia 14</a>.</p>
+<p>Classifiquei o <a href="https://adventofcode.com/2024/day/15">décimo quinto problema</a> como médio (parte 1) e médio (parte 2).</p>
+<h1 id="dicas">Dicas</h1>
+<ul>
+<li>A primeira parte do problema é ler o labirinto (armazém) e os comandos do robô.</li>
+<li>Utilize as entradas de exemplo para testar a solução.</li>
+<li>Você precisa detectar na entrada do labirinto, onde estão as paredes, o robô e as caixas.</li>
+<li>Os comandos estão em várias linhas, fica mais fácil se você concatená-las em uma só string.</li>
+<li>Você pode implementar as direções do robô (comandos) usando vetores.</li>
+<li>Vetores também podem ser utilizados para marcar as posições das caixas e paredes.</li>
+<li>As paredes circundam todo o armazém, você não precisa de preocupar com o robô saindo do armazém.</li>
+<li>Para verificar se o robô pode se mexer, verifique se a nova posição está livre. Neste caso, mova o robô, atualizando a posição.</li>
+<li>Se a posição seguinte for ocupada por uma parede, o robô não pode se mexer e fica no mesmo lugar.</li>
+<li>Para saber se pode mover as caixas, você pode tentar recursivamente todas as caixas a partir da direção do movimento. A cada caixa, verifique se ela pode se mover (espaço em branco na direção do movimento). Caso uma das caixas não possa ser movida, o movimento é cancelado.</li>
+<li>Lembre-se que o robô pode empurrar várias caixas ao mesmo tempo. Uma função recursiva pode ajudar a resolver este problema.</li>
+<li>Na parte 1, as caixas são do mesmo tamanho das paredes, ou seja, cada caixa ocupa uma posição. Na parte 2, cada caixa ocupa duas posições.</li>
+<li>A parte 2 é um pouco mais complexa, pois você pode mover mais que colunas e linhas de caixas, mas partes inteiras do armazém.</li>
+<li>O movimento horizontal é semelhante ao da parte 1, pois cada caixa tem altura de apenas um caractere, como na parte 1.</li>
+<li>Cada movimento na parte 2 pode mover até 2 caixas ao mesmo tempo. Mas esse movimento é feito em cascata, logo estas duas caixas podem mover outras 2, 3, ou mais caixas. Cada movimento de caixa pode movimentar a cada passo, zero, uma ou duas caixas, mas pode ocasionar o movimento de muitas outras.</li>
+<li>Na parte 2, você precisa verificar se o movimento inteiro é válido. Por exemplo, quando as caixas sendo empurradas formam um V ou ficam presas em uma parede, todas tem que parar de se mover. Se você implementar a função de movimento como na parte 1, verá que as caixas vão se mexer em vários pedaços, o que leva a resposta errada.</li>
+</ul>
+<h1 id="quanto-de-python-você-precisa-saber">Quanto de Python você precisa saber?</h1>
+<p>Os capítulos se referem ao meu livro <a href="https://python.nilo.pro.br/">Introdução à Programação com Python</a>.</p>
+<ul>
+<li>Listas - Capítulo 6</li>
+<li>Strings - Capítulo 7</li>
+<li>Funções recursivas - Capítulo 8</li>
+<li>Classes, herança - Capítulo 10</li>
+</ul>
+<h1 id="resposta">Resposta</h1>
+<p>Aqui o código que resolve o problema. Você verá as cicatrizes no código da troca entre a parte 1 e a parte 2, mas o código funciona relativamente bem.</p>
+<p>Você pode de usar as classes Armazém, Caixas, Parede, Robô, Problema e Problema2 para depurar sua solução. Os métodos <code>desenha</code>e <code>prompt</code> podem ajudar a interpretar e desenhar os comandos passo a passo. Embora não sejam chamados na resposta, eles estão no código caso você precise.</p>
+<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> sys
+</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> auxiliares <span style="color:#f92672">import</span> le_arquivo_sem_linhas_em_branco, Vector2d, Labirinto
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>ENTRADA_TESTE <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;&#34;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O.O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##@.O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#...O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.#.O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#...O..#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#......#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;^^&gt;&gt;&gt;vv&lt;v&gt;&gt;v&lt;&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>ENTRADA <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;&#34;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O..O.O#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#......O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.OO..O.O#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#..O@..O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#O#..O...#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#O..O..O.#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#.OO.O.OO#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#....O...#
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">##########
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;vv&gt;^&lt;v^&gt;v&gt;^vv^v&gt;v&lt;&gt;v^v&lt;v&lt;^vv&lt;&lt;&lt;^&gt;&lt;&lt;&gt;&lt;&gt;&gt;v&lt;vvv&lt;&gt;^v^&gt;^&lt;&lt;&lt;&gt;&lt;&lt;v&lt;&lt;&lt;v^vv^v&gt;^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">vvv&lt;&lt;^&gt;^v^^&gt;&lt;&lt;&gt;&gt;&gt;&lt;&gt;^&lt;&lt;&gt;&lt;^vv^^&lt;&gt;vvv&lt;&gt;&gt;&lt;^^v&gt;^&gt;vv&lt;&gt;v&lt;&lt;&lt;&lt;v&lt;^v&gt;^&lt;^^&gt;&gt;&gt;^&lt;v&lt;v
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&gt;&lt;&gt;vv&gt;v^v^&lt;&gt;&gt;&lt;&gt;&gt;&gt;&gt;&lt;^^&gt;vv&gt;v&lt;^^^&gt;&gt;v^v^&lt;^^&gt;v^^&gt;v^&lt;^v&gt;v&lt;&gt;&gt;v^v^&lt;v&gt;v^^&lt;^^vv&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;&lt;v&lt;^&gt;&gt;^^^^&gt;&gt;&gt;v^&lt;&gt;vvv^&gt;&lt;v&lt;&lt;&lt;&gt;^^^vv^&lt;vvv&gt;^&gt;v&lt;^^^^v&lt;&gt;^&gt;vvvv&gt;&lt;&gt;&gt;v^&lt;&lt;^^^^^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^&gt;&lt;^&gt;&lt;&gt;&gt;&gt;&lt;&gt;^^&lt;&lt;^^v&gt;&gt;&gt;&lt;^&lt;v&gt;^&lt;vv&gt;&gt;v&gt;&gt;&gt;^v&gt;&lt;&gt;^v&gt;&lt;&lt;&lt;&lt;v&gt;&gt;v&lt;v&lt;v&gt;vvv&gt;^&lt;&gt;&lt;&lt;&gt;^&gt;&lt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^&gt;&gt;&lt;&gt;^v&lt;&gt;&lt;^vvv&lt;^^&lt;&gt;&lt;v&lt;&lt;&lt;&lt;&lt;&gt;&lt;^v&lt;&lt;&lt;&gt;&lt;&lt;&lt;^^&lt;v&lt;^^^&gt;&lt;^&gt;&gt;^&lt;v^&gt;&lt;&lt;&lt;^&gt;&gt;^v&lt;v^v&lt;v^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&gt;^&gt;&gt;^v&gt;vv&gt;^&lt;&lt;^v&lt;&gt;&gt;&lt;&lt;&gt;&lt;&lt;v&lt;&lt;v&gt;&lt;&gt;v&lt;^vv&lt;&lt;&lt;&gt;^^v^&gt;^^&gt;&gt;&gt;&lt;&lt;^v&gt;&gt;v^v&gt;&lt;^^&gt;&gt;^&lt;&gt;vv^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&lt;&gt;&lt;^^&gt;^^^&lt;&gt;&lt;vvvvv^v&lt;v&lt;&lt;&gt;^v&lt;v&gt;v&lt;&lt;^&gt;&lt;&lt;&gt;&lt;&lt;&gt;&lt;&lt;&lt;^^&lt;&lt;&lt;^&lt;&lt;&gt;&gt;&lt;&lt;&gt;&lt;^^^&gt;^^&lt;&gt;^&gt;v&lt;&gt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">^^&gt;vv&lt;^v^v&lt;vv&gt;^&lt;&gt;&lt;v&lt;^v&gt;^^^&gt;&gt;&gt;^^vvv^&gt;vvv&lt;&gt;&gt;&gt;^&lt;^&gt;&gt;&gt;&gt;&gt;^&lt;&lt;^v&gt;^vvv&lt;&gt;^&lt;&gt;&lt;&lt;v&gt;
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">v^^&gt;&gt;&gt;&lt;&lt;^^&lt;&gt;&gt;^v^&lt;v^vv&lt;&gt;v^&lt;&lt;&gt;^&lt;^v^v&gt;&lt;^&lt;&lt;&lt;&gt;&lt;&lt;^&lt;v&gt;&lt;v&lt;&gt;vv&gt;&gt;v&gt;&lt;v^&lt;vv&lt;&gt;v^&lt;&lt;^
+</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">le_entrada</span>(entrada: str) <span style="color:#f92672">-&gt;</span> tuple[str, str]:
+</span></span><span style="display:flex;"><span> labirinto <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> comandos <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> linha <span style="color:#f92672">in</span> entrada<span style="color:#f92672">.</span>splitlines():
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> linha<span style="color:#f92672">.</span>strip():
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> linha<span style="color:#f92672">.</span>startswith(<span style="color:#e6db74">&#34;#&#34;</span>):
+</span></span><span style="display:flex;"><span> labirinto<span style="color:#f92672">.</span>append(linha)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>:
+</span></span><span style="display:flex;"><span> comandos<span style="color:#f92672">.</span>append(linha)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> labirinto, comandos
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Vector2dComSimbolo</span>(Vector2d):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x, símbolo):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>símbolo <span style="color:#f92672">=</span> símbolo
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __add__(self, other):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> Vector2dComSimbolo(self<span style="color:#f92672">.</span>y <span style="color:#f92672">+</span> other<span style="color:#f92672">.</span>y, self<span style="color:#f92672">.</span>x <span style="color:#f92672">+</span> other<span style="color:#f92672">.</span>x, self<span style="color:#f92672">.</span>símbolo)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __sub__(self, other):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> Vector2dComSimbolo(self<span style="color:#f92672">.</span>y <span style="color:#f92672">-</span> other<span style="color:#f92672">.</span>y, self<span style="color:#f92672">.</span>x <span style="color:#f92672">-</span> other<span style="color:#f92672">.</span>x, self<span style="color:#f92672">.</span>símbolo)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Caixas</span>(Vector2dComSimbolo):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x, símbolo<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;O&#34;</span>):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, símbolo)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Parede</span>(Vector2dComSimbolo):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, <span style="color:#e6db74">&#34;#&#34;</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Robô</span>(Vector2dComSimbolo):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, y, x):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(y, x, <span style="color:#e6db74">&#34;@&#34;</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Armazém</span>(Labirinto):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">inicie_desenho</span>(self, fundo<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;.&#34;</span>):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>fundo <span style="color:#f92672">=</span> fundo
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>desenho <span style="color:#f92672">=</span> []
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> _ <span style="color:#f92672">in</span> range(self<span style="color:#f92672">.</span>max_y):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>desenho<span style="color:#f92672">.</span>append([fundo] <span style="color:#f92672">*</span> self<span style="color:#f92672">.</span>max_x)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">desenhe</span>(self, objetos: list[Vector2dComSimbolo]):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> objeto <span style="color:#f92672">in</span> objetos:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>desenho[objeto<span style="color:#f92672">.</span>y][objeto<span style="color:#f92672">.</span>x] <span style="color:#f92672">=</span> objeto<span style="color:#f92672">.</span>símbolo
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">dump_desenho</span>(self, output<span style="color:#f92672">=</span>sys<span style="color:#f92672">.</span>stdout):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> linha <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>desenho:
+</span></span><span style="display:flex;"><span> print(<span style="color:#e6db74">&#34;&#34;</span><span style="color:#f92672">.</span>join(linha), file<span style="color:#f92672">=</span>output)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __iter__(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> iter(self<span style="color:#f92672">.</span>labirinto)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Problema</span>:
+</span></span><span style="display:flex;"><span> DIREÇÕES <span style="color:#f92672">=</span> {
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;&lt;&#34;</span>: Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;^&#34;</span>: Vector2d(<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;&gt;&#34;</span>: Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>),
+</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;v&#34;</span>: Vector2d(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>),
+</span></span><span style="display:flex;"><span> }
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, labirinto: list[str], comandos: list[str]):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>comandos <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;</span><span style="color:#f92672">.</span>join(comandos)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém <span style="color:#f92672">=</span> Armazém(labirinto)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>monta()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">monta</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>paredes <span style="color:#f92672">=</span> set()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas <span style="color:#f92672">=</span> dict()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> y, linha <span style="color:#f92672">in</span> enumerate(self<span style="color:#f92672">.</span>armazém):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> x, char <span style="color:#f92672">in</span> enumerate(linha):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;#&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>paredes<span style="color:#f92672">.</span>add(Parede(y, x))
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;O&#34;</span>:
+</span></span><span style="display:flex;"><span> caixa <span style="color:#f92672">=</span> Caixas(y, x)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas[caixa] <span style="color:#f92672">=</span> caixa
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;@&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">=</span> Robô(y, x)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">desenha</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>inicie_desenho()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>desenhe(self<span style="color:#f92672">.</span>paredes)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>desenhe(self<span style="color:#f92672">.</span>caixas)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>desenhe([self<span style="color:#f92672">.</span>robô])
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>dump_desenho()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">executa_comandos</span>(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> comando <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>comandos:
+</span></span><span style="display:flex;"><span> direção <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>DIREÇÕES[comando]
+</span></span><span style="display:flex;"><span> nova_posição <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">+</span> direção
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> nova_posição <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> self<span style="color:#f92672">.</span>empurra(nova_posição, direção):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">elif</span> nova_posição <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>paredes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">+=</span> direção
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">empurra</span>(self, posição: Vector2d, direção: Vector2d, executa: bool <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>):
+</span></span><span style="display:flex;"><span> nova_posição <span style="color:#f92672">=</span> posição <span style="color:#f92672">+</span> direção
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> nova_posição <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>paredes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> nova_posição <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> pode_empurrar <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>empurra(nova_posição, direção, executa)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> pode_empurrar:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> executa:
+</span></span><span style="display:flex;"><span> caixa <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>caixas<span style="color:#f92672">.</span>pop(posição)
+</span></span><span style="display:flex;"><span> caixa<span style="color:#f92672">.</span>y <span style="color:#f92672">=</span> nova_posição<span style="color:#f92672">.</span>y
+</span></span><span style="display:flex;"><span> caixa<span style="color:#f92672">.</span>x <span style="color:#f92672">=</span> nova_posição<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas[caixa] <span style="color:#f92672">=</span> caixa
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">gps_caixas</span>(self):
+</span></span><span style="display:flex;"><span> soma <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> caixas <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> soma <span style="color:#f92672">+=</span> caixas<span style="color:#f92672">.</span>y <span style="color:#f92672">*</span> <span style="color:#ae81ff">100</span> <span style="color:#f92672">+</span> caixas<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> soma
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">prompt</span>(self):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">while</span> <span style="color:#66d9ef">True</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>desenha()
+</span></span><span style="display:flex;"><span> comandos <span style="color:#f92672">=</span> input(<span style="color:#e6db74">&#34;Comandos: &#34;</span>)<span style="color:#f92672">.</span>lower()<span style="color:#f92672">.</span>strip()
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> comandos:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>comandos <span style="color:#f92672">=</span> comandos
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Problema2</span>(Problema):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, labirinto: list[str], comandos: list[str]):
+</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(labirinto, comandos)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armazém<span style="color:#f92672">.</span>max_x <span style="color:#f92672">*=</span> <span style="color:#ae81ff">2</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">monta</span>(self):
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>paredes <span style="color:#f92672">=</span> set()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas <span style="color:#f92672">=</span> dict()
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> y, linha <span style="color:#f92672">in</span> enumerate(self<span style="color:#f92672">.</span>armazém):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> x, char <span style="color:#f92672">in</span> enumerate(linha):
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;#&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>paredes<span style="color:#f92672">.</span>add(Parede(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>))
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>paredes<span style="color:#f92672">.</span>add(Parede(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>))
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;O&#34;</span>:
+</span></span><span style="display:flex;"><span> caixa1 <span style="color:#f92672">=</span> Caixas(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>, <span style="color:#e6db74">&#34;[&#34;</span>)
+</span></span><span style="display:flex;"><span> caixa2 <span style="color:#f92672">=</span> Caixas(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>, <span style="color:#e6db74">&#34;]&#34;</span>)
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas[caixa1] <span style="color:#f92672">=</span> caixa1
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas[caixa2] <span style="color:#f92672">=</span> caixa2
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> char <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;@&#34;</span>:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>robô <span style="color:#f92672">=</span> Robô(y, x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">empurra</span>(self, posição: Vector2d, direção: Vector2d, executa: bool <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>):
+</span></span><span style="display:flex;"><span> nova_posição <span style="color:#f92672">=</span> posição <span style="color:#f92672">+</span> direção
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> direção <span style="color:#f92672">in</span> [Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>), Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>)] <span style="color:#f92672">or</span> nova_posição <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>paredes:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> super()<span style="color:#f92672">.</span>empurra(posição, direção, executa)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> posição <span style="color:#f92672">not</span> <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> caixa1, caixa2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>ache_caixa(posição)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> pode_empurrar1 <span style="color:#f92672">=</span> super()<span style="color:#f92672">.</span>empurra(caixa1, direção, <span style="color:#66d9ef">False</span>)
+</span></span><span style="display:flex;"><span> pode_empurrar2 <span style="color:#f92672">=</span> super()<span style="color:#f92672">.</span>empurra(caixa2, direção, <span style="color:#66d9ef">False</span>)
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> pode_empurrar1 <span style="color:#f92672">and</span> pode_empurrar2:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> executa:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>empurra_v(caixa1, caixa2, direção)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">False</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">ache_caixa</span>(self, posição):
+</span></span><span style="display:flex;"><span> caixa1 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>caixas[posição]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> caixa1<span style="color:#f92672">.</span>símbolo <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;[&#34;</span>:
+</span></span><span style="display:flex;"><span> caixa2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>caixas[posição <span style="color:#f92672">+</span> Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>)]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>:
+</span></span><span style="display:flex;"><span> caixa2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>caixas[posição <span style="color:#f92672">+</span> Vector2d(<span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)]
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> caixa1, caixa2
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">empurra_v</span>(self, posição1, posição2, direção):
+</span></span><span style="display:flex;"><span> np1 <span style="color:#f92672">=</span> posição1 <span style="color:#f92672">+</span> direção
+</span></span><span style="display:flex;"><span> np2 <span style="color:#f92672">=</span> posição2 <span style="color:#f92672">+</span> direção
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> np1 <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>empurra_v(<span style="color:#f92672">*</span>self<span style="color:#f92672">.</span>ache_caixa(np1), direção)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> np2 <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>empurra_v(<span style="color:#f92672">*</span>self<span style="color:#f92672">.</span>ache_caixa(np2), direção)
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> posição, nova_posição <span style="color:#f92672">in</span> zip([posição1, posição2], [np1, np2]):
+</span></span><span style="display:flex;"><span> caixa <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>caixas<span style="color:#f92672">.</span>pop(posição)
+</span></span><span style="display:flex;"><span> caixa<span style="color:#f92672">.</span>y <span style="color:#f92672">=</span> nova_posição<span style="color:#f92672">.</span>y
+</span></span><span style="display:flex;"><span> caixa<span style="color:#f92672">.</span>x <span style="color:#f92672">=</span> nova_posição<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>caixas[caixa] <span style="color:#f92672">=</span> caixa
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">gps_caixas</span>(self):
+</span></span><span style="display:flex;"><span> soma <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> caixa <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>caixas:
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> caixa<span style="color:#f92672">.</span>símbolo <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;[&#34;</span>:
+</span></span><span style="display:flex;"><span> soma <span style="color:#f92672">+=</span> caixa<span style="color:#f92672">.</span>y <span style="color:#f92672">*</span> <span style="color:#ae81ff">100</span> <span style="color:#f92672">+</span> caixa<span style="color:#f92672">.</span>x
+</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> soma
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problema <span style="color:#f92672">=</span> Problema(<span style="color:#f92672">*</span>le_entrada(ENTRADA_TESTE))
+</span></span><span style="display:flex;"><span>problema<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problema<span style="color:#f92672">.</span>gps_caixas()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Parte 1 - teste pequena entrada:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">2028</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problema <span style="color:#f92672">=</span> Problema(<span style="color:#f92672">*</span>le_entrada(ENTRADA))
+</span></span><span style="display:flex;"><span>problema<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problema<span style="color:#f92672">.</span>gps_caixas()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Parte 1 - teste entrada grande:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">10092</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problema <span style="color:#f92672">=</span> Problema(<span style="color:#f92672">*</span>le_entrada(le_arquivo_sem_linhas_em_branco(<span style="color:#e6db74">&#34;15/input.txt&#34;</span>)))
+</span></span><span style="display:flex;"><span>problema<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problema<span style="color:#f92672">.</span>gps_caixas()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Parte 1 - entrada grande:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">1552463</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span><span style="color:#75715e"># Parte 2</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problema <span style="color:#f92672">=</span> Problema2(<span style="color:#f92672">*</span>le_entrada(ENTRADA))
+</span></span><span style="display:flex;"><span>problema<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problema<span style="color:#f92672">.</span>gps_caixas()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Parte 2 - teste entrada grande:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">9021</span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>
+</span></span><span style="display:flex;"><span>problema <span style="color:#f92672">=</span> Problema2(<span style="color:#f92672">*</span>le_entrada(le_arquivo_sem_linhas_em_branco(<span style="color:#e6db74">&#34;15/input.txt&#34;</span>)))
+</span></span><span style="display:flex;"><span>problema<span style="color:#f92672">.</span>executa_comandos()
+</span></span><span style="display:flex;"><span>gps <span style="color:#f92672">=</span> problema<span style="color:#f92672">.</span>gps_caixas()
+</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Parte 2 - entrada grande:&#34;</span>, gps)
+</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> gps <span style="color:#f92672">==</span> <span style="color:#ae81ff">1554058</span>
+</span></span></code></pre></div>
+ Mon, 16 Dec 2024 00:49:05 +0000
+
+
+ Rodrigo Delduca: My First Game with Carimbo, My Homemade Engine, For my Son
+ https://nullonerror.org/2024/10/08/my-first-game-with-carimbo
+ https://nullonerror.org/2024/10/08/my-first-game-with-carimbo/
+ <p><a href="https://youtu.be/nVRzCstyspQ"><img src="https://nullonerror.org/public/2024-10-08-my-first-game-with-carimbo/megarick.webp" alt="MegaRick Game" class="center" /></a>
+<a href="https://youtu.be/nVRzCstyspQ">https://youtu.be/nVRzCstyspQ</a></p>
+
+<h3 id="tldr">TL;DR</h3>
+
+<p>After a while, I decided to resume work on <a href="https://github.com/carimbolabs/carimbo">my game engine</a>.</p>
+
+<p>I made a game for my son. I could have used an existing engine, but I chose to write everything from scratch because code is like pasta—it’s much better and more enjoyable when homemade.</p>
+
+<p>This actually reminds me of when my father used to build my toys, from kites to wooden slides. Good memories. I have decided to do the same using what I know: programming.</p>
+
+<p>You can watch it here or <a href="https://carimbo.run">play it here</a>, (runs in the browser thanks to WebAssembly), use <code class="language-plaintext highlighter-rouge">A</code> and <code class="language-plaintext highlighter-rouge">D</code> for moving around and <code class="language-plaintext highlighter-rouge">space</code> to shoot.</p>
+
+<p>The engine was written in C++17, and the games are in Lua. The engine exposes some primitives to the Lua VM, which in turn coordinates the entire game.</p>
+
+<p><a href="https://github.com/willtobyte/megarick">Game source code</a> and <a href="https://github.com/willtobyte/carimbo">engine source code</a>.</p>
+
+<p>Artwork by <a href="https://www.fiverr.com/yuugenpixie">Aline Cardoso @yuugenpixie</a>.</p>
+
+<h3 id="result">Result</h3>
+
+<p><img src="https://nullonerror.org/public/2024-10-08-my-first-game-with-carimbo/play.jpeg" alt="Henrique Playing" class="center" /></p>
+
+<h3 id="going-deep">Going Deep</h3>
+
+<p>The entire game is scripted in <a href="https://www.lua.org">Lua</a>.</p>
+
+<p>First, we create the engine itself.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">local</span> <span class="n">engine</span> <span class="o">=</span> <span class="n">EngineFactory</span><span class="p">.</span><span class="n">new</span><span class="p">()</span>
+ <span class="p">:</span><span class="n">set_title</span><span class="p">(</span><span class="s2">"Mega Rick"</span><span class="p">)</span>
+ <span class="p">:</span><span class="n">set_width</span><span class="p">(</span><span class="mi">1920</span><span class="p">)</span>
+ <span class="p">:</span><span class="n">set_height</span><span class="p">(</span><span class="mi">1080</span><span class="p">)</span>
+ <span class="p">:</span><span class="n">set_fullscreen</span><span class="p">(</span><span class="kc">false</span><span class="p">)</span>
+ <span class="p">:</span><span class="n">create</span><span class="p">()</span>
+</code></pre></div></div>
+
+<p>Then we point to some resources for asset prefetching; they are loaded lazily to avoid impacting the <em>game loop</em>.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">engine</span><span class="p">:</span><span class="n">prefetch</span><span class="p">({</span>
+ <span class="s2">"blobs/bomb1.ogg"</span><span class="p">,</span>
+ <span class="s2">"blobs/bomb2.ogg"</span><span class="p">,</span>
+ <span class="s2">"blobs/bullet.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/candle.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/explosion.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/octopus.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/player.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/princess.png"</span><span class="p">,</span>
+ <span class="s2">"blobs/ship.png"</span>
+<span class="p">})</span>
+</code></pre></div></div>
+
+<p>We created the postal service, which is something I borrowed from languages like <em>Erlang</em>, where an entity can send messages to any other. As we’ll see below, the bullet sends a hit message when it collides with the octopus, and the octopus, upon receiving the hit, decrements its own health.</p>
+
+<p>We also obtain the SoundManager to play sounds and spawn the main entities.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">local</span> <span class="n">postal</span> <span class="o">=</span> <span class="n">PostalService</span><span class="p">.</span><span class="n">new</span><span class="p">()</span>
+<span class="kd">local</span> <span class="n">soundmanager</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">soundmanager</span><span class="p">()</span>
+<span class="kd">local</span> <span class="n">octopus</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"octopus"</span><span class="p">)</span>
+<span class="kd">local</span> <span class="n">player</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"player"</span><span class="p">)</span>
+<span class="kd">local</span> <span class="n">princess</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"princess"</span><span class="p">)</span>
+<span class="kd">local</span> <span class="n">candle1</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"candle"</span><span class="p">)</span>
+<span class="kd">local</span> <span class="n">candle2</span> <span class="o">=</span> <span class="n">engine</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"candle"</span><span class="p">)</span>
+</code></pre></div></div>
+
+<p>It’s not a triple-A title, but I’m very careful with resource management. A widely known technique is to create an object pool that you can reuse. In the code below, we limit it to a maximum of 3 bullets present on the screen.</p>
+
+<p>The <code class="language-plaintext highlighter-rouge">on_update</code> method is a callback called each loop in the engine. In the case of the bullet, I check if it has approached the green octopus. If so, I send a message to it indicating that it received a hit.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">_</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">3</span> <span class="k">do</span>
+ <span class="kd">local</span> <span class="n">bullet</span> <span class="o">=</span> <span class="n">entitymanager</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"bullet"</span><span class="p">)</span>
+ <span class="n">bullet</span><span class="p">.</span><span class="n">placement</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="o">-</span><span class="mi">128</span><span class="p">,</span> <span class="o">-</span><span class="mi">128</span><span class="p">)</span>
+ <span class="n">bullet</span><span class="p">:</span><span class="n">on_collision</span><span class="p">(</span><span class="s2">"octopus"</span><span class="p">,</span> <span class="k">function</span><span class="p">(</span><span class="n">self</span><span class="p">)</span>
+ <span class="n">self</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">unset</span><span class="p">()</span>
+ <span class="n">self</span><span class="p">.</span><span class="n">placement</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="o">-</span><span class="mi">128</span><span class="p">,</span> <span class="o">-</span><span class="mi">128</span><span class="p">)</span>
+ <span class="n">postalservice</span><span class="p">:</span><span class="n">post</span><span class="p">(</span><span class="n">Mail</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="n">octopus</span><span class="p">,</span> <span class="s2">"bullet"</span><span class="p">,</span> <span class="s2">"hit"</span><span class="p">))</span>
+ <span class="nb">table.insert</span><span class="p">(</span><span class="n">bullet_pool</span><span class="p">,</span> <span class="n">self</span><span class="p">)</span>
+ <span class="k">end</span><span class="p">)</span>
+ <span class="nb">table.insert</span><span class="p">(</span><span class="n">bullet_pool</span><span class="p">,</span> <span class="n">bullet</span><span class="p">)</span>
+ <span class="k">end</span>
+</code></pre></div></div>
+
+<p>On the octopus’s side, when it receives a “hit” message, the entity triggers an explosion animation, switches to attack mode, and decreases its health by 1. If it reaches 0, it changes the animation to “dead”.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">octopus</span> <span class="o">=</span> <span class="n">entitymanager</span><span class="p">:</span><span class="n">spawn</span><span class="p">(</span><span class="s2">"octopus"</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">.</span><span class="n">kv</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"life"</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">.</span><span class="n">placement</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="mi">1200</span><span class="p">,</span> <span class="mi">622</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"idle"</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">:</span><span class="n">on_mail</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="kd">local</span> <span class="n">behavior</span> <span class="o">=</span> <span class="n">behaviors</span><span class="p">[</span><span class="n">message</span><span class="p">]</span>
+ <span class="k">if</span> <span class="n">behavior</span> <span class="k">then</span>
+ <span class="n">behavior</span><span class="p">(</span><span class="n">self</span><span class="p">)</span>
+ <span class="k">end</span>
+<span class="k">end</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">.</span><span class="n">kv</span><span class="p">:</span><span class="n">subscribe</span><span class="p">(</span><span class="s2">"life"</span><span class="p">,</span> <span class="k">function</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
+ <span class="n">vitality</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="nb">string.format</span><span class="p">(</span><span class="s2">"%02d-"</span><span class="p">,</span> <span class="nb">math.max</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="mi">0</span><span class="p">)))</span>
+
+ <span class="k">if</span> <span class="n">value</span> <span class="o"><=</span> <span class="mi">0</span> <span class="k">then</span>
+ <span class="n">octopus</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"dead"</span><span class="p">)</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">timer</span> <span class="k">then</span>
+ <span class="n">timemanager</span><span class="p">:</span><span class="n">singleshot</span><span class="p">(</span><span class="mi">3000</span><span class="p">,</span> <span class="k">function</span><span class="p">()</span>
+ <span class="kd">local</span> <span class="k">function</span> <span class="nf">destroy</span><span class="p">(</span><span class="n">pool</span><span class="p">)</span>
+ <span class="k">for</span> <span class="n">i</span> <span class="o">=</span> <span class="o">#</span><span class="n">pool</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="k">do</span>
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">pool</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
+ <span class="nb">table.remove</span><span class="p">(</span><span class="n">pool</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
+ <span class="n">pool</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="kc">nil</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+
+ <span class="n">destroy</span><span class="p">(</span><span class="n">bullet_pool</span><span class="p">)</span>
+ <span class="n">destroy</span><span class="p">(</span><span class="n">explosion_pool</span><span class="p">)</span>
+ <span class="n">destroy</span><span class="p">(</span><span class="n">jet_pool</span><span class="p">)</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">octopus</span><span class="p">)</span>
+ <span class="n">octopus</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">player</span><span class="p">)</span>
+ <span class="n">player</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">princess</span><span class="p">)</span>
+ <span class="n">princess</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">candle1</span><span class="p">)</span>
+ <span class="n">candle1</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">candle2</span><span class="p">)</span>
+ <span class="n">candle2</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">entitymanager</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">floor</span><span class="p">)</span>
+ <span class="n">floor</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">overlay</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">vitality</span><span class="p">)</span>
+ <span class="n">vitality</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">overlay</span><span class="p">:</span><span class="n">destroy</span><span class="p">(</span><span class="n">online</span><span class="p">)</span>
+ <span class="n">online</span> <span class="o">=</span> <span class="kc">nil</span>
+
+ <span class="n">scenemanager</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"gameover"</span><span class="p">)</span>
+
+ <span class="nb">collectgarbage</span><span class="p">(</span><span class="s2">"collect"</span><span class="p">)</span>
+
+ <span class="n">resourcemanager</span><span class="p">:</span><span class="n">flush</span><span class="p">()</span>
+ <span class="k">end</span><span class="p">)</span>
+ <span class="n">timer</span> <span class="o">=</span> <span class="kc">true</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+<span class="k">end</span><span class="p">)</span>
+<span class="n">octopus</span><span class="p">:</span><span class="n">on_animationfinished</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="n">self</span><span class="p">)</span>
+ <span class="n">self</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"idle"</span><span class="p">)</span>
+<span class="k">end</span><span class="p">)</span>
+</code></pre></div></div>
+
+<p>And finally, the player’s <code class="language-plaintext highlighter-rouge">on_update</code>, where I apply the velocity if any movement keys are pressed, or set the animation to idle if none are pressed.</p>
+
+<p>We also have the projectile firing, where the fire function is called, taking an instance of a bullet from the pool, placing it in a semi-random position, and applying velocity.</p>
+
+<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">function</span> <span class="nf">loop</span><span class="p">()</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">player</span> <span class="k">then</span>
+ <span class="k">return</span>
+ <span class="k">end</span>
+
+ <span class="n">player</span><span class="p">.</span><span class="n">velocity</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">0</span>
+
+ <span class="k">if</span> <span class="n">statemanager</span><span class="p">:</span><span class="n">is_keydown</span><span class="p">(</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">left</span><span class="p">)</span> <span class="k">then</span>
+ <span class="n">player</span><span class="p">.</span><span class="n">reflection</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="n">Reflection</span><span class="p">.</span><span class="n">horizontal</span><span class="p">)</span>
+ <span class="n">player</span><span class="p">.</span><span class="n">velocity</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="o">-</span><span class="mi">360</span>
+ <span class="k">elseif</span> <span class="n">statemanager</span><span class="p">:</span><span class="n">is_keydown</span><span class="p">(</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">right</span><span class="p">)</span> <span class="k">then</span>
+ <span class="n">player</span><span class="p">.</span><span class="n">reflection</span><span class="p">:</span><span class="n">unset</span><span class="p">()</span>
+ <span class="n">player</span><span class="p">.</span><span class="n">velocity</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">360</span>
+ <span class="k">end</span>
+
+ <span class="n">player</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="n">player</span><span class="p">.</span><span class="n">velocity</span><span class="p">.</span><span class="n">x</span> <span class="o">~=</span> <span class="mi">0</span> <span class="ow">and</span> <span class="s2">"run"</span> <span class="ow">or</span> <span class="s2">"idle"</span><span class="p">)</span>
+
+ <span class="k">if</span> <span class="n">statemanager</span><span class="p">:</span><span class="n">is_keydown</span><span class="p">(</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">space</span><span class="p">)</span> <span class="k">then</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">key_states</span><span class="p">[</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">space</span><span class="p">]</span> <span class="k">then</span>
+ <span class="n">key_states</span><span class="p">[</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">space</span><span class="p">]</span> <span class="o">=</span> <span class="kc">true</span>
+
+ <span class="c1">-- player.velocity.y = -360</span>
+
+ <span class="k">if</span> <span class="n">octopus</span><span class="p">.</span><span class="n">kv</span><span class="p">:</span><span class="n">get</span><span class="p">(</span><span class="s2">"life"</span><span class="p">)</span> <span class="o"><=</span> <span class="mi">0</span> <span class="k">then</span>
+ <span class="k">return</span>
+ <span class="k">end</span>
+
+ <span class="k">if</span> <span class="o">#</span><span class="n">bullet_pool</span> <span class="o">></span> <span class="mi">0</span> <span class="k">then</span>
+ <span class="kd">local</span> <span class="n">bullet</span> <span class="o">=</span> <span class="nb">table.remove</span><span class="p">(</span><span class="n">bullet_pool</span><span class="p">)</span>
+ <span class="kd">local</span> <span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="n">player</span><span class="p">.</span><span class="n">x</span> <span class="o">+</span> <span class="n">player</span><span class="p">.</span><span class="n">size</span><span class="p">.</span><span class="n">width</span><span class="p">)</span> <span class="o">+</span> <span class="mi">100</span>
+ <span class="kd">local</span> <span class="n">y</span> <span class="o">=</span> <span class="n">player</span><span class="p">.</span><span class="n">y</span> <span class="o">+</span> <span class="mi">10</span>
+ <span class="kd">local</span> <span class="n">offset_y</span> <span class="o">=</span> <span class="p">(</span><span class="nb">math.random</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span> <span class="o">*</span> <span class="mi">30</span>
+
+ <span class="n">bullet</span><span class="p">.</span><span class="n">placement</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="n">offset_y</span><span class="p">)</span>
+ <span class="n">bullet</span><span class="p">.</span><span class="n">action</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="s2">"default"</span><span class="p">)</span>
+ <span class="n">bullet</span><span class="p">.</span><span class="n">velocity</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">800</span>
+
+ <span class="kd">local</span> <span class="n">sound</span> <span class="o">=</span> <span class="s2">"bomb"</span> <span class="o">..</span> <span class="nb">math.random</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
+ <span class="n">soundmanager</span><span class="p">:</span><span class="n">play</span><span class="p">(</span><span class="n">sound</span><span class="p">)</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+ <span class="k">else</span>
+ <span class="n">key_states</span><span class="p">[</span><span class="n">KeyEvent</span><span class="p">.</span><span class="n">space</span><span class="p">]</span> <span class="o">=</span> <span class="kc">false</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</code></pre></div></div>
+
+<h3 id="in-the-end">In The End</h3>
+
+<p>In the end, he requested some <em>minor</em> tweaks, such as the projectile being the princess sprite, the target being the player, and the entity that shoots being the green octopus. 🤷♂️</p>
+
+<p><img src="https://nullonerror.org/public/2024-10-08-my-first-game-with-carimbo/tweaks.jpeg" alt="Tweaks" class="center" /></p>
+ Tue, 08 Oct 2024 00:00:00 +0000
+ rodrigo@delduca.org (Rodrigo Delduca)
+
+
+ Rodrigo Delduca: How I saved a few dozen dollars by migrating my personal projects from the cloud to a Raspberry Pi
+ https://nullonerror.org/2024/06/05/how-i-saved-a-few-dozen-dollars-by-migrating-my-personal-projects-from-the-cloud-to-a-raspberry-pi
+ https://nullonerror.org/2024/06/05/how-i-saved-a-few-dozen-dollars-by-migrating-my-personal-projects-from-the-cloud-to-a-raspberry-pi/
+ <h3 id="intro">Intro</h3>
+
+<p>Since the first URL shorteners emerged, that’s always drawn me in for some reason, perhaps because I figured out on my own that they encoded the primary key in base36 (maybe).</p>
+
+<p>So around ~2010, I decided to make my own. It was called “encurta.ae” (make it short there in Portuguese), and it was based on AppEngine. Even back then, I was quite fond of serverless and such.</p>
+
+<p>It worked great locally, but when I deployed it, the datastore IDs were too large, causing the URLs to be too long.</p>
+
+<p>Fast-forward to nowadays, I’ve decided to bring this project to life again. This time with a twist: when shortening the URL, the shortener would take a screenshot of the website and embed Open Graph tags in the redirect URL. This way, when shared on a social network, the link would display a title, description, and thumbnail that accurately represent what the user is about to open.</p>
+
+<h3 id="stack">Stack</h3>
+
+<p>Typically, I use serverless for my personal projects because they scale to zero, thus eliminating costs when not in use. However, after working with serverless for many years, I’ve been wanting to experiment with a more grounded and down-to-earth approach to development and deployment.</p>
+
+<ul>
+ <li>I opted to use <a href="https://go.dev/">Go</a>, with several <a href="https://en.wikipedia.org/wiki/Coroutine">goroutines</a> performing tasks in parallel, and a purely written work queue mechanism.</li>
+ <li><a href="https://playwright.dev/">Playwright</a> & Chromium for automation and screenshot.</li>
+ <li><a href="https://sqlite.org/">SQLite</a> for the database (it’s simple)</li>
+ <li><a href="https://www.backblaze.com/">Backblaze</a> for storage</li>
+ <li><a href="https://bunny.net/">BunnyCDN</a> for cotent delivery</li>
+ <li><a href="https://www.papertrail.com/">Papertrail</a> for logging</li>
+</ul>
+
+<p>Deployment is done using Docker Compose, via SSH using the DOCKER_HOST environment variable pointing directly to a <a href="https://www.raspberrypi.com/">Raspberry Pi</a> that I had bought and never used before. Now it saves me $5 per month, and I can keep a limited number of projects running on it.</p>
+
+<p>And then you might ask: How do you expose it to the internet? I use <a href="https://www.cloudflare.com/products/tunnel/">Cloudflare Tunnel</a>; the setup is simple and creates a direct connection between the Raspberry Pi and the nearest <a href="https://en.wikipedia.org/wiki/Point_of_presence">Point of Presence</a>.</p>
+
+<p>This type of hosting is extremely advantageous because the server’s IP and/or ports are never revealed; it stays behind my firewall. Everything goes through Cloudflare.</p>
+
+<p>I have more than one Docker Compose file, and that’s what’s coolest. Locally, I run one, for deployment I instruct the Compose to read two others for logging and tunneling.</p>
+
+<p><code class="language-plaintext highlighter-rouge">docker-compose.yaml</code></p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span>
+ <span class="na">app</span><span class="pi">:</span>
+ <span class="na">build</span><span class="pi">:</span> <span class="s">.</span>
+ <span class="na">env_file</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">.env</span>
+ <span class="na">ports</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s2">"</span><span class="s">8000:8000"</span>
+ <span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span>
+ <span class="na">volumes</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">data:/data</span>
+ <span class="na">tmpfs</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">/tmp</span>
+<span class="na">volumes</span><span class="pi">:</span>
+ <span class="na">data</span><span class="pi">:</span>
+</code></pre></div></div>
+
+<p><code class="language-plaintext highlighter-rouge">docker-compose.logging.yaml</code></p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span>
+ <span class="na">app</span><span class="pi">:</span>
+ <span class="na">logging</span><span class="pi">:</span>
+ <span class="na">driver</span><span class="pi">:</span> <span class="s">syslog</span>
+ <span class="na">options</span><span class="pi">:</span>
+ <span class="na">syslog-address</span><span class="pi">:</span> <span class="s2">"</span><span class="s">udp://logs2.papertrailapp.com:XXX"</span>
+</code></pre></div></div>
+
+<p><code class="language-plaintext highlighter-rouge">docker-compose.cloudflare.yaml</code></p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span>
+ <span class="na">tunnel</span><span class="pi">:</span>
+ <span class="na">image</span><span class="pi">:</span> <span class="s">cloudflare/cloudflared</span>
+ <span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span>
+ <span class="na">command</span><span class="pi">:</span> <span class="s">tunnel run</span>
+ <span class="na">environment</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">TUNNEL_TOKEN=yourtoken</span>
+</code></pre></div></div>
+
+<p>Then for deploying</p>
+
+<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">DOCKER_HOST</span><span class="o">=</span>ssh://pi@192.168.0.10 docker compose <span class="nt">--file</span> docker-compose.yaml <span class="nt">--file</span> docker-compose.logging.yaml <span class="nt">--file</span> docker-compose.cloudflare.yaml up <span class="nt">--build</span> <span class="nt">--detach</span>
+</code></pre></div></div>
+
+<p>Example of the “worker queue”</p>
+
+<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">functions</span>
+
+<span class="k">import</span> <span class="p">(</span>
+ <span class="s">"context"</span>
+ <span class="s">"database/sql"</span>
+ <span class="s">"fmt"</span>
+ <span class="s">"os"</span>
+ <span class="s">"os/exec"</span>
+ <span class="s">"path/filepath"</span>
+ <span class="s">"sync"</span>
+ <span class="s">"time"</span>
+
+ <span class="n">google</span> <span class="s">"cloud.google.com/go/vision/apiv1"</span>
+ <span class="n">visionpb</span> <span class="s">"cloud.google.com/go/vision/v2/apiv1/visionpb"</span>
+ <span class="s">"github.com/martinlindhe/base36"</span>
+ <span class="s">"github.com/minio/minio-go/v7"</span>
+ <span class="s">"github.com/minio/minio-go/v7/pkg/credentials"</span>
+ <span class="s">"github.com/playwright-community/playwright-go"</span>
+ <span class="s">"go.uber.org/zap"</span>
+ <span class="s">"google.golang.org/api/option"</span>
+ <span class="n">log</span> <span class="s">"skhaz.dev/urlshortnen/logging"</span>
+<span class="p">)</span>
+
+<span class="k">var</span> <span class="p">(</span>
+ <span class="n">accessKeyID</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"BACKBLAZE_ACCESS_ID"</span><span class="p">)</span>
+ <span class="n">secretAccessKey</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"BACKBLAZE_APPLICATION_KEY"</span><span class="p">)</span>
+ <span class="n">bucket</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"BACKBLAZE_BUCKET"</span><span class="p">)</span>
+ <span class="n">endpoint</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"BACKBLAZE_ENDPOINT"</span><span class="p">)</span>
+ <span class="n">useSSL</span> <span class="o">=</span> <span class="no">true</span>
+ <span class="n">extension</span> <span class="o">=</span> <span class="s">"webp"</span>
+ <span class="n">mimetype</span> <span class="o">=</span> <span class="s">"image/webp"</span>
+ <span class="n">quality</span> <span class="o">=</span> <span class="s">"50"</span>
+<span class="p">)</span>
+
+<span class="k">type</span> <span class="n">WorkerFunctions</span> <span class="k">struct</span> <span class="p">{</span>
+ <span class="n">db</span> <span class="o">*</span><span class="n">sql</span><span class="o">.</span><span class="n">DB</span>
+ <span class="n">vision</span> <span class="o">*</span><span class="n">google</span><span class="o">.</span><span class="n">ImageAnnotatorClient</span>
+ <span class="n">mc</span> <span class="o">*</span><span class="n">minio</span><span class="o">.</span><span class="n">Client</span>
+ <span class="n">browser</span> <span class="n">playwright</span><span class="o">.</span><span class="n">BrowserContext</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="n">Worker</span><span class="p">(</span><span class="n">db</span> <span class="o">*</span><span class="n">sql</span><span class="o">.</span><span class="n">DB</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">defer</span> <span class="k">func</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">if</span> <span class="n">r</span> <span class="o">:=</span> <span class="nb">recover</span><span class="p">();</span> <span class="n">r</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"worker panic"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Any</span><span class="p">(</span><span class="s">"error"</span><span class="p">,</span> <span class="n">r</span><span class="p">))</span>
+ <span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span> <span class="o">*</span> <span class="m">10</span><span class="p">)</span>
+ <span class="k">go</span> <span class="n">Worker</span><span class="p">(</span><span class="n">db</span><span class="p">)</span>
+ <span class="p">}</span>
+ <span class="p">}()</span>
+
+ <span class="n">ctx</span> <span class="o">:=</span> <span class="n">context</span><span class="o">.</span><span class="n">Background</span><span class="p">()</span>
+
+ <span class="n">vision</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">google</span><span class="o">.</span><span class="n">NewImageAnnotatorClient</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">option</span><span class="o">.</span><span class="n">WithCredentialsJSON</span><span class="p">([]</span><span class="kt">byte</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"GOOGLE_CREDENTIALS"</span><span class="p">))))</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to create vision client"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">vision</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="n">mc</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">minio</span><span class="o">.</span><span class="n">New</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="o">&</span><span class="n">minio</span><span class="o">.</span><span class="n">Options</span><span class="p">{</span>
+ <span class="n">Creds</span><span class="o">:</span> <span class="n">credentials</span><span class="o">.</span><span class="n">NewStaticV4</span><span class="p">(</span><span class="n">accessKeyID</span><span class="p">,</span> <span class="n">secretAccessKey</span><span class="p">,</span> <span class="s">""</span><span class="p">),</span>
+ <span class="n">Secure</span><span class="o">:</span> <span class="n">useSSL</span><span class="p">,</span>
+ <span class="p">})</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to create minio client"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">pw</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">playwright</span><span class="o">.</span><span class="n">Run</span><span class="p">()</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to launch playwright"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="c">//nolint:golint,errcheck</span>
+ <span class="k">defer</span> <span class="n">pw</span><span class="o">.</span><span class="n">Stop</span><span class="p">()</span>
+
+ <span class="n">userDataDir</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">MkdirTemp</span><span class="p">(</span><span class="s">""</span><span class="p">,</span> <span class="s">"chromium"</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to create temporary directory"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">os</span><span class="o">.</span><span class="n">RemoveAll</span><span class="p">(</span><span class="n">userDataDir</span><span class="p">)</span>
+
+ <span class="n">browser</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">pw</span><span class="o">.</span><span class="n">Chromium</span><span class="o">.</span><span class="n">LaunchPersistentContext</span><span class="p">(</span><span class="n">userDataDir</span><span class="p">,</span> <span class="n">playwright</span><span class="o">.</span><span class="n">BrowserTypeLaunchPersistentContextOptions</span><span class="p">{</span>
+ <span class="n">Args</span><span class="o">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span>
+ <span class="s">"--headless=new"</span><span class="p">,</span>
+ <span class="s">"--no-zygote"</span><span class="p">,</span>
+ <span class="s">"--no-sandbox"</span><span class="p">,</span>
+ <span class="s">"--disable-gpu"</span><span class="p">,</span>
+ <span class="s">"--hide-scrollbars"</span><span class="p">,</span>
+ <span class="s">"--disable-setuid-sandbox"</span><span class="p">,</span>
+ <span class="s">"--disable-dev-shm-usage"</span><span class="p">,</span>
+ <span class="s">"--disable-extensions-except=/opt/extensions/ublock,/opt/extensions/isdncac"</span><span class="p">,</span>
+ <span class="s">"--load-extension=/opt/extensions/ublock,/opt/extensions/isdncac"</span><span class="p">,</span>
+ <span class="p">},</span>
+ <span class="n">DeviceScaleFactor</span><span class="o">:</span> <span class="n">playwright</span><span class="o">.</span><span class="n">Float</span><span class="p">(</span><span class="m">4.0</span><span class="p">),</span>
+ <span class="n">Headless</span><span class="o">:</span> <span class="n">playwright</span><span class="o">.</span><span class="n">Bool</span><span class="p">(</span><span class="no">false</span><span class="p">),</span>
+ <span class="n">Viewport</span><span class="o">:</span> <span class="o">&</span><span class="n">playwright</span><span class="o">.</span><span class="n">Size</span><span class="p">{</span>
+ <span class="n">Width</span><span class="o">:</span> <span class="m">1200</span><span class="p">,</span>
+ <span class="n">Height</span><span class="o">:</span> <span class="m">630</span><span class="p">,</span>
+ <span class="p">},</span>
+ <span class="n">UserAgent</span><span class="o">:</span> <span class="n">playwright</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="s">"Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Googlebot/2.1; +http://www.google.com/bot.html) Chrome/125.0.0.0 Safari/537.36"</span><span class="p">),</span>
+ <span class="p">})</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to launch chromium browser"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">browser</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="n">wf</span> <span class="o">:=</span> <span class="n">WorkerFunctions</span><span class="p">{</span>
+ <span class="n">db</span><span class="o">:</span> <span class="n">db</span><span class="p">,</span>
+ <span class="n">vision</span><span class="o">:</span> <span class="n">vision</span><span class="p">,</span>
+ <span class="n">mc</span><span class="o">:</span> <span class="n">mc</span><span class="p">,</span>
+ <span class="n">browser</span><span class="o">:</span> <span class="n">browser</span><span class="p">,</span>
+ <span class="p">}</span>
+
+ <span class="k">for</span> <span class="p">{</span>
+ <span class="n">start</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">Now</span><span class="p">()</span>
+
+ <span class="k">func</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">wg</span> <span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span>
+ <span class="n">rows</span> <span class="o">*</span><span class="n">sql</span><span class="o">.</span><span class="n">Rows</span>
+ <span class="n">err</span> <span class="kt">error</span>
+ <span class="p">)</span>
+
+ <span class="n">rows</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">Query</span><span class="p">(</span><span class="s">"SELECT id, url FROM data WHERE ready = 0 ORDER BY created_at LIMIT 6"</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"error executing query"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">rows</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="k">for</span> <span class="n">rows</span><span class="o">.</span><span class="n">Next</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">var</span> <span class="n">id</span> <span class="kt">int64</span>
+ <span class="k">var</span> <span class="n">url</span> <span class="kt">string</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">=</span> <span class="n">rows</span><span class="o">.</span><span class="n">Scan</span><span class="p">(</span><span class="o">&</span><span class="n">id</span><span class="p">,</span> <span class="o">&</span><span class="n">url</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"error scanning row"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">wg</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
+ <span class="k">go</span> <span class="n">wf</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="o">&</span><span class="n">wg</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">id</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">rows</span><span class="o">.</span><span class="n">Err</span><span class="p">();</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"error during rows iteration"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">wg</span><span class="o">.</span><span class="n">Wait</span><span class="p">()</span>
+ <span class="p">}()</span>
+
+ <span class="n">elapsed</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">Since</span><span class="p">(</span><span class="n">start</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">remaining</span> <span class="o">:=</span> <span class="m">5</span><span class="o">*</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span> <span class="o">-</span> <span class="n">elapsed</span><span class="p">;</span> <span class="n">remaining</span> <span class="o">></span> <span class="m">0</span> <span class="p">{</span>
+ <span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">remaining</span><span class="p">)</span>
+ <span class="p">}</span>
+ <span class="p">}</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="p">(</span><span class="n">wf</span> <span class="o">*</span><span class="n">WorkerFunctions</span><span class="p">)</span> <span class="n">run</span><span class="p">(</span><span class="n">wg</span> <span class="o">*</span><span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span><span class="p">,</span> <span class="n">url</span> <span class="kt">string</span><span class="p">,</span> <span class="n">id</span> <span class="kt">int64</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">defer</span> <span class="n">wg</span><span class="o">.</span><span class="n">Done</span><span class="p">()</span>
+
+ <span class="k">var</span> <span class="n">message</span> <span class="kt">string</span>
+
+ <span class="k">if</span> <span class="n">id</span> <span class="o"><</span> <span class="m">0</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="s">"invalid id: id must be non-negative"</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">ctx</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="n">Background</span><span class="p">()</span>
+ <span class="n">short</span> <span class="o">=</span> <span class="n">base36</span><span class="o">.</span><span class="n">Encode</span><span class="p">(</span><span class="kt">uint64</span><span class="p">(</span><span class="n">id</span><span class="p">))</span>
+ <span class="p">)</span>
+
+ <span class="n">dir</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">MkdirTemp</span><span class="p">(</span><span class="s">""</span><span class="p">,</span> <span class="s">"screenshot"</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to create temporary directory: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">os</span><span class="o">.</span><span class="n">RemoveAll</span><span class="p">(</span><span class="n">dir</span><span class="p">)</span>
+
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">fileName</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"%s.%s"</span><span class="p">,</span> <span class="n">short</span><span class="p">,</span> <span class="n">extension</span><span class="p">)</span>
+ <span class="n">filePath</span> <span class="o">=</span> <span class="n">filepath</span><span class="o">.</span><span class="n">Join</span><span class="p">(</span><span class="n">dir</span><span class="p">,</span> <span class="n">fileName</span><span class="p">)</span>
+ <span class="p">)</span>
+
+ <span class="n">page</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">wf</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">NewPage</span><span class="p">()</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to create new page: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">page</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">page</span><span class="o">.</span><span class="n">Goto</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">playwright</span><span class="o">.</span><span class="n">PageGotoOptions</span><span class="p">{</span>
+ <span class="n">WaitUntil</span><span class="o">:</span> <span class="n">playwright</span><span class="o">.</span><span class="n">WaitUntilStateDomcontentloaded</span><span class="p">,</span>
+ <span class="p">});</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to navigate to url: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span> <span class="o">*</span> <span class="m">5</span><span class="p">)</span>
+
+ <span class="n">title</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">page</span><span class="o">.</span><span class="n">Title</span><span class="p">()</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"could not get title: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">description</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">page</span><span class="o">.</span><span class="n">Locator</span><span class="p">(</span><span class="s">`meta[name="description"]`</span><span class="p">)</span><span class="o">.</span><span class="n">GetAttribute</span><span class="p">(</span><span class="s">"content"</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Info</span><span class="p">(</span><span class="s">"could not get meta description"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="n">description</span> <span class="o">=</span> <span class="s">""</span>
+ <span class="p">}</span>
+
+ <span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">page</span><span class="o">.</span><span class="n">Screenshot</span><span class="p">(</span><span class="n">playwright</span><span class="o">.</span><span class="n">PageScreenshotOptions</span><span class="p">{</span>
+ <span class="n">Path</span><span class="o">:</span> <span class="n">playwright</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="n">filePath</span><span class="p">),</span>
+ <span class="p">});</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to create screenshot: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">fp</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">Open</span><span class="p">(</span><span class="n">filePath</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to open screenshot: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">fp</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="n">image</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">google</span><span class="o">.</span><span class="n">NewImageFromReader</span><span class="p">(</span><span class="n">fp</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to load screenshot: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">annotations</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">wf</span><span class="o">.</span><span class="n">vision</span><span class="o">.</span><span class="n">DetectSafeSearch</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">image</span><span class="p">,</span> <span class="no">nil</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to detect labels: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="k">if</span> <span class="n">annotations</span><span class="o">.</span><span class="n">Adult</span> <span class="o">>=</span> <span class="n">visionpb</span><span class="o">.</span><span class="n">Likelihood_POSSIBLE</span> <span class="o">||</span> <span class="n">annotations</span><span class="o">.</span><span class="n">Violence</span> <span class="o">>=</span> <span class="n">visionpb</span><span class="o">.</span><span class="n">Likelihood_POSSIBLE</span> <span class="o">||</span> <span class="n">annotations</span><span class="o">.</span><span class="n">Racy</span> <span class="o">>=</span> <span class="n">visionpb</span><span class="o">.</span><span class="n">Likelihood_POSSIBLE</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"site is not safe %v"</span><span class="p">,</span> <span class="n">annotations</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">cmd</span> <span class="o">:=</span> <span class="n">exec</span><span class="o">.</span><span class="n">Command</span><span class="p">(</span><span class="s">"convert"</span><span class="p">,</span> <span class="n">filePath</span><span class="p">,</span> <span class="s">"-resize"</span><span class="p">,</span> <span class="s">"50%"</span><span class="p">,</span> <span class="s">"-filter"</span><span class="p">,</span> <span class="s">"Lanczos"</span><span class="p">,</span> <span class="s">"-quality"</span><span class="p">,</span> <span class="n">quality</span><span class="p">,</span> <span class="n">filePath</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">cmd</span><span class="o">.</span><span class="n">Run</span><span class="p">();</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"error during image conversion: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">wf</span><span class="o">.</span><span class="n">mc</span><span class="o">.</span><span class="n">FPutObject</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">bucket</span><span class="p">,</span> <span class="n">fileName</span><span class="p">,</span> <span class="n">filePath</span><span class="p">,</span> <span class="n">minio</span><span class="o">.</span><span class="n">PutObjectOptions</span><span class="p">{</span><span class="n">ContentType</span><span class="o">:</span> <span class="n">mimetype</span><span class="p">})</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to upload file to minio: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">Exec</span><span class="p">(</span><span class="s">"UPDATE data SET ready = 1, title = ?, description = ? WHERE url = ?"</span><span class="p">,</span> <span class="n">title</span><span class="p">,</span> <span class="n">description</span><span class="p">,</span> <span class="n">url</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"failed to update database record: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
+ <span class="n">setError</span><span class="p">(</span><span class="n">wf</span><span class="o">.</span><span class="n">db</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
+ <span class="k">return</span>
+ <span class="p">}</span>
+
+ <span class="k">go</span> <span class="n">warmup</span><span class="p">(</span><span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"%s/%s"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"DOMAIN"</span><span class="p">),</span> <span class="n">short</span><span class="p">))</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="n">setError</span><span class="p">(</span><span class="n">db</span> <span class="o">*</span><span class="n">sql</span><span class="o">.</span><span class="n">DB</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">db</span><span class="o">.</span><span class="n">Exec</span><span class="p">(</span><span class="s">"UPDATE data SET ready = 1, error = ? WHERE url = ?"</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">url</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"failed to update database error record"</span><span class="p">,</span> <span class="n">zap</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="p">))</span>
+ <span class="p">}</span>
+<span class="p">}</span>
+</code></pre></div></div>
+
+<p>It’s noticeable that it took a bit more effort than if I had used a task queue. However, it turned out to be quite robust and easy to debug, and for that reason alone, it was worth it.</p>
+
+<h3 id="conclusion">Conclusion</h3>
+
+<p>In sharing the shortened link (<a href="https://takealook.pro/4MP">https://takealook.pro/4MP</a>) for my company <a href="https://ultratech.software/">Ultratech Software</a> on social networks, you can have a really nice preview, as seen below.</p>
+
+<p><img src="https://nullonerror.org/public/2024-06-05-how-i-saved-a-few-dozen-dollars-by-migrating-my-personal-projects-from-the-cloud-to-a-raspberry-pi/takealook.pro.avif" alt="takealook.pro" class="center" /></p>
+
+<p><a href="https://takealook.pro/">Take A Look</a></p>
+ Wed, 05 Jun 2024 00:00:00 +0000
+ rodrigo@delduca.org (Rodrigo Delduca)
+
+
+ Rodrigo Delduca: My 2024 Setup
+ https://nullonerror.org/2024/03/20/my-2024-setup
+ https://nullonerror.org/2024/03/20/my-2024-setup/
+ <h3 id="software--productivity">Software & Productivity</h3>
+
+<ol>
+ <li><a href="https://brew.sh/">brew</a> as package manager</li>
+ <li><a href="https://asdf-vm.com/">asdf</a> as language & tools management</li>
+ <li><a href="https://zed.dev/">Zed</a> as code editor</li>
+ <li><a href="https://bear.app/">Bear</a> as notes keeping</li>
+ <li><a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> as browser</li>
+ <li><a href="https://kagi.com/">Kagi</a> as search engine</li>
+ <li><a href="https://chat.openai.com/">ChatGPT</a> as AI assistant</li>
+ <li><a href="https://www.usebruno.com/">Bruno</a> as API client</li>
+ <li><a href="https://orbstack.dev/">OrbStack</a> as Docker desktop replacement, plus virtual machines</li>
+ <li>Apple ecosystem for mail, calendar, reminders, music, messaging, and more.</li>
+</ol>
+
+<h3 id="degoogling">Degoogling</h3>
+
+<p>As can be seen, I am using Kagi, which delivers excellent results without any garbage or ads, instead of Google. I also use email with my own domain through Apple’s iCloud email system, which allows for custom domains, as well as calendar and other features, all outside Google’s domain. Unfortunately, I have to maintain my Google account to use YouTube and occasionally for sites that do not allow creating accounts with a username and password, but only through Single Sign-On (SSO).</p>
+ Wed, 20 Mar 2024 00:00:00 +0000
+ rodrigo@delduca.org (Rodrigo Delduca)
+
+
+ Rodrigo Delduca: Mastering Atomic Complex Operations in Redis: Unlocking High-Performance Data Handling With Lua
+ https://nullonerror.org/2024/03/06/mastering-atomic-complex-operations-in-redis-unlocking-high-performance-data-handling-with-lua
+ https://nullonerror.org/2024/03/06/mastering-atomic-complex-operations-in-redis-unlocking-high-performance-data-handling-with-lua/
+ <p>I have been using Redis for its most basic operations such as caching with expiration and counters, as well as slightly more complex operations like <a href="https://redis.io/docs/data-types/sorted-sets/">zsets</a>, <a href="https://redis.io/commands/zrangebyscore/">zrangebyscore</a>, and <a href="https://redis.io/commands/scan/">scan</a>, or as a <a href="https://redis.io/commands/rpoplpush/">queue</a> for probably more than 6 years on a daily basis.</p>
+
+<p>I won’t discuss Postgres today, as we can achieve similar results. Today, I will introduce something different: the possibility of scripting Redis with <a href="https://en.wikipedia.org/wiki/Lua_%28programming_language%29">Lua</a>.</p>
+
+<p>Months ago, I found myself facing the following problem: I needed to retrieve a JSON string from Redis using a specific key, merge it with the local JSON, and then submit it back to Redis as a string.</p>
+
+<p>This would have been a trivial task if not for the following scenario: On the “other” side, there was a process that consumed the same JSON and then, in an atomic pipeline operation, deleted it.</p>
+
+<p>Given this context, I could not perform the JSON merge on the client side, as it would not be atomic. This is where my native language, Lua (incidentally, I have a dog with the same name, and another dog named Python, yes, I’m a nerd, how did you guess?), comes into play.</p>
+
+<p>With Lua, operations are atomic and accelerated with JIT, making it possible to do a myriad of things, one very classic example being a rate limiter by IP address.</p>
+
+<p>Let’s see how my solution turned out:</p>
+
+<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">script</span> <span class="o">=</span> <span class="s2">`
+ local key = KEYS[1]
+ local input = ARGV[1]
+ local existing = redis.call('GET', key)
+
+ if existing then
+ local existingJson = cjson.decode(existing)
+ local inputJson = cjson.decode(input)
+ for _, v in ipairs(inputJson) do
+ table.insert(existingJson, v)
+ end
+ input = cjson.encode(existingJson)
+ end
+
+ redis.call('SET', key, input)
+ return input
+ `</span><span class="p">;</span>
+
+<span class="kd">const</span> <span class="nx">key</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">...</span><span class="dl">"</span><span class="p">;</span>
+
+<span class="k">await</span> <span class="nx">redis</span><span class="p">.</span><span class="nb">eval</span><span class="p">(</span><span class="nx">script</span><span class="p">,</span> <span class="p">{</span>
+ <span class="na">keys</span><span class="p">:</span> <span class="p">[</span><span class="nx">key</span><span class="p">],</span>
+ <span class="na">arguments</span><span class="p">:</span> <span class="p">[</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">schema</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">data</span><span class="p">))],</span>
+<span class="p">});</span>
+</code></pre></div></div>
+
+<p>And on the other side, the consumer can retrieve the JSON and delete it, atomically within a pipeline:</p>
+
+<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">pipeline</span> <span class="o">=</span> <span class="nx">redis</span><span class="p">.</span><span class="nx">multi</span><span class="p">();</span>
+<span class="nx">pipeline</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">...</span><span class="dl">"</span><span class="p">);</span>
+<span class="nx">pipeline</span><span class="p">.</span><span class="nx">del</span><span class="p">(</span><span class="dl">"</span><span class="s2">...</span><span class="dl">"</span><span class="p">);</span>
+<span class="kd">const</span> <span class="p">[</span><span class="nx">jsonStr</span><span class="p">]</span> <span class="o">=</span> <span class="nx">pipeline</span><span class="p">.</span><span class="nx">exec</span><span class="p">();</span>
+</code></pre></div></div>
+
+<p>In this way, all operations are atomic.</p>
+
+<p><img src="https://nullonerror.org/public/2024-03-06-mastering-atomic-complex-operations-in-redis-unlocking-high-performance-data-handling-with-lua/elmo.jpg" alt="Elmo reacting to the atomicity of combining Lua with Redis" class="center" /></p>
+ Wed, 06 Mar 2024 00:00:00 +0000
+ rodrigo@delduca.org (Rodrigo Delduca)
+
+
+ Rodrigo Delduca: 2023 was a productive year
+ https://nullonerror.org/2023/12/18/2023-was-a-productive-year
+ https://nullonerror.org/2023/12/18/2023-was-a-productive-year/
+ <p>The year started and I joined <a href="https://fueled.com/">Fueled</a>, an agency specializing in mobile apps and web systems, as a lead engineer.</p>
+
+<p>At Fueled, so far I’ve had the opportunity to work with three of their biggest clients to date. One of them is the world’s most famous lingerie brand, for which I developed a ‘social network’ that currently has about 5 million users. Then, I worked on a credit platform for elders in the United States that garnered 500,000 users at launch. Currently, I am working on a personal time management project for one of the top coaches in the field.</p>
+
+<p>In parallel, I wrote five articles for Fueled’s technical blog. They are:</p>
+
+<ul>
+ <li><a href="https://fueled.com/the-cache/posts/backend/working-with-compressed-binary-data-on-aws-iot-core/">Working With Compressed Binary Data on AWS Iot Core</a></li>
+ <li><a href="https://fueled.com/the-cache/posts/backend/combating-telegram-spam-with-a-serverless-bot/">Combating Telegram Spam With a Serverless Bot</a></li>
+ <li><a href="https://fueled.com/the-cache/posts/backend/clean-architecture-with-fastapi/">Clean Architecture with FastAPI</a></li>
+ <li><a href="https://fueled.com/the-cache/posts/backend/devops/cloud-workspace/">Secure Cloud Workspace with VSCode and Tailscale</a></li>
+ <li><a href="https://fueled.com/the-cache/posts/backend/golang/lint-all-the-things-with-golangci-lint/">Lint all the things with golangci-lint!</a></li>
+</ul>
+
+<p>I also wrote another five for my personal blog (this one). They are:</p>
+
+<ul>
+ <li><a href="https://nullonerror.org/2023/01/19/gotta-go-fast-with-docker-on-macos/">Gotta go fast with Docker on macOS</a></li>
+ <li><a href="https://nullonerror.org/2023/10/17/working-with-compressed-binary-data-on-aws-iot-core/">Working with compressed binary data on AWS IoT Core</a></li>
+ <li><a href="https://nullonerror.org/2023/11/01/what-i-ve-been-automating-with-github-actions-an-automated-life/">What I’ve been automating with GitHub Actions, an automated life</a></li>
+ <li><a href="https://nullonerror.org/2023/11/12/flipping-bits-whilst-updating-pixels/">Flipping Bits Whilst Updating Pixels</a></li>
+ <li><a href="https://nullonerror.org/2023/12/01/executing-untrusted-code-in-serverless-environments-a-telegram-bot-for-running-c-and-c++-code-on-cloud-run/">Executing Untrusted Code in Serverless Environments: A Telegram Bot for Running C and C++ Code on Cloud Run</a></li>
+</ul>
+
+<p>As if that wasn’t enough, I also developed and maintained personal projects, one of which, <a href="https://github.com/flippingpixels/carimbo">Carimbo</a>, was the one I devoted most of my time to. They are:</p>
+
+<ul>
+ <li><a href="https://github.com/flippingpixels/carimbo">Carimbo</a> - A 2D engine written in C++ using SDL and Lua as a scripting language.</li>
+ <li><a href="https://github.com/flippingpixels/play">Carimbo Play</a> - A web server to serve a combination of Carimbo releases and games.</li>
+ <li><a href="https://github.com/skhaz/grammateus">Grammateus</a> - An assistant that summarizes Twitch streams using OpenAI, written in Go.</li>
+ <li><a href="https://github.com/skhaz/neo-compiler-and-runner">Neo Compiler Bot</a> - A Telegram bot capable of compiling and executing unsafe code in the cloud.</li>
+ <li><a href="https://github.com/skhaz/freudian-slip">Freudian Slip Bot</a> - A Telegram bot designed to detect slip-ups between the lines.</li>
+</ul>
+
+<p>And many other small projects.</p>
+
+<p>As a result.</p>
+
+<p><img src="https://nullonerror.org/public/2023-12-18-2023-was-a-productive-year/github.png" alt="GitHub Stats" /></p>
+
+<p>Let’s see if 2024 will surpass it. Happy new year everyone!</p>
+ Mon, 18 Dec 2023 00:00:00 +0000
+ rodrigo@delduca.org (Rodrigo Delduca)
+
+
+ Rodrigo Delduca: Executing Untrusted Code in Serverless Environments: A Telegram Bot for Running C and C++ Code on Cloud Run
+ https://nullonerror.org/2023/12/01/executing-untrusted-code-in-serverless-environments-a-telegram-bot-for-running-c-and-c++-code-on-cloud-run
+ https://nullonerror.org/2023/12/01/executing-untrusted-code-in-serverless-environments-a-telegram-bot-for-running-c-and-c++-code-on-cloud-run/
+ <h2 id="intro">Intro</h2>
+
+<p>I enjoy experimenting and writing Telegram bots for programming groups I participate in. In two groups, people frequently ask about C or C++ code, seeking help, examples, and more. Instead of using online tools like <a href="https://godbolt.org/">Godbolt</a> (Compiler Explorer), they prefer sending their code directly in messages.</p>
+
+<p>I had previously created such a bot using a Flask webserver, which communicated with another container through <a href="https://www.jsonrpc.org/">JSON-RPC</a>. It worked well but occasionally had issues.</p>
+
+<p>With the rise of <a href="https://en.wikipedia.org/wiki/Large_language_model">LLM</a>, I switched to using OpenAI, but many users complained about the unconventional results, which was amusing.</p>
+
+<p>Recently, while working on a project named <a href="https://github.com/flippingpixels/carimbo">Carimbo</a>, I started exploring <a href="https://webassembly.org/">WebAssembly</a>. I realized it could be ideal for running untrusted code. Initially, I considered using <code class="language-plaintext highlighter-rouge">isolated-vm</code> with WebAssembly, but I was quite satisfied with <a href="https://wasmtime.dev/">Wasmtime</a>. It offered options to limit CPU time and RAM usage, among other features.</p>
+
+<h2 id="cgroups">Cgroups</h2>
+
+<p>Any experienced developer would likely suggest using <a href="https://en.wikipedia.org/wiki/Cgroups">cgroups</a> and <a href="https://en.wikipedia.org/wiki/Linux_namespaces">namespaces</a>, which are indeed superior options. However, I prefer not to incur the costs of VMs or keep a machine running 24/7 at my home. This is primarily because <a href="https://cloud.google.com/run">Cloud Run</a>, based on <a href="https://www.docker.com/">Docker</a>, already utilizes cgroups, and to my knowledge, nested cgroups aren’t possible.</p>
+
+<p>Cloud Run offers me several advantages. Without delving into too much detail, it’s a serverless platform built on top of <a href="https://kubernetes.io/">Kubernetes</a>, employing <a href="https://gvisor.dev/">gVisor</a> for an added security layer. You don’t need to handle Kubernetes directly, but the option for fine-tuning is available, which I will discuss in this article.</p>
+
+<h2 id="the-bot">The Bot</h2>
+
+<p>Unlike in my previous work <a href="https://nullonerror.org/2021/01/08/hosting-telegram-bots-on-google-cloud-run/">Hosting Telegram bots on Cloud Run for free</a>, this time I will not use <a href="https://flask.palletsprojects.com">Flask</a>, but instead, I will directly employ <a href="https://www.starlette.io/">Starlette</a>. Starlette is an asynchronous framework for Python. One of the main reasons for this migration is to utilize <a href="https://docs.python.org/3/library/asyncio.html">asyncio</a>, which will enable handling more requests. Additionally, the python-telegram-robot library has shifted to this asynchronous model, aligning with this change.</p>
+
+<p>Let’s start with the Dockerfile.</p>
+
+<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="w"> </span><span class="s">python:3.12-slim-bookworm</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">base</span>
+
+<span class="k">ENV</span><span class="s"> PIP_DISABLE_PIP_VERSION_CHECK 1</span>
+<span class="k">ENV</span><span class="s"> PYTHONUNBUFFERED 1</span>
+<span class="k">ENV</span><span class="s"> PYTHONDONTWRITEBYTECODE 1</span>
+<span class="k">ENV</span><span class="s"> EMSDK=/emsdk</span>
+<span class="k">ENV</span><span class="s"> PATH=/emsdk:/emsdk/upstream/emscripten:/opt/venv/bin:$PATH</span>
+
+<span class="k">FROM</span><span class="w"> </span><span class="s">base</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">builder</span>
+<span class="k">RUN </span>python <span class="nt">-m</span> venv /opt/venv
+<span class="k">COPY</span><span class="s"> requirements.txt .</span>
+<span class="k">RUN </span>pip <span class="nb">install</span> <span class="nt">--no-cache-dir</span> <span class="nt">--requirement</span> requirements.txt
+
+<span class="k">FROM</span><span class="s"> base</span>
+<span class="k">WORKDIR</span><span class="s"> /opt/app</span>
+
+<span class="c"># Let's steal this entire directory from the official Emscripten image.</span>
+<span class="k">COPY</span><span class="s"> --from=emscripten/emsdk:3.1.49 /emsdk /emsdk</span>
+<span class="k">COPY</span><span class="s"> --from=builder /opt/venv /opt/venv</span>
+<span class="k">COPY</span><span class="s"> . .</span>
+
+<span class="k">RUN </span>useradd <span class="nt">-r</span> user
+<span class="k">USER</span><span class="s"> user</span>
+
+<span class="c"># Instead of Gunicorn, we will use Uvicorn, which is an ASGI web server implementation for Python.</span>
+<span class="k">CMD</span><span class="s"> exec uvicorn main:app --host 0.0.0.0 --port $PORT --workers 8 --timeout-keep-alive 600 --timeout-graceful-shutdown 600</span>
+</code></pre></div></div>
+
+<p>The main differences are that we steal an entire directory from the Emscripten Docker image, which saves us from having to build in the image, which is excellent. We also use Uvicorn, an ASGI web server that allows direct use of asyncio.</p>
+
+<p>Now let’s see how it goes with handling the incoming requests.</p>
+
+<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">equals</span><span class="p">(</span><span class="n">left</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="bp">None</span><span class="p">,</span> <span class="n">right</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="bp">None</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
+ <span class="s">"""
+ Compare two strings using a consistent amount of time to avoid timing attacks.
+ """</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">left</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">right</span><span class="p">:</span>
+ <span class="k">return</span> <span class="bp">False</span>
+
+ <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">left</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">right</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">False</span>
+
+ <span class="k">for</span> <span class="n">c1</span><span class="p">,</span> <span class="n">c2</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">left</span><span class="p">,</span> <span class="n">right</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">c1</span> <span class="o">!=</span> <span class="n">c2</span><span class="p">:</span>
+ <span class="k">return</span> <span class="bp">False</span>
+
+ <span class="k">return</span> <span class="bp">True</span>
+
+
+<span class="k">async</span> <span class="k">def</span> <span class="nf">webhook</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">Request</span><span class="p">):</span>
+ <span class="s">"""
+ Entry point for requests coming from Telegram.
+ """</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">equals</span><span class="p">(</span>
+ <span class="n">request</span><span class="p">.</span><span class="n">headers</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"X-Telegram-Bot-Api-Secret-Token"</span><span class="p">),</span>
+ <span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">[</span><span class="s">"SECRET"</span><span class="p">],</span>
+ <span class="p">):</span>
+ <span class="c1"># This section prevents false calls, only this application and Telegram know the secret.
+</span> <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">401</span><span class="p">)</span>
+
+ <span class="n">payload</span> <span class="o">=</span> <span class="k">await</span> <span class="n">request</span><span class="p">.</span><span class="n">json</span><span class="p">()</span>
+
+ <span class="c1"># Where the bot becomes operational, the JSON is passed to the application, which in turn processes the request.
+</span> <span class="k">async</span> <span class="k">with</span> <span class="n">application</span><span class="p">:</span>
+ <span class="k">await</span> <span class="n">application</span><span class="p">.</span><span class="n">process_update</span><span class="p">(</span><span class="n">Update</span><span class="p">.</span><span class="n">de_json</span><span class="p">(</span><span class="n">payload</span><span class="p">,</span> <span class="n">application</span><span class="p">.</span><span class="n">bot</span><span class="p">))</span>
+
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">200</span><span class="p">)</span>
+
+
+<span class="n">app</span> <span class="o">=</span> <span class="n">Starlette</span><span class="p">(</span>
+ <span class="n">routes</span><span class="o">=</span><span class="p">[</span>
+ <span class="n">Route</span><span class="p">(</span><span class="s">"/"</span><span class="p">,</span> <span class="n">webhook</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">"POST"</span><span class="p">]),</span>
+ <span class="p">],</span>
+<span class="p">)</span>
+</code></pre></div></div>
+
+<p>Finally, we have the handler for messages that start with <code class="language-plaintext highlighter-rouge">/run</code>.</p>
+
+<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="k">def</span> <span class="nf">on_run</span><span class="p">(</span><span class="n">update</span><span class="p">:</span> <span class="n">Update</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="n">ContextTypes</span><span class="p">.</span><span class="n">DEFAULT_TYPE</span><span class="p">)</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">update</span><span class="p">.</span><span class="n">message</span><span class="p">.</span><span class="n">reply_to_message</span> <span class="ow">or</span> <span class="n">update</span><span class="p">.</span><span class="n">message</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">message</span><span class="p">:</span>
+ <span class="k">return</span>
+
+ <span class="n">text</span> <span class="o">=</span> <span class="n">message</span><span class="p">.</span><span class="n">text</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">text</span><span class="p">:</span>
+ <span class="k">return</span>
+
+ <span class="n">text</span> <span class="o">=</span> <span class="n">text</span><span class="p">.</span><span class="n">lstrip</span><span class="p">(</span><span class="s">"/run"</span><span class="p">)</span>
+
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">text</span><span class="p">:</span>
+ <span class="k">await</span> <span class="n">message</span><span class="p">.</span><span class="n">reply_text</span><span class="p">(</span><span class="s">"Luke, I need the code for the Death Star's system."</span><span class="p">)</span>
+ <span class="k">return</span>
+
+ <span class="k">try</span><span class="p">:</span>
+ <span class="c1"># All the code is asynchronous, while the 'run' function is not. Therefore, we execute it in a thread.
+</span> <span class="n">coro</span> <span class="o">=</span> <span class="n">asyncio</span><span class="p">.</span><span class="n">to_thread</span><span class="p">(</span><span class="n">run</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span>
+
+ <span class="c1"># We execute the thread as a coroutine and limit its execution to 30 seconds.
+</span> <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">asyncio</span><span class="p">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">coro</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span>
+
+ <span class="c1"># Below, we prevent flooding in groups by placing very long messages into a bucket and returning the public URL.
+</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">></span> <span class="mi">64</span><span class="p">:</span>
+ <span class="n">blob</span> <span class="o">=</span> <span class="n">bucket</span><span class="p">.</span><span class="n">blob</span><span class="p">(</span><span class="n">hashlib</span><span class="p">.</span><span class="n">sha256</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">text</span><span class="p">).</span><span class="n">encode</span><span class="p">()).</span><span class="n">hexdigest</span><span class="p">())</span>
+ <span class="n">blob</span><span class="p">.</span><span class="n">upload_from_string</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
+ <span class="n">blob</span><span class="p">.</span><span class="n">make_public</span><span class="p">()</span>
+
+ <span class="n">result</span> <span class="o">=</span> <span class="n">blob</span><span class="p">.</span><span class="n">public_url</span>
+
+ <span class="c1"># Respond to the message with the result, which can be either an error or a success.
+</span> <span class="k">await</span> <span class="n">message</span><span class="p">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">asyncio</span><span class="p">.</span><span class="nb">TimeoutError</span><span class="p">:</span>
+ <span class="c1"># If the code exceeds the time limit or takes too long to compile, we return some emojis.
+</span> <span class="k">await</span> <span class="n">message</span><span class="p">.</span><span class="n">reply_text</span><span class="p">(</span><span class="s">"⏰😮💨"</span><span class="p">)</span>
+</code></pre></div></div>
+
+<h2 id="running-untrusted-code">Running Untrusted Code</h2>
+
+<p>Each request to execute code is compiled using em++, an ‘alias’ for clang++, targeting WebAssembly, and then executed with the <a href="https://wasi.dev/">WASI</a> runtime. Each execution runs separately and in a thread-safe manner in its own directory. While I could limit CPU usage (fuel) and memory usage, as indicated by the commented lines, in my case I opted for a container with 4GB of RAM and 4 vCPUs, which is more than sufficient given that I configured Run to accept only 8 connections per instance.</p>
+
+<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="n">source</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
+ <span class="k">with</span> <span class="n">TemporaryDirectory</span><span class="p">()</span> <span class="k">as</span> <span class="n">path</span><span class="p">:</span>
+ <span class="n">os</span><span class="p">.</span><span class="n">chdir</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
+
+ <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"main.cpp"</span><span class="p">,</span> <span class="s">"w+t"</span><span class="p">)</span> <span class="k">as</span> <span class="n">main</span><span class="p">:</span>
+ <span class="n">main</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">source</span><span class="p">)</span>
+ <span class="n">main</span><span class="p">.</span><span class="n">flush</span><span class="p">()</span>
+
+ <span class="k">try</span><span class="p">:</span>
+ <span class="c1"># Compile it.
+</span> <span class="n">result</span> <span class="o">=</span> <span class="n">subprocess</span><span class="p">.</span><span class="n">run</span><span class="p">(</span>
+ <span class="p">[</span>
+ <span class="s">"em++"</span><span class="p">,</span>
+ <span class="s">"-s"</span><span class="p">,</span>
+ <span class="s">"ENVIRONMENT=node"</span><span class="p">,</span>
+ <span class="s">"-s"</span><span class="p">,</span>
+ <span class="s">"WASM=1"</span><span class="p">,</span>
+ <span class="s">"-s"</span><span class="p">,</span>
+ <span class="s">"PURE_WASI=1"</span><span class="p">,</span>
+ <span class="s">"main.cpp"</span><span class="p">,</span>
+ <span class="p">],</span>
+ <span class="n">capture_output</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
+ <span class="n">text</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
+ <span class="n">check</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
+ <span class="p">)</span>
+
+ <span class="k">if</span> <span class="n">result</span><span class="p">.</span><span class="n">returncode</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">result</span><span class="p">.</span><span class="n">stderr</span>
+
+ <span class="c1"># Run it.
+</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"a.out.wasm"</span><span class="p">,</span> <span class="s">"rb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">binary</span><span class="p">:</span>
+ <span class="n">wasi</span> <span class="o">=</span> <span class="n">WasiConfig</span><span class="p">()</span>
+ <span class="c1"># Store the output in a file.
+</span> <span class="n">wasi</span><span class="p">.</span><span class="n">stdout_file</span> <span class="o">=</span> <span class="s">"a.out.stdout"</span>
+ <span class="c1"># Store the errors in a file.
+</span> <span class="n">wasi</span><span class="p">.</span><span class="n">stderr_file</span> <span class="o">=</span> <span class="s">"a.out.stderr"</span>
+
+ <span class="n">config</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
+ <span class="c1"># config.consume_fuel = True
+</span> <span class="n">engine</span> <span class="o">=</span> <span class="n">Engine</span><span class="p">(</span><span class="n">config</span><span class="p">)</span>
+ <span class="n">store</span> <span class="o">=</span> <span class="n">Store</span><span class="p">(</span><span class="n">engine</span><span class="p">)</span>
+ <span class="n">store</span><span class="p">.</span><span class="n">set_wasi</span><span class="p">(</span><span class="n">wasi</span><span class="p">)</span>
+ <span class="c1"># Limits the RAM.
+</span> <span class="c1"># store.set_limits(16 * 1024 * 1024)
+</span> <span class="c1"># Limits the CPU.
+</span> <span class="c1"># store.set_fuel(10_000_000_000)
+</span>
+ <span class="n">linker</span> <span class="o">=</span> <span class="n">Linker</span><span class="p">(</span><span class="n">engine</span><span class="p">)</span>
+ <span class="n">linker</span><span class="p">.</span><span class="n">define_wasi</span><span class="p">()</span>
+ <span class="n">module</span> <span class="o">=</span> <span class="n">Module</span><span class="p">(</span><span class="n">store</span><span class="p">.</span><span class="n">engine</span><span class="p">,</span> <span class="n">binary</span><span class="p">.</span><span class="n">read</span><span class="p">())</span>
+ <span class="n">instance</span> <span class="o">=</span> <span class="n">linker</span><span class="p">.</span><span class="n">instantiate</span><span class="p">(</span><span class="n">store</span><span class="p">,</span> <span class="n">module</span><span class="p">)</span>
+
+ <span class="c1"># `_start` is the binary entrypoint, also known as main.
+</span> <span class="n">start</span> <span class="o">=</span> <span class="n">instance</span><span class="p">.</span><span class="n">exports</span><span class="p">(</span><span class="n">store</span><span class="p">)[</span><span class="s">"_start"</span><span class="p">]</span>
+ <span class="k">assert</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">Func</span><span class="p">)</span>
+
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">start</span><span class="p">(</span><span class="n">store</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">ExitTrap</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
+ <span class="c1"># If exit code is not 0, we return the errors.
+</span> <span class="k">if</span> <span class="n">e</span><span class="p">.</span><span class="n">code</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
+ <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"a.out.stderr"</span><span class="p">,</span> <span class="s">"rt"</span><span class="p">)</span> <span class="k">as</span> <span class="n">stderr</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">stderr</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
+
+ <span class="c1"># If no errors, we return the output.
+</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"a.out.stdout"</span><span class="p">,</span> <span class="s">"rt"</span><span class="p">)</span> <span class="k">as</span> <span class="n">stdout</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">stdout</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
+ <span class="k">except</span> <span class="n">subprocess</span><span class="p">.</span><span class="n">CalledProcessError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">e</span><span class="p">.</span><span class="n">stderr</span>
+ <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span> <span class="c1"># noqa
+</span> <span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
+</code></pre></div></div>
+
+<h2 id="deploy">Deploy</h2>
+
+<p>In the past, I always used Google’s tools for deployment, but this time I tried building the Docker image in GitHub Action, which gave me two huge advantages.</p>
+
+<ol>
+ <li>Cache: I don’t know why, but I never got the cache to work in Cloud Build. With GitHub, it’s just a matter of using a flag.</li>
+ <li>Modern Docker syntax usage: In Cloud Build, it’s not possible to use <a href="https://www.docker.com/blog/introduction-to-heredocs-in-dockerfiles/">heredoc</a>, for example.</li>
+ <li>Speed: I know it’s possible to upgrade the Cloud Build machine, but that costs money, and on GitHub, I have a quite generous free quota.</li>
+</ol>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Deploy on Google Cloud Platform</span>
+
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">push</span><span class="pi">:</span>
+ <span class="na">branches</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">main</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">deploy</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Authenticate to Google Cloud</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">google-github-actions/auth@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">credentials_json</span><span class="pi">:</span> <span class="s">$</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Google Cloud SDK</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">google-github-actions/setup-gcloud@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">project_id</span><span class="pi">:</span> <span class="s">$</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Docker Buildx</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">docker/setup-buildx-action@v3</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Authenticate Docker</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">gcloud auth configure-docker --quiet $-docker.pkg.dev</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build And Push Telegram Service</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">docker/build-push-action@v5</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">context</span><span class="pi">:</span> <span class="s">.</span>
+ <span class="na">push</span><span class="pi">:</span> <span class="no">true</span>
+ <span class="na">tags</span><span class="pi">:</span> <span class="s">$/$:$</span>
+ <span class="na">cache-from</span><span class="pi">:</span> <span class="s">type=gha</span>
+ <span class="na">cache-to</span><span class="pi">:</span> <span class="s">type=gha,mode=max</span>
+
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy Telegram Service to Cloud Run</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">TELEGRAM_SERVICE_NAME</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">REGION</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">REGISTRY</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">GITHUB_SHA</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">TELEGRAM_TOKEN</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">SECRET</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">BUCKET</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">cat <<EOF | envsubst > service.yaml</span>
+ <span class="s">apiVersion: serving.knative.dev/v1</span>
+ <span class="s">kind: Service</span>
+ <span class="s">metadata:</span>
+ <span class="s">name: "$TELEGRAM_SERVICE_NAME"</span>
+ <span class="s">labels:</span>
+ <span class="s">cloud.googleapis.com/location: "$REGION"</span>
+ <span class="s">spec:</span>
+ <span class="s">template:</span>
+ <span class="s">metadata:</span>
+ <span class="s">annotations:</span>
+ <span class="s">run.googleapis.com/execution-environment: "gen2"</span>
+ <span class="s">run.googleapis.com/startup-cpu-boost: "true"</span>
+ <span class="s">run.googleapis.com/cpu-throttling: "true"</span>
+ <span class="s">autoscaling.knative.dev/maxScale: "16"</span>
+ <span class="s">spec:</span>
+ <span class="s">containerConcurrency: "1"</span>
+ <span class="s">timeoutSeconds: "60"</span>
+ <span class="s">containers:</span>
+ <span class="s">- image: "$REGISTRY/$TELEGRAM_SERVICE_NAME:$GITHUB_SHA"</span>
+ <span class="s">name: "$TELEGRAM_SERVICE_NAME"</span>
+ <span class="s">resources:</span>
+ <span class="s">limits:</span>
+ <span class="s">cpu: "4000m"</span>
+ <span class="s">memory: "4Gi"</span>
+ <span class="s">env:</span>
+ <span class="s">- name: TELEGRAM_TOKEN</span>
+ <span class="s">value: "$TELEGRAM_TOKEN"</span>
+ <span class="s">- name: SECRET</span>
+ <span class="s">value: "$SECRET"</span>
+ <span class="s">- name: BUCKET</span>
+ <span class="s">value: "$BUCKET"</span>
+ <span class="s">EOF</span>
+
+ <span class="s">gcloud run services replace service.yaml</span>
+ <span class="s">rm -f service.yaml</span>
+</code></pre></div></div>
+
+<h2 id="conclusion">Conclusion</h2>
+
+<p>Try here: <a href="https://t.me/neo_compiler_bot">https://t.me/neo_compiler_bot</a> or <code class="language-plaintext highlighter-rouge">@neo_compiler_bot</code> on Telegram.</p>
+
+<p>Source code: <a href="https://github.com/skhaz/neo-compiler-and-runner">https://github.com/skhaz/neo-compiler-and-runner</a>.</p>
+
+<p><img src="https://nullonerror.org/public/2023-12-01-executing-untrusted-code-in-serverless-environments-a-telegram-bot-for-running-c-and-c++-code-on-cloud-run/telegram.png" alt="Telegram" class="center" /></p>
+ Fri, 01 Dec 2023 00:00:00 +0000
+ rodrigo@delduca.org (Rodrigo Delduca)
+
+
+ Rodrigo Delduca: Flipping Bits Whilst Updating Pixels
+ https://nullonerror.org/2023/11/12/flipping-bits-whilst-updating-pixels
+ https://nullonerror.org/2023/11/12/flipping-bits-whilst-updating-pixels/
+ <blockquote>
+ <p>I have less than 16 milliseconds to do more than thousands of operations.</p>
+</blockquote>
+
+<p>Roughly 15 years ago, during an extended summer holiday, in a shared room of a student residence, I found myself endeavoring to port <a href="https://github.com/skhaz/wintermoon">my 2D game</a> engine built on top of <a href="https://en.wikipedia.org/wiki/Simple_DirectMedia_Layer">SDL</a> to the <a href="https://en.wikipedia.org/wiki/Google_Native_Client">Google Native Client (NaCl)</a>. NaCl served as a sandboxing mechanism for Chrome, enabling the execution of native code within the browser, specifically within the Chrome browser. It’s safe to assert that NaCl can be considered the progenitor of <a href="https://webassembly.org">WebAssembly</a>.</p>
+
+<p>A considerable amount of time has elapsed, and many changes have transpired. I transitioned from game development to web development, yet low-level programming has always coursed through my veins. Consequently, I resolved to revive the dream of crafting my own game engine running on the web. Today, with the advent of WebAssembly, achieving this goal is significantly more feasible and portable.</p>
+
+<p>Therefore, I created Carimbo 🇧🇷, meaning “stamp” in English. The name encapsulates the notion that 2D engines are continually stamping sprites onto the screen.</p>
+
+
+ <img src="https://nullonerror.org/public/2023-11-12-flipping-bits-whilst-updating-pixels/carimbo.webp" alt="Carimbo" class="center" />
+ Artwork by <a href="https://www.fiverr.com/yuugenpixie">@yuugenpixie</a>
+
+
+<p>This engine shares the foundational principles of <em>Wintermoon</em>; it is built upon the SDL library, employs Lua for scripting, and consolidates all assets within a compressed LZMA file, which is <em>mounted</em> when the engine initializes.</p>
+
+<p>In essence, it operates as follows: video, audio, joystick, network, and file system components are initialized. Then, the <code class="language-plaintext highlighter-rouge">bundle.zip</code> file is mounted. When I say ‘mounted,’ I employ <a href="https://icculus.org/physfs/">a library</a> that makes read and write operations within the compressed file entirely transparent, eliminating the need for decompression, which is excellent. Subsequently, the ‘main.lua’ file is executed. This file should utilize the factory to construct the engine, which is the cornerstone. Following this, the script must spawn entities and other objects to be used within the game. Finally, with the game defined, the script should invoke the ‘run’ method of the engine, which will block and initiate the event loop.</p>
+
+<p>However, this time around, the tooling for C++ has significantly improved compared to that era. Numerous package managers now exist, and compilers, along with the standard library, have matured considerably. Notably, there’s no longer a necessity to employ Boost for advanced features; many functionalities, including smart pointers and others formerly associated with Boost, are now integral parts of the standard library.</p>
+
+<p>Speaking of package managers, in Carimbo, I opted for <a href="https://conan.io/">Conan</a>, which, in my opinion, is an excellent package manager.</p>
+
+<p>It was during this exploration that I discovered Conan’s support for various toolings, including <a href="https://github.com/emscripten-core/emsdk">emsdk</a>—the Software Development Kit (SDK) and compiler for the Emscripten project. Emscripten is an LLVM/Clang-based compiler designed to translate C and C++ source code into WebAssembly.</p>
+
+<p>With the <code class="language-plaintext highlighter-rouge">emsdk</code>, I could finally fulfill my long-held aspiration.</p>
+
+<p><img src="https://nullonerror.org/public/2023-11-12-flipping-bits-whilst-updating-pixels/blank.jpeg" alt="" class="center" /></p>
+
+<p>Yes, a black screen and an overwhelming sense of victory. I had successfully ported my code to run in the browser. Now, all that remained was to figure out how to load the assets (at that time). However, the event loop was running just a tad below 60 frames per second due to a bug in the counter.</p>
+
+<p>And you might be wondering, ‘How do you debug this?’ Firstly, for various reasons that can burden the final binary, a lot is stripped away. Therefore, we need to recompile everything and all dependencies with sanitizers and debugging symbols. Secondly, WebAssembly should be linked with <strong>-sASSERTIONS=2</strong>, <strong>-sRUNTIME_DEBUG</strong>, and <strong>–profiling</strong>. This way, it’s possible to see the stack trace in the browser console as if by magic. Additionally, <a href="https://developer.chrome.com/blog/wasm-debugging-2020/">Chrome has a debugger</a> that allows you to insert breakpoints within your source code and inspect step by step.</p>
+
+<p>By the way, a binary and all its dependencies compiled with all sanitizers and debugging symbols can easily surpass 300 megabytes! So, I recommend compiling with -Os or -O1.</p>
+
+<p><a href="https://github.com/carimbolabs/carimbo">The source code for the Carimbo</a></p>
+
+<p>Check out the engine running below; this is just a concept without audio and joystick support.</p>
+ Sun, 12 Nov 2023 00:00:00 +0000
+ rodrigo@delduca.org (Rodrigo Delduca)
+
+
+ Rodrigo Delduca: What I’ve been automating with GitHub Actions, an automated life
+ https://nullonerror.org/2023/11/01/what-i-ve-been-automating-with-github-actions-an-automated-life
+ https://nullonerror.org/2023/11/01/what-i-ve-been-automating-with-github-actions-an-automated-life/
+ <h1 id="automate-all-the-things">Automate all the things!</h1>
+
+<p>Programmers, like no other beings on the planet, are completely obsessed with automating things, from the simplest to the most complex, and I’m no different.</p>
+
+<p>I have automated several things using GitHub Actions, and today I will show some of the things I’ve done.</p>
+
+<h2 id="readmemd">README.md</h2>
+
+<p>In my GitHub README, I periodically fetch the RSS feed from my blog (the one you are currently reading) and populate it with the latest articles, like this:</p>
+
+<p>Ah, you can use any source of RSS, like your YouTube channel!</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Latest blog post workflow</span>
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">schedule</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0</span><span class="nv"> </span><span class="s">*/6</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*"</span>
+ <span class="na">workflow_dispatch</span><span class="pi">:</span> <span class="c1"># Run workflow manually</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">update-readme-with-blog</span><span class="pi">:</span>
+ <span class="na">name</span><span class="pi">:</span> <span class="s">Update this repo's README with latest blog posts</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v2</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Pull NULL on Error posts</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">gautamkrishnar/blog-post-workflow@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">comment_tag_name</span><span class="pi">:</span> <span class="s">BLOG</span>
+ <span class="na">commit_message</span><span class="pi">:</span> <span class="s">Update with the latest blog posts</span>
+ <span class="na">committer_username</span><span class="pi">:</span> <span class="s">Rodrigo Delduca</span>
+ <span class="na">committer_email</span><span class="pi">:</span> <span class="s">46259+skhaz@users.noreply.github.com</span>
+ <span class="na">max_post_count</span><span class="pi">:</span> <span class="m">6</span>
+ <span class="na">feed_list</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://nullonerror.org/feed"</span>
+</code></pre></div></div>
+
+<h2 id="resume">Resume</h2>
+
+<p>My resume is public. I have an action in the repository that compiles and uploads it to a Google Cloud bucket and sets the object as public, like this:</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">on</span><span class="pi">:</span> <span class="s">push</span>
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">build</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">container</span><span class="pi">:</span> <span class="s">texlive/texlive</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">xelatex resume.tex</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Authenticate to Google Cloud</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">google-github-actions/auth@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">credentials_json</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Upload to Google Cloud Storage</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">google-github-actions/upload-cloud-storage@v1</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">path</span><span class="pi">:</span> <span class="s">resume.pdf</span>
+ <span class="na">destination</span><span class="pi">:</span> <span class="s">gcs.skhaz.dev</span>
+ <span class="na">predefinedAcl</span><span class="pi">:</span> <span class="s">publicRead</span>
+</code></pre></div></div>
+
+<p>You can check it out at <a href="https://gcs.skhaz.dev/resume.pdf">https://gcs.skhaz.dev/resume.pdf</a>.</p>
+
+<h2 id="github-stars">GitHub Stars</h2>
+
+<p>I believe everyone enjoys starring repositories, but GitHub’s interface doesn’t help much when it comes to finding or organizing them.</p>
+
+<p>To do this, I use <a href="https://github.com/maguowei/starred">starred</a>, which generates a README file categorizing by language. You can check it out at the following address: <a href="https://github.com/skhaz/stars">https://github.com/skhaz/stars</a></p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Update Stars</span>
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">workflow_dispatch</span><span class="pi">:</span>
+ <span class="na">schedule</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s">0 0 * * *</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">stars</span><span class="pi">:</span>
+ <span class="na">name</span><span class="pi">:</span> <span class="s">Update stars</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v3</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Python</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-python@v3</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">python-version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3.10"</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install dependencies</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">python -m pip install --upgrade pip</span>
+ <span class="s">pip install starred</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Get repository name</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">echo "REPOSITORY_NAME=${GITHUB_REPOSITORY#*/}" >> $GITHUB_ENV</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Update repository category by language</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">GITHUB_TOKEN</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">REPOSITORY</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">USERNAME</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="s">starred --username ${USERNAME} --repository ${REPOSITORY} --sort --token ${GITHUB_TOKEN} --message 'awesome-stars category by language update by github actions cron, created by starred'</span>
+</code></pre></div></div>
+
+<h2 id="healthchecks">Healthchecks</h2>
+
+<p>I imagine that, just like me, you also have several websites. In my case, all of them are simple and do not generate any income. However, I want to make sure that everything is in order, so I use https://healthchecks.io in conjunction with GitHub Actions. Healthchecks.io operates passively; it does not make requests to your site. On the contrary, you must make a request to it, and then it marks it as healthy. If there are no pings for a certain amount of time (configurable), it will notify you through various means that the site or application is not functioning as it should. Think of it as a Kubernetes probe.</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Health Check</span>
+
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">workflow_dispatch</span><span class="pi">:</span>
+ <span class="na">schedule</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*"</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">health</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Check health</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">STATUSCODE=$(curl -s -o /dev/null --write-out "%{http_code}" "${SITE_URL}")</span>
+
+ <span class="s">if test $STATUSCODE -ne 200; then</span>
+ <span class="s">exit 1</span>
+ <span class="s">fi</span>
+
+ <span class="s">curl -fsS -m 10 --retry 5 -o /dev/null "https://hc-ping.com/${HEALTH_UUID}"</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">HEALTH_UUID</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">SITE_URL</span><span class="pi">:</span> <span class="s">$</span>
+</code></pre></div></div>
+
+<h2 id="salary">Salary</h2>
+
+<p>This one is a bit more complex, as it goes beyond just an action. I have a repository called ‘salary’, where there’s an action that runs every hour. What this action does is essentially run a Go code, and the result updates the README file. This way, I can simply access the URL and get an estimate of how much I’ll receive.</p>
+
+<p>In the program, I have two goroutines running in parallel. In one of them, I query the number of hours on Toggl, multiply it by my rate, and return it through a channel. The other does the same, but it uses an API to convert dollars to Brazilian reais, and in the end, the values are summed up.</p>
+
+<p><code class="language-plaintext highlighter-rouge">main.go</code></p>
+
+<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>
+
+<span class="k">import</span> <span class="p">(</span>
+ <span class="s">"bytes"</span>
+ <span class="s">"encoding/json"</span>
+ <span class="s">"fmt"</span>
+ <span class="s">"io"</span>
+ <span class="s">"log"</span>
+ <span class="s">"math"</span>
+ <span class="s">"net/http"</span>
+ <span class="s">"os"</span>
+ <span class="s">"strconv"</span>
+ <span class="s">"sync"</span>
+ <span class="s">"time"</span>
+<span class="p">)</span>
+
+<span class="k">type</span> <span class="n">DateRange</span> <span class="k">struct</span> <span class="p">{</span>
+ <span class="n">StartDate</span> <span class="kt">string</span> <span class="s">`json:"start_date"`</span>
+ <span class="n">EndDate</span> <span class="kt">string</span> <span class="s">`json:"end_date"`</span>
+<span class="p">}</span>
+
+<span class="k">type</span> <span class="n">Summary</span> <span class="k">struct</span> <span class="p">{</span>
+ <span class="n">TrackedSecond</span> <span class="kt">int</span> <span class="s">`json:"tracked_seconds"`</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="n">toggl</span><span class="p">(</span><span class="n">result</span> <span class="k">chan</span><span class="o"><-</span> <span class="kt">int</span><span class="p">,</span> <span class="n">wg</span> <span class="o">*</span><span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">defer</span> <span class="n">wg</span><span class="o">.</span><span class="n">Done</span><span class="p">()</span>
+
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">now</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">Now</span><span class="p">()</span>
+ <span class="n">firstDay</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">Date</span><span class="p">(</span><span class="n">now</span><span class="o">.</span><span class="n">Year</span><span class="p">(),</span> <span class="n">now</span><span class="o">.</span><span class="n">Month</span><span class="p">(),</span> <span class="m">1</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">time</span><span class="o">.</span><span class="n">UTC</span><span class="p">)</span>
+ <span class="n">lastDay</span> <span class="o">=</span> <span class="n">firstDay</span><span class="o">.</span><span class="n">AddDate</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="o">-</span><span class="m">1</span><span class="p">)</span>
+
+ <span class="n">url</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"https://api.track.toggl.com/reports/api/v3/workspace/%s/projects/summary"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"TOGGL_WORKSPACE_ID"</span><span class="p">))</span>
+ <span class="n">dataRange</span> <span class="o">=</span> <span class="n">DateRange</span><span class="p">{</span>
+ <span class="n">StartDate</span><span class="o">:</span> <span class="n">firstDay</span><span class="o">.</span><span class="n">Format</span><span class="p">(</span><span class="s">"2006-01-02"</span><span class="p">),</span>
+ <span class="n">EndDate</span><span class="o">:</span> <span class="n">lastDay</span><span class="o">.</span><span class="n">Format</span><span class="p">(</span><span class="s">"2006-01-02"</span><span class="p">),</span>
+ <span class="p">}</span>
+ <span class="p">)</span>
+
+ <span class="n">payload</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">json</span><span class="o">.</span><span class="n">Marshal</span><span class="p">(</span><span class="n">dataRange</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">req</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">http</span><span class="o">.</span><span class="n">NewRequest</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">MethodPost</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">bytes</span><span class="o">.</span><span class="n">NewBuffer</span><span class="p">(</span><span class="n">payload</span><span class="p">))</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">req</span><span class="o">.</span><span class="n">Header</span><span class="o">.</span><span class="n">Set</span><span class="p">(</span><span class="s">"Content-Type"</span><span class="p">,</span> <span class="s">"application/json"</span><span class="p">)</span>
+ <span class="n">req</span><span class="o">.</span><span class="n">SetBasicAuth</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"TOGGL_EMAIL"</span><span class="p">),</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"TOGGL_PASSWORD"</span><span class="p">))</span>
+
+ <span class="n">client</span> <span class="o">:=</span> <span class="o">&</span><span class="n">http</span><span class="o">.</span><span class="n">Client</span><span class="p">{}</span>
+ <span class="n">resp</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">client</span><span class="o">.</span><span class="n">Do</span><span class="p">(</span><span class="n">req</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">resp</span><span class="o">.</span><span class="n">Body</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="n">body</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">io</span><span class="o">.</span><span class="n">ReadAll</span><span class="p">(</span><span class="n">resp</span><span class="o">.</span><span class="n">Body</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="k">var</span> <span class="n">summaries</span> <span class="p">[]</span><span class="n">Summary</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">Unmarshal</span><span class="p">(</span><span class="n">body</span><span class="p">,</span> <span class="o">&</span><span class="n">summaries</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">total</span> <span class="o">:=</span> <span class="m">0</span>
+ <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">summary</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">summaries</span> <span class="p">{</span>
+ <span class="n">total</span> <span class="o">+=</span> <span class="n">summary</span><span class="o">.</span><span class="n">TrackedSecond</span>
+ <span class="p">}</span>
+
+ <span class="n">hourlyRate</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">strconv</span><span class="o">.</span><span class="n">Atoi</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"TOGGL_HOURLY_RATE"</span><span class="p">))</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">result</span> <span class="o"><-</span> <span class="p">(</span><span class="n">total</span> <span class="o">/</span> <span class="m">3600</span><span class="p">)</span> <span class="o">*</span> <span class="n">hourlyRate</span>
+<span class="p">}</span>
+
+<span class="k">type</span> <span class="n">CurrencyData</span> <span class="k">struct</span> <span class="p">{</span>
+ <span class="n">Quotes</span> <span class="k">struct</span> <span class="p">{</span>
+ <span class="n">USDBRL</span> <span class="kt">float64</span> <span class="s">`json:"USDBRL"`</span>
+ <span class="p">}</span> <span class="s">`json:"quotes"`</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="n">husky</span><span class="p">(</span><span class="n">result</span> <span class="k">chan</span><span class="o"><-</span> <span class="kt">int</span><span class="p">,</span> <span class="n">wg</span> <span class="o">*</span><span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span><span class="p">)</span> <span class="p">{</span>
+ <span class="k">defer</span> <span class="n">wg</span><span class="o">.</span><span class="n">Done</span><span class="p">()</span>
+
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">currency</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"HUSKY_CURRENCY"</span><span class="p">)</span>
+ <span class="n">url</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"https://api.apilayer.com/currency_data/live?base=USD&symbols=%s&currencies=%s"</span><span class="p">,</span> <span class="n">currency</span><span class="p">,</span> <span class="n">currency</span><span class="p">)</span>
+ <span class="p">)</span>
+
+ <span class="n">req</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">http</span><span class="o">.</span><span class="n">NewRequest</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">MethodGet</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="no">nil</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">req</span><span class="o">.</span><span class="n">Header</span><span class="o">.</span><span class="n">Set</span><span class="p">(</span><span class="s">"apikey"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"APILAYER_APIKEY"</span><span class="p">))</span>
+
+ <span class="n">client</span> <span class="o">:=</span> <span class="o">&</span><span class="n">http</span><span class="o">.</span><span class="n">Client</span><span class="p">{}</span>
+ <span class="n">resp</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">client</span><span class="o">.</span><span class="n">Do</span><span class="p">(</span><span class="n">req</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+ <span class="k">defer</span> <span class="n">resp</span><span class="o">.</span><span class="n">Body</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
+
+ <span class="n">body</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">io</span><span class="o">.</span><span class="n">ReadAll</span><span class="p">(</span><span class="n">resp</span><span class="o">.</span><span class="n">Body</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="k">var</span> <span class="n">data</span> <span class="n">CurrencyData</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">Unmarshal</span><span class="p">(</span><span class="n">body</span><span class="p">,</span> <span class="o">&</span><span class="n">data</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">monthlySalary</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">strconv</span><span class="o">.</span><span class="n">ParseFloat</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"HUSKY_MONTHLY_SALARY"</span><span class="p">),</span> <span class="m">64</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
+ <span class="n">log</span><span class="o">.</span><span class="n">Fatalln</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="n">gross</span> <span class="o">:=</span> <span class="kt">int</span><span class="p">(</span><span class="n">math</span><span class="o">.</span><span class="n">Floor</span><span class="p">(</span><span class="n">monthlySalary</span> <span class="o">*</span> <span class="n">data</span><span class="o">.</span><span class="n">Quotes</span><span class="o">.</span><span class="n">USDBRL</span><span class="p">))</span>
+ <span class="n">deduction</span> <span class="o">:=</span> <span class="n">gross</span> <span class="o">*</span> <span class="m">1</span> <span class="o">/</span> <span class="m">100</span>
+ <span class="n">result</span> <span class="o"><-</span> <span class="n">gross</span> <span class="o">-</span> <span class="n">deduction</span>
+<span class="p">}</span>
+
+<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">var</span> <span class="p">(</span>
+ <span class="n">ch</span> <span class="o">=</span> <span class="nb">make</span><span class="p">(</span><span class="k">chan</span> <span class="kt">int</span><span class="p">)</span>
+ <span class="n">wg</span> <span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span>
+ <span class="n">funcs</span> <span class="o">=</span> <span class="p">[]</span><span class="k">func</span><span class="p">(</span><span class="k">chan</span><span class="o"><-</span> <span class="kt">int</span><span class="p">,</span> <span class="o">*</span><span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span><span class="p">){</span><span class="n">toggl</span><span class="p">,</span> <span class="n">husky</span><span class="p">}</span>
+ <span class="p">)</span>
+
+ <span class="n">wg</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">funcs</span><span class="p">))</span>
+
+ <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">fun</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">funcs</span> <span class="p">{</span>
+ <span class="k">go</span> <span class="n">fun</span><span class="p">(</span><span class="n">ch</span><span class="p">,</span> <span class="o">&</span><span class="n">wg</span><span class="p">)</span>
+ <span class="p">}</span>
+
+ <span class="k">go</span> <span class="k">func</span><span class="p">()</span> <span class="p">{</span>
+ <span class="n">wg</span><span class="o">.</span><span class="n">Wait</span><span class="p">()</span>
+ <span class="nb">close</span><span class="p">(</span><span class="n">ch</span><span class="p">)</span>
+ <span class="p">}()</span>
+
+ <span class="k">var</span> <span class="n">sum</span> <span class="kt">int</span>
+ <span class="k">for</span> <span class="n">result</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">ch</span> <span class="p">{</span>
+ <span class="n">sum</span> <span class="o">+=</span> <span class="n">result</span>
+ <span class="p">}</span>
+
+ <span class="n">fmt</span><span class="o">.</span><span class="n">Print</span><span class="p">(</span><span class="n">sum</span><span class="p">)</span>
+<span class="p">}</span>
+</code></pre></div></div>
+
+<p>So in the action, all you have to do is run and update the README periodically.</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Run</span>
+
+<span class="na">on</span><span class="pi">:</span>
+ <span class="na">workflow_dispatch</span><span class="pi">:</span>
+ <span class="na">schedule</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*"</span>
+
+<span class="na">permissions</span><span class="pi">:</span>
+ <span class="na">contents</span><span class="pi">:</span> <span class="s">write</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">run</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup Go</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-go@v4</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Update Markdown</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">salary=$(make run)</span>
+
+ <span class="s">cat > README.md <<EOF</span>
+ <span class="s">### Salary</span>
+
+ <span class="s">EOF</span>
+
+ <span class="s">printf "%s\n" "$salary" >> README.md</span>
+ <span class="na">env</span><span class="pi">:</span>
+ <span class="na">APILAYER_APIKEY</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">HUSKY_MONTHLY_SALARY</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">HUSKY_CURRENCY</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">TOGGL_WORKSPACE_ID</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">TOGGL_EMAIL</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">TOGGL_PASSWORD</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="na">TOGGL_HOURLY_RATE</span><span class="pi">:</span> <span class="s">$</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Commit report</span>
+ <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
+ <span class="s">git config --global user.name 'github-actions[bot]'</span>
+ <span class="s">git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com'</span>
+ <span class="s">git add README.md</span>
+ <span class="s">git commit -am "Automated Salary Update" || true</span>
+ <span class="s">git push</span>
+</code></pre></div></div>
+
+<h2 id="blog">Blog</h2>
+
+<p>The next step is to automate the publishing of new blog posts using ChatGPT ;-).</p>
+ Wed, 01 Nov 2023 00:00:00 +0000
+ rodrigo@delduca.org (Rodrigo Delduca)
+
+
+ Rodrigo Delduca: Working with compressed binary data on AWS IoT Core
+ https://nullonerror.org/2023/10/17/working-with-compressed-binary-data-on-aws-iot-core
+ https://nullonerror.org/2023/10/17/working-with-compressed-binary-data-on-aws-iot-core/
+ <h3 id="objective">Objective</h3>
+
+<p>Today we will see how to send compressed CBOR with ZSTD from an ESP32 microcontroller through an MQTT topic, passing through AWS IoT Core, and finally being decoded in a TypeScript-written Lambda function.</p>
+
+<h3 id="intro">Intro</h3>
+
+<p>Firstly, what is a microcontroller (MCU)? In this article, we will be using the ESP32 microcontroller from Espressif. It is a highly affordable and inexpensive microcontroller that comes with built-in WiFi and Bluetooth, making it ideal for IoT projects as we will see today. It also boasts a generous amount of flash memory and RAM, as well as a powerful dual-core 32-bit CPU.</p>
+
+<p>Another tool we will be using is PlatformIO, a development framework that functions as a series of plugins and command-line tools for VSCode. With PlatformIO, we have everything we need, from project setup, board selection, serial port speed, compilation flags (yes, we’ll be compiling code in C and C++), and more. Installing it is quite simple; just visit https://platformio.org/ and follow the instructions.</p>
+
+<p>Lastly, to streamline our project and infrastructure, we will be using the Serverless Framework, a framework designed for developing serverless solutions. In our case, we will be using a Lambda function to receive messages sent to the topic. The Serverless Framework is a perfect fit for this scenario and works seamlessly with AWS Amazon.</p>
+
+<h3 id="compressed-binary-data">Compressed Binary Data</h3>
+
+<p>We could certainly use JSON. In fact, with JSON, it is possible to perform queries using the WHERE clause (Yes, IoT Core supports SQL, and we will delve into that later). However, our objective here is to save as many bytes as possible. Imagine that our application will send and receive data via a costly and unreliable telephone connection. Therefore, we need to compress the data as much as possible.</p>
+
+<p>Firstly, let’s construct the raw payload in CBOR. CBOR is an interchangeable binary format specified in an RFC and supported by various programming languages (it is derived from MsgPack, in case you’ve heard of it before).</p>
+
+<p>Since this part will be done on the microcontroller side, we will be using the C++ language with the <code class="language-plaintext highlighter-rouge">ssilverman/libCBOR</code> library. To do this, we open the <code class="language-plaintext highlighter-rouge">platformio.ini</code> file and add the dependency under <code class="language-plaintext highlighter-rouge">lib_deps</code>.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf"><CBOR.h></span><span class="cp">
+#include</span> <span class="cpf"><CBOR_parsing.h></span><span class="cp">
+#include</span> <span class="cpf"><CBOR_streams.h></span><span class="cp">
+</span>
+<span class="k">namespace</span> <span class="n">cbor</span> <span class="o">=</span> <span class="o">::</span><span class="n">qindesign</span><span class="o">::</span><span class="n">cbor</span><span class="p">;</span>
+</code></pre></div></div>
+
+<p>Since we are developing for a microcontroller, memory allocation is a critical concern as it can lead to fragmentation and other issues. Therefore, we will define a buffer of 256 bytes for reading and writing CBOR messages, which is more than sufficient for our current application.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">constexpr</span> <span class="kt">size_t</span> <span class="n">kBytesSize</span> <span class="o">=</span> <span class="mi">256</span><span class="p">;</span>
+<span class="kt">uint8_t</span> <span class="n">bytes</span><span class="p">[</span><span class="n">kBytesSize</span><span class="p">]{</span><span class="mi">0</span><span class="p">};</span>
+<span class="n">cbor</span><span class="o">::</span><span class="n">BytesStream</span> <span class="n">bs</span><span class="p">{</span><span class="n">bytes</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">bytes</span><span class="p">)};</span>
+<span class="n">cbor</span><span class="o">::</span><span class="n">BytesPrint</span> <span class="n">bp</span><span class="p">{</span><span class="n">bytes</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">bytes</span><span class="p">)};</span>
+</code></pre></div></div>
+
+<p>Next, let’s prepare to use ZSTD. The steps are the same as with CBOR.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf"><zstd.h></span><span class="cp">
+</span>
+<span class="k">constexpr</span> <span class="kt">size_t</span> <span class="n">kZBytesSize</span> <span class="o">=</span> <span class="mi">256</span><span class="p">;</span>
+<span class="kt">uint8_t</span> <span class="n">zbytes</span><span class="p">[</span><span class="n">kZBytesSize</span><span class="p">]{</span><span class="mi">0</span><span class="p">};</span>
+</code></pre></div></div>
+
+<p>Finally, and not least importantly, let’s prepare to send messages to a topic on IoT Core. The setup is a bit complex and requires attention. Firstly, we need to add the <code class="language-plaintext highlighter-rouge">knolleary/PubSubClient</code> library.</p>
+
+<p>We will need three things from AWS IoT Core: the Certificate Authority (CA) certificate, the Thing certificate, and the private key certificate of the Thing. We will see how to create rules in IoT Core to allow things to subscribe and publish only to specific topics for security reasons. One more thing, in our project, we will hardcode these values in the flash memory of the ESP32 using the <em>PROGMEM</em> directive.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf"><WiFiClientSecure.h></span><span class="cp">
+#include</span> <span class="cpf"><PubSubClient.h></span><span class="cp">
+</span>
+<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">AWS_IOT_ENDPOINT</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"....amazonaws.com"</span><span class="p">;</span>
+<span class="k">static</span> <span class="k">const</span> <span class="kt">uint16_t</span> <span class="n">AWS_IOT_PORT</span> <span class="o">=</span> <span class="mi">8883</span><span class="p">;</span>
+<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">THINGNAME</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"My ESP32"</span><span class="p">;</span>
+
+<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">AWS_CERT_CA</span><span class="p">[]</span> <span class="n">PROGMEM</span> <span class="o">=</span> <span class="s">R"EOF(
+-----BEGIN CERTIFICATE-----
+... AWS Amazon Certificate Authority (CA)
+-----END CERTIFICATE-----
+)EOF"</span><span class="p">;</span>
+
+<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">AWS_CERT_CRT</span><span class="p">[]</span> <span class="n">PROGMEM</span> <span class="o">=</span> <span class="s">R"KEY(
+-----BEGIN CERTIFICATE-----
+... Thing's certificate
+-----END CERTIFICATE-----
+)KEY"</span><span class="p">;</span>
+
+<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">AWS_CERT_PRIVATE</span><span class="p">[]</span> <span class="n">PROGMEM</span> <span class="o">=</span> <span class="s">R"KEY(
+-----BEGIN RSA PRIVATE KEY-----
+... Thing's private certificate
+-----END RSA PRIVATE KEY-----
+)KEY"</span><span class="p">;</span>
+</code></pre></div></div>
+
+<p>We need an instance of PubSub to publish and subscribe to topics, as well as a WiFi client to configure the aforementioned keys.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">WiFiClientSecure</span> <span class="n">net</span><span class="p">;</span>
+<span class="n">PubSubClient</span> <span class="nf">pubsub</span><span class="p">(</span><span class="n">AWS_IOT_ENDPOINT</span><span class="p">,</span> <span class="n">AWS_IOT_PORT</span><span class="p">,</span> <span class="n">net</span><span class="p">);</span>
+</code></pre></div></div>
+
+<p>Now let’s proceed with a typical Arduino program structure, with the setup and loop functions. In the setup function, we will connect to the WiFi network, and then we will configure the keys in the WiFiClientSecure object so that the PubSubClient can use them to connect to IoT Core. Without these keys, the connection will be rejected by the AWS Amazon servers.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span>
+<span class="p">{</span>
+ <span class="c1">// For debugging.</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span>
+
+ <span class="c1">// Connect to WiFi.</span>
+ <span class="n">WiFi</span><span class="p">.</span><span class="n">mode</span><span class="p">(</span><span class="n">WIFI_STA</span><span class="p">);</span>
+ <span class="n">WiFi</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="n">WIFI_SSID</span><span class="p">,</span> <span class="n">WIFI_PASSWORD</span><span class="p">);</span>
+
+ <span class="c1">// Wait for the connection.</span>
+ <span class="k">while</span> <span class="p">(</span><span class="n">WiFi</span><span class="p">.</span><span class="n">status</span><span class="p">()</span> <span class="o">!=</span> <span class="n">WL_CONNECTED</span><span class="p">)</span>
+ <span class="p">{</span>
+ <span class="n">delay</span><span class="p">(</span><span class="mi">500</span><span class="p">);</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">"."</span><span class="p">);</span>
+ <span class="p">}</span>
+
+ <span class="c1">// Show WiFi information.</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">();</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">"Connected to "</span><span class="p">);</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="n">WIFI_SSID</span><span class="p">);</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s">"IP address: "</span><span class="p">);</span>
+ <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="n">WiFi</span><span class="p">.</span><span class="n">localIP</span><span class="p">());</span>
+
+ <span class="c1">// Setup the certificates.</span>
+ <span class="n">net</span><span class="p">.</span><span class="n">setCACert</span><span class="p">(</span><span class="n">AWS_CERT_CA</span><span class="p">);</span>
+ <span class="n">net</span><span class="p">.</span><span class="n">setCertificate</span><span class="p">(</span><span class="n">AWS_CERT_CRT</span><span class="p">);</span>
+ <span class="n">net</span><span class="p">.</span><span class="n">setPrivateKey</span><span class="p">(</span><span class="n">AWS_CERT_PRIVATE</span><span class="p">);</span>
+
+ <span class="c1">// Connect to IoT Core.</span>
+ <span class="n">pubsub</span><span class="p">.</span><span class="n">connect</span><span class="p">(</span><span class="n">THINGNAME</span><span class="p">);</span>
+<span class="p">}</span>
+</code></pre></div></div>
+
+<p>If everything goes well, the <code class="language-plaintext highlighter-rouge">connect</code> method of PubSubClient will return <em>true</em>. We won’t perform that check; instead, we’ll attempt to publish directly and monitor the results on the IoT Core dashboard.</p>
+
+<p>Now comes the interesting part. We will assemble our payload in CBOR format, compress it using ZSTD, and publish it to a topic.</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">on_sensor</span><span class="p">(</span><span class="k">const</span> <span class="kt">uint64_t</span> <span class="o">*</span><span class="n">sensor_data</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">sensor_data_size</span><span class="p">)</span> <span class="p">{</span>
+ <span class="c1">// Create a new instance of the CBOR writer.</span>
+ <span class="n">cbor</span><span class="o">::</span><span class="n">Writer</span> <span class="n">cbor</span><span class="p">{</span><span class="n">bp</span><span class="p">};</span>
+
+ <span class="c1">// Reset BytesPrint instance.</span>
+ <span class="n">bp</span><span class="p">.</span><span class="n">reset</span><span class="p">();</span>
+
+ <span class="c1">// Indicates that what follows is an array of a certain size.</span>
+ <span class="n">cbor</span><span class="p">.</span><span class="n">beginArray</span><span class="p">(</span><span class="n">sensor_data_size</span><span class="p">);</span>
+ <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">sensor_data_size</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
+ <span class="p">{</span>
+ <span class="c1">// For each item in the array, write it to CBOR.</span>
+ <span class="n">cbor</span><span class="p">.</span><span class="n">writeUnsignedInt</span><span class="p">(</span><span class="n">sensor_data</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
+ <span class="p">}</span>
+
+ <span class="c1">// Get the final CBOR size.</span>
+ <span class="k">const</span> <span class="kt">size_t</span> <span class="n">lenght</span> <span class="o">=</span> <span class="n">cbor</span><span class="p">.</span><span class="n">getWriteSize</span><span class="p">();</span>
+
+ <span class="c1">// Compress the CBOR buffer using ZSTD.</span>
+ <span class="kt">size_t</span> <span class="n">compressedSize</span> <span class="o">=</span> <span class="n">ZSTD_compress</span><span class="p">(</span><span class="n">zbytes</span><span class="p">,</span> <span class="n">kZBytesSize</span><span class="p">,</span> <span class="n">bytes</span><span class="p">,</span> <span class="n">lenght</span><span class="p">,</span> <span class="n">ZSTD_CLEVEL_DEFAULT</span><span class="p">);</span>
+
+ <span class="c1">// Publish the binary compressed data onto the topic.</span>
+ <span class="kt">char</span> <span class="n">topic</span><span class="p">[</span><span class="mi">128</span><span class="p">];</span>
+ <span class="n">sprintf</span><span class="p">(</span><span class="n">topic</span><span class="p">,</span> <span class="s">"sensors/%s/v1"</span><span class="p">,</span> <span class="n">THINGNAME</span><span class="p">);</span>
+ <span class="n">pubsub</span><span class="p">.</span><span class="n">publish</span><span class="p">(</span><span class="n">topic</span><span class="p">,</span> <span class="n">zbytes</span><span class="p">,</span> <span class="n">compressedSize</span><span class="p">,</span> <span class="nb">false</span><span class="p">);</span>
+<span class="p">}</span>
+</code></pre></div></div>
+
+<h3 id="on-the-cloud-side">On the cloud side</h3>
+
+<p>As I mentioned before, we will be using the Serverless Framework, which follows the Infrastructure as Code (IaC) approach. So what we’ll do is create the rules for the Things, create a lambda function, and define the trigger for that lambda as IoT Core. Since the data is in binary format, we will encode it in Base64 in the IoT Core SQL.</p>
+
+<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">service</span><span class="pi">:</span> <span class="s">myiot</span>
+
+<span class="na">configValidationMode</span><span class="pi">:</span> <span class="s">error</span>
+
+<span class="na">frameworkVersion</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3"</span>
+
+<span class="na">provider</span><span class="pi">:</span>
+ <span class="na">name</span><span class="pi">:</span> <span class="s">aws</span>
+ <span class="na">runtime</span><span class="pi">:</span> <span class="s">nodejs18.x</span>
+ <span class="na">architecture</span><span class="pi">:</span> <span class="s">arm64</span>
+ <span class="na">stage</span><span class="pi">:</span> <span class="s">development</span>
+
+<span class="na">resources</span><span class="pi">:</span>
+ <span class="na">Resources</span><span class="pi">:</span>
+ <span class="na">IoTPolicy</span><span class="pi">:</span>
+ <span class="na">Type</span><span class="pi">:</span> <span class="s">AWS::IoT::Policy</span>
+ <span class="na">Properties</span><span class="pi">:</span>
+ <span class="na">PolicyName</span><span class="pi">:</span> <span class="s">IoTPolicy</span>
+ <span class="na">PolicyDocument</span><span class="pi">:</span>
+ <span class="na">Version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2012-10-17"</span>
+ <span class="na">Statement</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">Effect</span><span class="pi">:</span> <span class="s">Allow</span>
+ <span class="na">Action</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">iot:Connect</span>
+ <span class="na">Resource</span><span class="pi">:</span> <span class="s">arn:aws:iot:*:*:client/\${iot:Connection.Thing.ThingName}</span>
+ <span class="pi">-</span> <span class="na">Effect</span><span class="pi">:</span> <span class="s">Allow</span>
+ <span class="na">Action</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">iot:Publish</span>
+ <span class="pi">-</span> <span class="s">iot:Receive</span>
+ <span class="na">Resource</span><span class="pi">:</span> <span class="s">arn:aws:iot:*:*:topic/*/\${iot:Connection.Thing.ThingName}/*</span>
+ <span class="pi">-</span> <span class="na">Effect</span><span class="pi">:</span> <span class="s">Allow</span>
+ <span class="na">Action</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">iot:Subscribe</span>
+ <span class="na">Resource</span><span class="pi">:</span> <span class="s">arn:aws:iot:*:*:topicfilter/*/\${iot:Connection.Thing.ThingName}/*</span>
+
+<span class="na">functions</span><span class="pi">:</span>
+ <span class="na">tracker</span><span class="pi">:</span>
+ <span class="na">handler</span><span class="pi">:</span> <span class="s">app/mylambda.handler</span>
+ <span class="na">events</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">iot</span><span class="pi">:</span>
+ <span class="na">sql</span><span class="pi">:</span> <span class="s2">"</span><span class="s">SELECT</span><span class="nv"> </span><span class="s">timestamp()</span><span class="nv"> </span><span class="s">AS</span><span class="nv"> </span><span class="s">timestamp,</span><span class="nv"> </span><span class="s">topic()</span><span class="nv"> </span><span class="s">AS</span><span class="nv"> </span><span class="s">topic,</span><span class="nv"> </span><span class="s">encode(*,</span><span class="nv"> </span><span class="s">'base64')</span><span class="nv"> </span><span class="s">AS</span><span class="nv"> </span><span class="s">data</span><span class="nv"> </span><span class="s">FROM</span><span class="nv"> </span><span class="s">'sensors/+/v1'"</span>
+ <span class="na">sqlVersion</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2016-03-23"</span>
+
+<span class="na">plugins</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">serverless-plugin-typescript</span>
+</code></pre></div></div>
+
+<p>The YAML file for serverless may seem a bit intimidating at first, but it’s simple. It essentially does two things. Firstly, it creates an IoTPolicy for the things. A Thing can only publish or subscribe to its own topic with IoTPolicies, allowing for a granular level of security.</p>
+
+<p>Also, it’s worth noting that we are using ARM64 architecture. Lambdas running on the ARM architecture are not only more cost-effective but also more efficient compared to x86_64.</p>
+
+<p>The second part is the definition of the lambda function and its trigger. It uses a query in IoT Core, which is the key to working with binary data in IoT Core. You need to encode the data in <strong>base64</strong> before sending it to the lambda function; otherwise, it won’t work. This lambda function is “listening” to the sensors topic from any Thing, hence the plus symbol in the topic. By default, I prefer to version APIs, and a topic shouldn’t be an exception. Therefore, this is version 1 of my project.</p>
+
+<p>Now let’s take a look at the lambda function itself. For this project, I chose to use TypeScript, but AWS Lambda and the Serverless Framework support various programming languages.</p>
+
+<p>The code is quite straightforward. It receives the binary payload in the <code class="language-plaintext highlighter-rouge">data</code> parameter (as defined in the SQL statement above). First, it decodes the payload from base64 to binary. Then, it decompresses it using ZSTD and, finally, utilizes the CBOR library to parse it into a JavaScript object, ready to be used.</p>
+
+<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">decompressSync</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@skhaz/zstd</span><span class="dl">"</span><span class="p">;</span>
+<span class="k">import</span> <span class="p">{</span> <span class="nx">decodeFirstSync</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">cbor</span><span class="dl">"</span><span class="p">;</span>
+
+<span class="k">export</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">handler</span><span class="p">(</span><span class="nx">event</span><span class="p">:</span> <span class="p">{</span> <span class="nl">timestamp</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span> <span class="nl">topic</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="nl">data</span><span class="p">:</span> <span class="kr">string</span> <span class="p">})</span> <span class="p">{</span>
+ <span class="c1">// Extract from event some variables.</span>
+ <span class="kd">const</span> <span class="p">{</span> <span class="nx">timestamp</span><span class="p">,</span> <span class="nx">topic</span><span class="p">,</span> <span class="nx">data</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">event</span><span class="p">;</span>
+
+ <span class="c1">// Decode from base64 to binary.</span>
+ <span class="kd">const</span> <span class="nx">buffer</span> <span class="o">=</span> <span class="nx">Buffer</span><span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="dl">"</span><span class="s2">base64</span><span class="dl">"</span><span class="p">);</span>
+ <span class="c1">// Decompress using ZSTD algorithm.</span>
+ <span class="kd">const</span> <span class="nx">cbor</span> <span class="o">=</span> <span class="nx">decompressSync</span><span class="p">(</span><span class="nx">buffer</span><span class="p">);</span>
+ <span class="c1">// Parse the binary CBOR to JavaScript object.</span>
+ <span class="kd">const</span> <span class="nx">payload</span> <span class="o">=</span> <span class="nx">decodeFirstSync</span><span class="p">(</span><span class="nx">cbor</span><span class="p">);</span>
+
+ <span class="c1">// Print the sensor array data.</span>
+ <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">payload</span><span class="p">);</span>
+<span class="p">}</span>
+</code></pre></div></div>
+
+<h3 id="conclusion">Conclusion</h3>
+
+<p>In conclusion, it is possible to save a few bytes or even more with these techniques while utilizing cloud solutions like IoT Core in your projects. It allows for efficient data handling and enables the integration of cloud services into your applications.</p>
+
+<p>The compression ratio can vary dramatically, depending solely on the input. In my experiments with numerical data, it ranged from around 30% to 40%.</p>
+ Tue, 17 Oct 2023 00:00:00 +0000
+ rodrigo@delduca.org (Rodrigo Delduca)
+
+
+ Thiago Avelino: Gentlemen’s Club vs Strip Club: Is There Really a Difference?
+ http://feeds.feedburner.com/5e0a2504-6b2a-4c40-9a68-30b5d8d58eb0
+ https://podcasters.spotify.com/pod/show/strip-clubs-las-vegas/episodes/Gentlemens-Club-vs-Strip-Club-Is-There-Really-a-Difference-e27337q
+ <p>Confused about the distinctions between Strip Clubs and Gentlemen&#39;s Clubs in Las Vegas? In this episode, we&#39;re going to dissect what sets these venues apart and what you can anticipate from the very best of both. Be sure to listen in as we demystify the Las Vegas club scene.</p>
+<p><br></p>
+<p><a href="https://www.stripclubconcierge.com/" target="_blank" rel="noopener noreferer">https://www.stripclubconcierge.com/</a></p>
+<p><br></p>
+<p><a href="https://www.stripclubconcierge.com/gentlemens-club-vs-strip-club/" target="_blank" rel="noopener noreferer">https://www.stripclubconcierge.com/gentlemens-club-vs-strip-club/</a></p>
+<p><br></p>
+<p>Tags: Gentlemens Clubs Las Vegas, Best Gentlemens Clubs Las Vegas, Treasures Gentlemen’s Club, Crazy Horse III Gentlemen’s Club</p>
+ Tue, 15 Aug 2023 13:21:00 +0000
+
+
+ Thiago Avelino: Las Vegas Strippers 101: Your 2023 Guide to "Strippers to the Room" (Private Bachelor Party Bookings)
+ http://feeds.feedburner.com/90e07b6c-bdce-437a-98b9-f8df1b0fca61
+ https://podcasters.spotify.com/pod/show/strip-clubs-las-vegas/episodes/Las-Vegas-Strippers-101-Your-2023-Guide-to-Strippers-to-the-Room-Private-Bachelor-Party-Bookings-e273351
+ <p>Are you mulling over hiring Las Vegas strippers for a bachelor party? In this episode, we help you weigh up the pros and cons between opting for private in-room entertainers and taking the gang to a strip club. Make sure you tune in to get all the information you need for planning the ultimate bachelor party.</p>
+<p><br></p>
+<p><a href="https://www.stripclubconcierge.com/" target="_blank" rel="noopener noreferer">https://www.stripclubconcierge.com/</a></p>
+<p><br></p>
+<p><a href="https://www.stripclubconcierge.com/las-vegas-strippers/" target="_blank" rel="noopener noreferer">https://www.stripclubconcierge.com/las-vegas-strippers/</a></p>
+<p><br></p>
+<p>Tags: Las Vegas Bachelor Party, Las Vegas Guides, Las Vegas Strippers, Outcall Strippers, Private Strippers</p>
+ Sat, 12 Aug 2023 13:18:00 +0000
+
+
+ Ellison Leão: Gerando documentação vimdoc de plugins Neovim com Github Actions
+ https://dev.to/ellisonleao/gerando-documentacao-vimdoc-de-plugins-neovim-com-github-actions-22if
+ https://dev.to/ellisonleao/gerando-documentacao-vimdoc-de-plugins-neovim-com-github-actions-22if
+ <p><em>Dificuldade: intermediário</em></p>
+
+<p>Escrever documentação nem sempre é uma das tarefas mais prazerosas, mas ter uma boa documentação nos nossos projetos é o que realmente faz diferença. Ainda mais quando usamos ferramentas que nos auxiliam nessa escrita.</p>
+
+<p>Nesse post vamos ensinar como automatizar a geração da documentação específica para plugins <strong>Neovim</strong> utilizando <strong>Github Actions</strong></p>
+
+<h2>
+
+
+ Entendendo o fluxo
+</h2>
+
+<p>Assumindo que entendemos o fluxo básico das <strong>Github Actions</strong>, temos o seguinte fluxo para a geração da doc:</p>
+
+<ol>
+<li>Fazemos um push na branch principal (geralmente chamada de <code>main</code>)</li>
+<li>Um job chamado <code>docs</code> é iniciado com os seguintes passos:
+
+<ul>
+<li>Conversão do <code>README.md</code> do seu projeto em um arquivo .txt no formato <a href="https://vimdoc.sourceforge.net/htmldoc/helphelp.html#help-writing" rel="noopener noreferrer">vimdoc</a>
+</li>
+<li>Criamos um commit e fazemos o push na mesma branch com o resultado da conversão</li>
+</ul>
+</li>
+</ol>
+
+<p>Convertendo isso para o <strong>Github Actions</strong>, temos:</p>
+
+<ol>
+<li>Crie um arquivo <code>.github/workflows/docs.yml</code> com o seguinte conteúdo:
+</li>
+</ol>
+
+<div class="highlight js-code-highlight">
+<pre class="highlight yaml"><code><span class="na">on</span><span class="pi">:</span>
+ <span class="na">push</span><span class="pi">:</span>
+ <span class="na">branches</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="s">main</span> <span class="c1"># aqui nossa branch principal se chama `main`</span>
+
+<span class="na">jobs</span><span class="pi">:</span>
+ <span class="na">docs</span><span class="pi">:</span>
+ <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
+ <span class="na">needs</span><span class="pi">:</span> <span class="s">test</span>
+ <span class="na">steps</span><span class="pi">:</span>
+ <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v3</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">panvimdoc</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">kdheepak/panvimdoc@main</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">vimdoc</span><span class="pi">:</span> <span class="s">nome-do-seu-projeto</span> <span class="c1"># sem extensão .txt</span>
+ <span class="na">version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Neovim</span><span class="nv"> </span><span class="s">>=</span><span class="nv"> </span><span class="s">0.8.0"</span>
+ <span class="na">demojify</span><span class="pi">:</span> <span class="kc">true</span> <span class="c1"># coloque false caso queira manter os emojis</span>
+ <span class="na">treesitter</span><span class="pi">:</span> <span class="kc">true</span>
+ <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Push changes</span>
+ <span class="na">uses</span><span class="pi">:</span> <span class="s">stefanzweifel/git-auto-commit-action@v4</span>
+ <span class="na">with</span><span class="pi">:</span>
+ <span class="na">commit_message</span><span class="pi">:</span> <span class="s2">"</span><span class="s">auto-generate</span><span class="nv"> </span><span class="s">vimdoc"</span>
+ <span class="na">commit_user_name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">github-actions[bot]"</span>
+ <span class="na">commit_user_email</span><span class="pi">:</span> <span class="s2">"</span><span class="s">github-actions[bot]@users.noreply.github.com"</span>
+ <span class="na">commit_author</span><span class="pi">:</span> <span class="s2">"</span><span class="s">github-actions[bot]</span><span class="nv"> </span><span class="s"><github-actions[bot]@users.noreply.github.com>"</span>
+</code></pre>
+
+</div>
+
+
+
+<h2>
+
+
+ Preparando seu README
+</h2>
+
+<p>Para um bom resultado de uma documentação vimdoc, recomendamos que seu README tenha pelo menos:</p>
+
+<ul>
+<li>Uma seção <code>Sobre</code>
+</li>
+<li>Uma seção <code>Instalação</code>
+</li>
+<li>Uma seção <code>Como usar</code>
+</li>
+</ul>
+
+<p>O uso de tabelas, bullet points também é suportado e recomendado. Quanto mais detalhes conseguir colocar na documentação, melhor ficará o resultado (até emojis são suportados!)</p>
+
+<h2>
+
+
+ Colocando tudo para funcionar
+</h2>
+
+<p>Antes de commitar e rodar a action pela primeira vez, crie o arquivo vazio da doc:<br />
+</p>
+
+<div class="highlight js-code-highlight">
+<pre class="highlight shell"><code><span class="nv">$ </span><span class="nb">touch </span>doc/nome-do-seu-projeto.txt
+<span class="nv">$ </span>git add doc/nome-do-seu-projeto.txt
+<span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s1">'adicionando arquivo vimdoc'</span>
+<span class="nv">$ </span>git push
+</code></pre>
+
+</div>
+
+
+
+<p>Agora envie o novo worflow:<br />
+</p>
+
+<div class="highlight js-code-highlight">
+<pre class="highlight shell"><code><span class="nv">$ </span>git add .github/workflows/docs.yml
+<span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s1">'adicionando docs workflow'</span>
+<span class="nv">$ </span>git push
+</code></pre>
+
+</div>
+
+
+
+<p>Se tudo deu certo, você já poderá ver o resultado do workflow com seu vimdoc gerado na branch principal. Exemplo do resultado na imagem abaixo</p>
+
+<p><a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pre86sg64pzp5y69bdg.png" class="article-body-image-wrapper"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pre86sg64pzp5y69bdg.png" alt="resultado do job" width="800" height="377" /></a></p>
+
+<p>Exemplo pode ser visto <a href="https://github.com/ellisonleao/carbon-now.nvim/actions/runs/5673377787" rel="noopener noreferrer">aqui</a></p>
+
+<h2>
+
+
+ Links úteis
+</h2>
+
+<ul>
+<li>Documentação da <a href="https://kdheepak.com/panvimdoc/" rel="noopener noreferrer">github action</a>
+</li>
+<li>Exemplo de um bom <a href="https://github.com/folke/lazy.nvim/blob/main/README.md" rel="noopener noreferrer">README</a> vs o <a href="https://github.com/folke/lazy.nvim/blob/main/doc/lazy.nvim.txt" rel="noopener noreferrer">resultado</a> em vimdoc</li>
+</ul>
+
+<p>Se gostou do artigo, não esqueça de compartilhar em outras redes e aproveita e me segue também! <a href="https://linktr.ee/ellisonleao" rel="noopener noreferrer">https://linktr.ee/ellisonleao</a></p>
+ Wed, 26 Jul 2023 20:40:59 +0000
+
+
+ Blog do PauloHRPinheiro: Debugando em Python
+ https://paulohrpinheiro.xyz/texts/python/2022-12-17-debugando-em-python.html
+ https://paulohrpinheiro.xyz/texts/python/2022-12-17-debugando-em-python.html
+ Iniciando a usar o módulo pdb do Python para debugar com maestria, na linha de comando
+ Sat, 17 Dec 2022 00:00:00 +0000
+
+
+ PythonClub: Questões para estudo de algoritmos
+ tag:pythonclub.com.br,2022-10-17:/questoes-para-estudo-de-algoritmos.html
+ https://pythonclub.com.br/questoes-para-estudo-de-algoritmos.html
+ <p>Recentemente li o texto do Maycon Alves, <a href="https://mayconbalves.com.br/3-algoritmos-para-voc%C3%AA-sua-l%C3%B3gica/">"3 algoritmos para você sua lógica"</a>, onde são apresentados 3 problemas para treinar a lógica e escrita de algoritmos: <a href="https://pt.wikipedia.org/wiki/Fatorial">cálculo de fatorial</a>, <a href="https://pt.wikipedia.org/wiki/N%C3%BAmero_primo">identificar se um número é primo</a>, e <a href="https://pt.wikipedia.org/wiki/Sequ%C3%AAncia_de_Fibonacci">calcular os valores da sequência de Fibonacci</a>. São problemas interessantes, e após resolvê-los, pode-se fazer outras perguntas que levam a um entendimento mais profundo desses algoritmos. Eu recomendo que leiam o texto do Maycon primeiro e tentem implementar uma solução para esses problemas propostos, e com isso feito, vamos discutir um pouco sobre eles.</p>
+<h2>Analisando as soluções</h2>
+<p>No texto do Maycon, tem uma dica sobre o problema da sequência de Fibonacci, onde é dito que ele pode ser resolvido usando recursividade ou <em>loops</em>. Vamos analisar essas opções.</p>
+<p>Uma solução recursiva pode ser implementada como a baixo. O código dessa solução é simples e se aproxima bastante da descrição matemática do problema.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">n</span> <span class="o"><=</span> <span class="mi">1</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">n</span>
+ <span class="k">return</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Enquanto uma solução iterativa (com <em>loop</em>) pode ser um pouco mais complicada de se ler:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
+ <span class="n">a</span> <span class="o">=</span> <span class="mi">0</span>
+ <span class="n">b</span> <span class="o">=</span> <span class="mi">1</span>
+ <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
+ <span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> <span class="n">b</span><span class="p">,</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span>
+ <span class="k">return</span> <span class="n">b</span>
+</pre></div>
+
+
+<p>Essas mesmas técnicas podem ser utilizadas para resolver o cálculo do fatorial. Onde uma implementação recursiva e outra iterativa podem ser vistas a baixo:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">fatorial</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">n</span> <span class="o"><=</span> <span class="mi">1</span><span class="p">:</span>
+ <span class="k">return</span> <span class="mi">1</span>
+ <span class="k">return</span> <span class="n">n</span> <span class="o">*</span> <span class="n">fatorial</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">fatorial</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="mi">0</span>
+ <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
+ <span class="n">valor</span> <span class="o">*=</span> <span class="n">i</span>
+ <span class="k">return</span> <span class="n">valor</span>
+</pre></div>
+
+
+<p>Com essas soluções implementadas, vem uma pergunta: Existe alguma diferença entre elas, além da diferença de como isso está expresso no código? Um primeiro teste que pode ser feito é de desempenho visando observar quanto tempo cada implementação leva para calcular a resposta. Os testes foram executados em um notebook com processador Intel Core i7-6500U CPU @ 2.50GHz, memória RAM DDR4 2133MHz, no Python 3.9.2 do Debian 11, desativamente o <em>garbage collector</em> do Python durante a execução das funções para ter um resultado com menos variação, apresentados como uma média de 10 execuções (<a href="https://github.com/eduardoklosowski/blog/tree/main/content/2022-10-17-questoes-para-estudo-de-algoritmos">código utilizado</a>).</p>
+<p>O gráfico a baixo mostra o tempo de execução das implementações que calculam os valores da sequência de Fibonacci, onde é possível observar que a implementação iterativa mantém uma linearidade do tempo conforme vai se pedindo números maiores da sequência, diferente da implementação recursiva, que a cada valor quase dobra o tempo de execução.</p>
+<p><img alt="Gráfico do tempo de execução Fibonacci" src="https://pythonclub.com.br/feeds/images/eduardoklosowski/questoes-para-estudo-de-algoritmos/fibonacci.png" /></p>
+<p>E a baixo pode-se observar o gráfico para as implementações que calculam o fatorial, que devido aos tempos serem mais baixos, possui uma variação um pouco maior, e é possível observar uma tendência de reta para as duas implementações, com a implementação recursiva tendo um ângulo um pouco mais íngreme, implicando em um algoritmo mais lento.</p>
+<p><img alt="Gráfico do tempo de execução fatorial" src="https://pythonclub.com.br/feeds/images/eduardoklosowski/questoes-para-estudo-de-algoritmos/fatorial.png" /></p>
+<p>A partir desses dois gráficos algumas perguntas podem ser feitas: Por que a implementação recursiva do Fibonacci apresentou uma curva que se aproxima de uma exponencial e não de uma reta como as demais? Qual a diferença para a implementação recursiva do fatorial que explicar isso? Implementações recursivas são sempre piores que as implementações iterativas, ou existem casos em elas superem ou se equivalem as iterativas?</p>
+<p>Saindo um pouco desses gráficos, outras questões podem ser levantadas, como: Existe mais algum aspecto ou característica além do tempo de execução (e facilidade de leitura do código) para a escolha entre uma implementação ou outra? Considerando que essas funções vão rodar em um servidor, existe alguma coisa que possa ser feita para melhorar a performance dessas funções, como reaproveitar resultados já calculados para se calcular novos resultados? Como isso poderia ser feito? Quais são as desvantagens dessa abordagem?</p>
+<p>Olhando para o problema de verificar se um número é primo, existe o <a href="https://pt.wikipedia.org/wiki/Crivo_de_Erat%C3%B3stenes">crivo de Eratóstenes</a>, ele é uma implementação eficiente? Existem casos em que ele pode ser uma boa solução ou não? O exemplo a baixo (retirado da Wikipédia) mostra o processo para encontrar todos os números primos até 120, existe alguma forma de adaptá-lo para executar diversas vezes reaproveitando o que já foi calculado, como sempre retornar o próximo número primo?</p>
+<p><img alt="Exemplo do crivo de Eratóstenes" src="https://upload.wikimedia.org/wikipedia/commons/8/8c/New_Animation_Sieve_of_Eratosthenes.gif" /></p>
+<h2>Considerações</h2>
+<p>Se você nunca se deparou com perguntas desse tipo, seja bem-vindo a área de análise de algoritmos, onde após se ter uma solução, busca-se analisar e descrever o comportamento do algoritmo, e até a busca de algoritmos mais eficientes. E trazendo para o dia a dia de um desenvolvedor, essas questões podem ser a resposta do motivo do código funcionar muito bem no computador de quem desenvolveu, mas demorar muito ou apresentar problemas para rodar no servidor de produção, ou com o tempo (e crescimento do volume de dados) começar a dar problemas.</p>
+<p>Nesse artigo eu apenas levantei as perguntas, deixo que cada um busque as respostas, que existem. Sintam-se livres para me procurar para discutir alguma questão ou orientações para encontrar as respostas, seja nos comentários do texto no <a href="https://dev.to/eduardoklosowski/questoes-para-estudo-de-algoritmos-5dab">dev.to</a> ou no <a href="https://twitter.com/eduklosowski">Twitter</a>.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ Mon, 17 Oct 2022 18:00:00 +0000
+
+
+ Ellison Leão: Meus 10 plugins favoritos para o Neovim em 2022
+ https://dev.to/ellisonleao/meus-10-plugins-favoritos-para-o-neovim-em-2022-43g2
+ https://dev.to/ellisonleao/meus-10-plugins-favoritos-para-o-neovim-em-2022-43g2
+ <blockquote>
+<p>Uma breve lista dos melhores plugins pra usar agora e aumentar sua produtividade</p>
+</blockquote>
+
+<p>Quando comecei a usar o Neovim, lá por 2017, depois de passar 6 anos usando Vim, não tinha sentido uma diferença tão grande porque primeiro a API lua era praticamente inexistente e minha configuração ainda era puramente vimscript e meus plugins também. </p>
+
+<p>Mas tudo mudou quando fiz o upgrade pras versões 0.4.x e gradualmente comecei a me aventurar no mundo do Lua, no final de 2019. De lá pra ca já consegui migrar minhas configurações 100% para Lua e 99% dos plugins que uso hoje também são feitos na linguagem. Aqui vai minha lista dos 10 melhores plugins que acho indispensáveis hoje:</p>
+
+<h2>
+
+
+ #10
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/wbthomason" rel="noopener noreferrer">
+ wbthomason
+ </a> / <a href="https://github.com/wbthomason/packer.nvim" rel="noopener noreferrer">
+ packer.nvim
+ </a>
+ </h2>
+ <h3>
+ A use-package inspired plugin manager for Neovim. Uses native packages, supports Luarocks dependencies, written in Lua, allows for expressive config
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<p><strong>NOTICE:</strong></p>
+<p>This repository is currently unmaintained. For the time being (as of August, 2023), it is recommended to use one of the following plugin managers instead:</p>
+<ul>
+<li>
+<a href="https://github.com/folke/lazy.nvim" rel="noopener noreferrer">lazy.nvim</a>: Most stable and maintained plugin manager for Nvim.</li>
+<li>
+<a href="https://github.com/lewis6991/pckr.nvim" rel="noopener noreferrer">pckr.nvim</a>: Spiritual successor of packer.nvim. Functional but not as stable as lazy.nvim.</li>
+</ul>
+<div class="markdown-heading">
+<h1 class="heading-element">packer.nvim</h1>
+</div>
+<p><a href="https://gitter.im/packer-nvim/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/335c9ee0b540d19a29323d900502be99a012414abdc965f6e4bd7047842f6541/68747470733a2f2f6261646765732e6769747465722e696d2f7061636b65722d6e76696d2f636f6d6d756e6974792e737667" alt="Gitter" /></a></p>
+<p><a href="https://github.com/jwiegley/use-package" rel="noopener noreferrer"><code>use-package</code></a> inspired plugin/package management for
+Neovim.</p>
+<p>Have questions? Start a <a href="https://github.com/wbthomason/packer.nvim/discussions" rel="noopener noreferrer">discussion</a>.</p>
+<p>Have a problem or idea? Make an <a href="https://github.com/wbthomason/packer.nvim/issues" rel="noopener noreferrer">issue</a> or a <a href="https://github.com/wbthomason/packer.nvim/pulls" rel="noopener noreferrer">PR</a>.</p>
+<p><strong>Packer is built on native packages. You may wish to read <code>:h packages</code> before continuing</strong></p>
+<div class="markdown-heading">
+<h2 class="heading-element">Table of Contents</h2>
+</div>
+<ol>
+<li><a href="https://github.com/wbthomason/packer.nvim#features" rel="noopener noreferrer">Features</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#requirements" rel="noopener noreferrer">Requirements</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#quickstart" rel="noopener noreferrer">Quickstart</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#bootstrapping" rel="noopener noreferrer">Bootstrapping</a></li>
+<li>
+<a href="https://github.com/wbthomason/packer.nvim#usage" rel="noopener noreferrer">Usage</a>
+<ol>
+<li><a href="https://github.com/wbthomason/packer.nvim#the-startup-function" rel="noopener noreferrer">The startup function</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#custom-initialization" rel="noopener noreferrer">Custom Initialization</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#specifying-plugins" rel="noopener noreferrer">Specifying Plugins</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#performing-plugin-management-operations" rel="noopener noreferrer">Performing plugin management operations</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#extending-packer" rel="noopener noreferrer">Extending packer</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#compiling-lazy-loaders" rel="noopener noreferrer">Compiling Lazy-Loaders</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#user-autocommands" rel="noopener noreferrer">User autocommands</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#using-a-floating-window" rel="noopener noreferrer">Using a floating window</a></li>
+</ol>
+</li>
+<li><a href="https://github.com/wbthomason/packer.nvim#profiling" rel="noopener noreferrer">Profiling</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#debugging" rel="noopener noreferrer">Debugging</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#compatibility-and-known-issues" rel="noopener noreferrer">Compatibility and known issues</a></li>
+<li><a href="https://github.com/wbthomason/packer.nvim#contributors" rel="noopener noreferrer">Contributors</a></li>
+</ol>
+<div class="markdown-heading">
+<h2 class="heading-element">Features</h2>
+</div>
+<ul>
+<li>Declarative plugin specification</li>
+<li>Support for dependencies</li>
+<li>Support for Luarocks dependencies</li>
+<li>Expressive configuration and lazy-loading options</li>
+<li>Automatically compiles efficient lazy-loading code to improve startup time</li>
+<li>…</li>
+</ul>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/wbthomason/packer.nvim" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Começando por esse incrível gerenciador de plugins, que vai deixar sua vida bem mais fácil na hora de instalar, atualizar e configurar seus plugins, principalmente se você utiliza lua nas suas configurações. Tem suporte a instalação de pacotes do luarocks, <em>lazy loading</em> e mais.</p>
+
+<h2>
+
+
+ #9
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/numToStr" rel="noopener noreferrer">
+ numToStr
+ </a> / <a href="https://github.com/numToStr/Comment.nvim" rel="noopener noreferrer">
+ Comment.nvim
+ </a>
+ </h2>
+ <h3>
+ 🧠 💪 // Smart and powerful comment plugin for neovim. Supports treesitter, dot repeat, left-right/up-down motions, hooks, and more
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">// Comment.nvim </h1>
+</div>
+<p><sup>⚡ Smart and Powerful commenting plugin for neovim ⚡</sup></p>
+<p><a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/42532967/136532939-926a8350-84b7-4e78-b045-fe21b5947388.gif"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F42532967%2F136532939-926a8350-84b7-4e78-b045-fe21b5947388.gif" alt="Comment.nvim" title="Commenting go brrrr" /></a></p>
+<div class="markdown-heading">
+<h3 class="heading-element">✨ Features</h3>
+</div>
+<ul>
+<li>Supports treesitter. <a href="https://github.com/numToStr/Comment.nvim#treesitter" rel="noopener noreferrer">Read more</a>
+</li>
+<li>Supports <code>commentstring</code>. Read <code>:h comment.commentstring</code>
+</li>
+<li>Supports line (<code>//</code>) and block (<code>/* */</code>) comments</li>
+<li>Dot (<code>.</code>) repeat support for <code>gcc</code>, <code>gbc</code> and friends</li>
+<li>Count support for <code>[count]gcc</code> and <code>[count]gbc</code>
+</li>
+<li>Left-right (<code>gcw</code> <code>gc$</code>) and Up-Down (<code>gc2j</code> <code>gc4k</code>) motions</li>
+<li>Use with text-objects (<code>gci{</code> <code>gbat</code>)</li>
+<li>Supports pre and post hooks</li>
+<li>Ignore certain lines, powered by Lua regex</li>
+</ul>
+<div class="markdown-heading">
+<h3 class="heading-element">🚀 Installation</h3>
+</div>
+<ul>
+<li>With <a href="https://github.com/folke/lazy.nvim" rel="noopener noreferrer">lazy.nvim</a>
+</li>
+</ul>
+<div class="highlight highlight-source-lua notranslate position-relative overflow-auto js-code-highlight">
+<pre><span class="pl-c"><span class="pl-c">--</span> add this to your lua/plugins.lua, lua/plugins/init.lua, or the file you keep your other plugins:</span>
+{
+ <span class="pl-s"><span class="pl-pds">'</span>numToStr/Comment.nvim<span class="pl-pds">'</span></span>,
+ <span class="pl-smi">opts</span> <span class="pl-k">=</span> {
+ <span class="pl-c"><span class="pl-c">--</span> add any options here</span>
+ }
+}
+</pre>
+
+</div>
+<ul>
+<li>With <a href="https://github.com/wbthomason/packer.nvim" rel="noopener noreferrer">packer.nvim</a>
+</li>
+</ul>
+<div class="highlight highlight-source-lua notranslate position-relative overflow-auto js-code-highlight">
+<pre><span class="pl-c1">use</span> {
+ <span class="pl-s"><span class="pl-pds">'</span>numToStr/Comment.nvim<span class="pl-pds">'</span></span>,
+ <span class="pl-en">config</span> <span class="pl-k">=</span> <span class="pl-k">function</span>()
+ <span class="pl-c1">require</span>(<span class="pl-s"><span class="pl-pds">'</span>Comment<span class="pl-pds">'</span></span>).<span class="pl-c1">setup</span>()
+ <span class="pl-k">end</span>
+}</pre>
+
+</div>
+<ul>
+<li>With <a href="https://github.com/junegunn/vim-plug" rel="noopener noreferrer">vim-plug</a>
+</li>
+</ul>
+<div class="highlight highlight-source-viml notranslate position-relative overflow-auto js-code-highlight">
+<pre>Plug <span class="pl-s"><span class="pl-pds">'</span>numToStr/Comment.nvim<span class="pl-pds">'</span></span>
+<span class="pl-c"><span class="pl-c">"</span> Somewhere after plug#end()</span></pre>…
+</div>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/numToStr/Comment.nvim" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Um poderoso plugin para inserir e remover comentários em códigos. Tem suporte ao treesitter, que vamos falar mais pra frente. </p>
+
+<h2>
+
+
+ #8
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/neovim" rel="noopener noreferrer">
+ neovim
+ </a> / <a href="https://github.com/neovim/nvim-lspconfig" rel="noopener noreferrer">
+ nvim-lspconfig
+ </a>
+ </h2>
+ <h3>
+ Quickstart configs for Nvim LSP
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">nvim-lspconfig</h1>
+</div>
+<p>nvim-lspconfig is a "data only" repo, providing basic, default <a href="https://neovim.io/doc/user/lsp.html" rel="nofollow noopener noreferrer">Nvim LSP client</a>
+configurations for various LSP servers.</p>
+<p>View <a href="https://github.com/neovim/nvim-lspconfigdoc/configs.md" rel="noopener noreferrer">all configs</a> or <code>:help lspconfig-all</code> from Nvim.</p>
+<div class="markdown-heading">
+<h2 class="heading-element">Important ⚠️
+</h2>
+</div>
+<ul>
+<li>These configs are <strong>best-effort and supported by the community (you).</strong> See <a href="https://github.com/neovim/nvim-lspconfig#contributions" rel="noopener noreferrer">contributions</a>.</li>
+<li>If you found a bug in Nvim LSP (<code>:help lsp</code>), <a href="https://github.com/neovim/neovim/issues/new?assignees=&labels=bug%2Clsp&template=lsp_bug_report.yml" rel="noopener noreferrer">report it to Neovim core</a>
+<ul>
+<li>
+<strong>Do not</strong> report it here. Only configuration data lives here.</li>
+</ul>
+</li>
+<li>This repo only provides <em>configurations</em>. Its programmatic API is deprecated and must not be used externally.
+<ul>
+<li>The "framework" parts (<em>not</em> the configs) of nvim-lspconfig <a href="https://github.com/neovim/neovim/issues/28479" rel="noopener noreferrer">will be upstreamed to Nvim core</a>.</li>
+</ul>
+</li>
+</ul>
+<div class="markdown-heading">
+<h2 class="heading-element">Install</h2>
+
+</div>
+<p><a href="https://luarocks.org/modules/neovim/nvim-lspconfig" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/57aa64ed10a79399509246a1ae220f761b1c35e4d44c6c07595faf4463019fd8/68747470733a2f2f696d672e736869656c64732e696f2f6c7561726f636b732f762f6e656f76696d2f6e76696d2d6c7370636f6e6669673f6c6f676f3d6c756126636f6c6f723d707572706c65" alt="LuaRocks" /></a></p>
+<ul>
+<li>Requires Nvim 0.10 above. Update Nvim and nvim-lspconfig before reporting an issue.</li>
+<li>Install nvim-lspconfig using Vim's "packages" feature:
+<div class="snippet-clipboard-content notranslate position-relative overflow-auto"><pre class="notranslate"><code>git clone https://github.com/neovim/nvim-lspconfig ~/.config/nvim/pack/nvim/start/nvim-lspconfig
+</code></pre></div>
+</li>
+<li>Or use a 3rd-party plugin manager (consult the documentation for your plugin manager).</li>
+</ul>
+<div class="markdown-heading">
+<h2 class="heading-element">Quickstart</h2>
+
+</div>
+<ol>
+<li>Install a language server, e.g. <a href="https://github.com/neovim/nvim-lspconfigdoc/configs.md#pyright" rel="noopener noreferrer">pyright</a>
+<div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight">
+<pre>npm i -g pyright</pre>
+
+</div>
+</li>
+<li>Add…</li>
+</ol>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/neovim/nvim-lspconfig" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Plugin obrigatório para quem quer utilizar o maravilhoso cliente nativo LSP. Com esse plugin a configuração dos LSP servers das linguagens fica extremamente fácil.</p>
+
+<h2>
+
+
+ #7
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/williamboman" rel="noopener noreferrer">
+ williamboman
+ </a> / <a href="https://github.com/williamboman/nvim-lsp-installer" rel="noopener noreferrer">
+ nvim-lsp-installer
+ </a>
+ </h2>
+ <h3>
+ Further development has moved to https://github.com/williamboman/mason.nvim!
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h2 class="heading-element">ℹ️ Project status</h2>
+</div>
+<blockquote>
+<p>This is an excerpt from the <a href="https://github.com/williamboman/nvim-lsp-installer/discussions/876" rel="noopener noreferrer">announcement here</a>.</p>
+</blockquote>
+<p><em><code>nvim-lsp-installer</code> is no longer maintained.</em></p>
+<p><a href="https://github.com/williamboman/mason.nvim" rel="noopener noreferrer"><code>mason.nvim</code></a> is the next generation version of <code>nvim-lsp-installer</code>. It
+builds on top of the very same foundation as <code>nvim-lsp-installer</code> (which means it's easy to migrate), but with a
+majority of internals refactored to improve extensibility and testability.</p>
+<p>More importantly, the scope of <code>mason.nvim</code> has also been widened to target more than just LSP servers. <code>mason.nvim</code>
+supports DAP servers, linters, formatters, and more. As of writing, <code>mason.nvim</code> provides 150+ packages for 100+
+languages. It can be thought of as a general-purpose package manager, native to Neovim, that runs everywhere Neovim runs
+(Windows, macOS, Linux, etc.).</p>
+<p>Another big change with <code>mason.nvim</code> is that executables are now linked to a single, shared, location, allowing seamless
+access from Neovim builtins (shell, terminal, etc.) as well as other 3rd party plugins.</p>
+<p>There have also been other improvements…</p>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/williamboman/nvim-lsp-installer" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Plugin que fornece todo suporte para uma rápida instalação dos LSP servers com uma excelente UX. É um plugin que funciona em conjunto com o <strong>nvim-lspconfig</strong></p>
+
+<h2>
+
+
+ #6
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/jose-elias-alvarez" rel="noopener noreferrer">
+ jose-elias-alvarez
+ </a> / <a href="https://github.com/jose-elias-alvarez/null-ls.nvim" rel="noopener noreferrer">
+ null-ls.nvim
+ </a>
+ </h2>
+ <h3>
+ Use Neovim as a language server to inject LSP diagnostics, code actions, and more via Lua.
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">ARCHIVED</h1>
+</div>
+<p>null-ls is now archived and will no longer receive updates. Please see
+<a href="https://github.com/jose-elias-alvarez/null-ls.nvim/issues/1621" rel="noopener noreferrer">this issue</a> for
+details.</p>
+<div class="markdown-heading">
+<h1 class="heading-element">null-ls.nvim</h1>
+</div>
+<p>Use Neovim as a language server to inject LSP diagnostics, code actions, and
+more via Lua.</p>
+<div class="markdown-heading">
+<h2 class="heading-element">Motivation</h2>
+</div>
+<p>Neovim's LSP ecosystem is growing, and plugins like
+<a href="https://github.com/nvim-telescope/telescope.nvim" rel="noopener noreferrer">telescope.nvim</a> and
+<a href="https://github.com/folke/trouble.nvim" rel="noopener noreferrer">trouble.nvim</a> make it a joy to work with
+LSP features like code actions and diagnostics.</p>
+<p>Unlike the VS Code and coc.nvim ecosystems, Neovim doesn't provide a way for
+non-LSP sources to hook into its LSP client. null-ls is an attempt to bridge
+that gap and simplify the process of creating, sharing, and setting up LSP
+sources using pure Lua.</p>
+<p>null-ls is also an attempt to reduce the boilerplate required to set up
+general-purpose language servers and improve performance by removing the need
+for external processes.</p>
+<div class="markdown-heading">
+<h2 class="heading-element">Status</h2>
+
+</div>
+<p>null-ls is in <strong>beta status</strong>. Please see below for steps to follow if something
+doesn't work the way…</p>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/jose-elias-alvarez/null-ls.nvim" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Gere diagnósticos, formate, realize ações de código e mais, utilizando esse plugin que utiliza o neovim como um servidor LSP, e faz a ponte entre ferramentas que não fazem uma conexão direta com o cliente nativo LSP. Entre os exemplos podemos citar:</p>
+
+<ul>
+<li>Formatar código ao salvar o buffer com o prettier</li>
+<li>Gerar relatório de erros e warnings com golangci-lint para Go</li>
+<li>Formatar código Lua com stylua</li>
+</ul>
+
+<p>e <a href="https://github.com/jose-elias-alvarez/null-ls.nvim/blob/main/doc/BUILTINS.md" rel="noopener noreferrer">mais</a>! </p>
+
+<h2>
+
+
+ #5
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/nvim-treesitter" rel="noopener noreferrer">
+ nvim-treesitter
+ </a> / <a href="https://github.com/nvim-treesitter/nvim-treesitter" rel="noopener noreferrer">
+ nvim-treesitter
+ </a>
+ </h2>
+ <h3>
+ Nvim Treesitter configurations and abstraction layer
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div>
+ <div class="markdown-heading">
+<h1 class="heading-element">nvim-treesitter</h1>
+</div>
+ <p>
+ <a href="https://matrix.to/#/#nvim-treesitter:matrix.org" rel="nofollow noopener noreferrer">
+ <img alt="Matrix Chat" src="https://camo.githubusercontent.com/954e3fc1a9172d1c08e4e307b5361d27a5a48d45818951fffab09bdc86fa13e9/68747470733a2f2f696d672e736869656c64732e696f2f6d61747269782f6e76696d2d747265657369747465723a6d61747269782e6f7267" />
+ </a>
+ <a href="https://github.com/nvim-treesitter/nvim-treesitter/actions?query=workflow%3A%22Linting+and+style+checking%22+branch%3Amaster" rel="noopener noreferrer">
+ <img alt="Linting and Style" src="https://github.com/nvim-treesitter/nvim-treesitter/workflows/Linting%20and%20style%20checking/badge.svg" />
+ </a>
+ <a href="https://github.com/nvim-treesitter/nvim-treesitter/actions?query=workflow%3A%22Check+loading+of+syntax+files%22+branch%3Amaster" rel="noopener noreferrer">
+ <img alt="Syntax files" src="https://github.com/nvim-treesitter/nvim-treesitter/workflows/Check%20loading%20of%20syntax%20files/badge.svg" />
+ </a>
+ </p>
+</div>
+<div>
+ <p>
+ <a rel="noopener noreferrer" href="https://github.com/nvim-treesitter/nvim-treesitterassets/logo.png"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fnvim-treesitter%2Fnvim-treesitterassets%2Flogo.png" alt="Logo" /></a>
+ </p>
+ <p>
+ <a href="https://github.com/tree-sitter/tree-sitter" rel="noopener noreferrer">Treesitter</a>
+ configurations and abstraction layer for
+ <a href="https://github.com/neovim/neovim/" rel="noopener noreferrer">Neovim</a>
+ </p>
+ <p>
+ <i>
+ Logo by <a href="https://github.com/steelsojka" rel="noopener noreferrer">@steelsojka</a>
+ </i>
+ </p>
+</div>
+<p>The goal of <code>nvim-treesitter</code> is both to provide a simple and easy way to use the interface for <a href="https://github.com/tree-sitter/tree-sitter" rel="noopener noreferrer">tree-sitter</a> in Neovim and to provide some basic functionality such as highlighting based on it:</p>
+<p><a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/2361214/202753610-e923bf4e-e88f-494b-bb1e-d22a7688446f.png"><img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F2361214%2F202753610-e923bf4e-e88f-494b-bb1e-d22a7688446f.png" alt="example-cpp" /></a></p>
+<p>Traditional highlighting (left) vs Treesitter-based highlighting (right)
+More examples can be found in <a href="https://github.com/nvim-treesitter/nvim-treesitter/wiki/Gallery" rel="noopener noreferrer">our gallery</a>.</p>
+<p><strong>Warning: Treesitter and nvim-treesitter highlighting are an experimental feature of Neovim.
+Please consider the experience with this plug-in as experimental until Tree-Sitter support in Neovim is stable!
+We recommend using the nightly builds of Neovim if possible.
+You can find the current roadmap <a href="https://github.com/nvim-treesitter/nvim-treesitter/issues/4767" rel="noopener noreferrer">here</a>.
+The roadmap and all features of this plugin are open to change, and any suggestion will be highly appreciated!</strong></p>
+<p>Nvim-treesitter is based on three interlocking features: <a href="https://github.com/nvim-treesitter/nvim-treesitter#language-parsers" rel="noopener noreferrer"><strong>language parsers</strong></a>, <a href="https://github.com/nvim-treesitter/nvim-treesitter#adding-queries" rel="noopener noreferrer"><strong>queries</strong></a>, and <a href="https://github.com/nvim-treesitter/nvim-treesitter#available-modules" rel="noopener noreferrer"><strong>modules</strong></a>, where <em>modules</em> provide features – e.g., highlighting – based on…</p>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/nvim-treesitter/nvim-treesitter" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Um plugin que faz interface com o excelente projeto <a href="https://github.com/tree-sitter/tree-sitter" rel="noopener noreferrer">tree-sitter</a> e traz funcionalides como _syntax highlighting _, indentação, seleção incremental, movimentação, folding, entre outros. </p>
+
+<h2>
+
+
+ #4
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/L3MON4D3" rel="noopener noreferrer">
+ L3MON4D3
+ </a> / <a href="https://github.com/L3MON4D3/LuaSnip" rel="noopener noreferrer">
+ LuaSnip
+ </a>
+ </h2>
+ <h3>
+ Snippet Engine for Neovim written in Lua.
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<p><a href="https://matrix.to/#/%23luasnip:matrix.org" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/7e4c0c2d22ff5f4cb470267e74a6e420bc201b085070d0ff43c6626e84dfb09a/68747470733a2f2f696d672e736869656c64732e696f2f6d61747269782f6c7561736e69703a6d61747269782e6f72673f6c6162656c3d4d6174726978266c6f676f3d6d6174726978" alt="LuaSnip" /></a></p>
+<div class="markdown-heading">
+<h1 class="heading-element">LuaSnip</h1>
+</div>
+
+
+
+
+ <span class="m-1">javadoc.mp4</span>
+
+
+
+
+
+
+
+<div class="markdown-heading">
+<h1 class="heading-element">Features</h1>
+
+</div>
+<ul>
+<li>Tabstops</li>
+<li>Text-Transformations using Lua functions</li>
+<li>Conditional Expansion</li>
+<li>Defining nested Snippets</li>
+<li>Filetype-specific Snippets</li>
+<li>Choices</li>
+<li>Dynamic Snippet creation</li>
+<li>Regex-Trigger</li>
+<li>Autotriggered Snippets</li>
+<li>Easy Postfix Snippets</li>
+<li>Fast</li>
+<li>Parse <a href="https://microsoft.github.io/language-server-protocol/specification#snippet_syntax" rel="nofollow noopener noreferrer">LSP-Style</a> Snippets either directly in Lua, as a VSCode package or a SnipMate snippet collection.</li>
+<li>Expand LSP-Snippets with <a href="https://github.com/hrsh7th/nvim-compe" rel="noopener noreferrer">nvim-compe</a> (or its' successor, <a href="https://github.com/hrsh7th/nvim-cmp" rel="noopener noreferrer">nvim-cmp</a> (requires <a href="https://github.com/saadparwaiz1/cmp_luasnip" rel="noopener noreferrer">cmp_luasnip</a>))</li>
+<li>Snippet history (jump back into older snippets)</li>
+<li>Resolve filetype at the cursor using Treesitter</li>
+</ul>
+<div class="markdown-heading">
+<h1 class="heading-element">Drawbacks</h1>
+
+</div>
+<ul>
+<li>Snippets that make use of the entire functionality of this plugin have to be defined in Lua (but 95% of snippets can be written in LSP-syntax).</li>
+</ul>
+<div class="markdown-heading">
+<h1 class="heading-element">Requirements</h1>
+
+</div>
+<p>Neovim >= 0.7 (extmarks)
+<code>jsregexp</code> for <code>lsp-snippet-transformations</code> (see <a href="https://github.com/L3MON4D3/LuaSnip/blob/master/DOC.md#transformations" rel="noopener noreferrer">here</a> for some tips on installing it).</p>
+<div class="markdown-heading">
+<h1 class="heading-element">Setup</h1>
+
+</div>
+<div class="markdown-heading">
+<h2 class="heading-element">Install</h2>
+
+</div>
+<ul>
+<li>
+<p>With your preferred plugin manager i.e. <a href="https://github.com/junegunn/vim-plug" rel="noopener noreferrer">vim-plug</a>, <a href="https://github.com/wbthomason/packer.nvim" rel="noopener noreferrer">Packer</a> or <a href="https://github.com/folke/lazy.nvim" rel="noopener noreferrer">lazy</a><br />
+<strong>Packer</strong>:</p>
+<div class="highlight highlight-source-lua notranslate position-relative overflow-auto js-code-highlight">
+<pre><span class="pl-c1">use</span>({
+ <span class="pl-s"><span class="pl-pds">"</span>L3MON4D3/LuaSnip<span class="pl-pds">"</span></span>
+ <span class="pl-c"><span class="pl-c">--</span> follow latest release.</span>
+ <span class="pl-smi">tag</span> <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span>v2.*<span class="pl-pds">"</span></span>, <span class="pl-c"><span class="pl-c">--</span> Replace <CurrentMajor> by the latest released major (first</span></pre>…
+</div>
+</li>
+</ul>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/L3MON4D3/LuaSnip" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>O melhor script para criação de snippets do Neovim. Simples assim. Tem uma curva de aprendizado um pouco alta, mas quando se pega o jeito, você não vai querer largar nunca. <a href="https://www.youtube.com/watch?v=Dn800rlPIho" rel="noopener noreferrer">Aqui</a> um vídeo explicando mais sobre as funcionalidades dele (Em inglês)</p>
+
+<h2>
+
+
+ #3
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/hrsh7th" rel="noopener noreferrer">
+ hrsh7th
+ </a> / <a href="https://github.com/hrsh7th/nvim-cmp" rel="noopener noreferrer">
+ nvim-cmp
+ </a>
+ </h2>
+ <h3>
+ A completion plugin for neovim coded in Lua.
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">nvim-cmp</h1>
+</div>
+<p>A completion engine plugin for neovim written in Lua
+Completion sources are installed from external repositories and "sourced".</p>
+
+
+
+
+ <span class="m-1">demo.mp4</span>
+
+
+
+
+
+
+
+
+<div class="markdown-heading">
+<h1 class="heading-element">Readme!</h1>
+
+</div>
+<ol>
+<li>There is a GitHub issue that documents <a href="https://github.com/hrsh7th/nvim-cmp/issues/231" rel="noopener noreferrer">breaking changes</a> for nvim-cmp. Subscribe to the issue to be notified of upcoming breaking changes.</li>
+<li>This is my hobby project. You can support me via GitHub sponsors.</li>
+<li>Bug reports are welcome, but don't expect a fix unless you provide minimal configuration and steps to reproduce your issue.</li>
+<li>The <code>cmp.mapping.preset.*</code> is pre-defined configuration that aims to mimic neovim's native like behavior. It can be changed without announcement. Please manage key-mapping by yourself.</li>
+</ol>
+<div class="markdown-heading">
+<h1 class="heading-element">Concept</h1>
+
+</div>
+<ul>
+<li>Full support for LSP completion related capabilities</li>
+<li>Powerful customizability via Lua functions</li>
+<li>Smart handling of key mappings</li>
+<li>No flicker</li>
+</ul>
+<div class="markdown-heading">
+<h1 class="heading-element">Setup</h1>
+
+</div>
+<div class="markdown-heading">
+<h3 class="heading-element">Recommended Configuration</h3>
+
+</div>
+<p>This example configuration uses <code>vim-plug</code> as the plugin manager and <code>vim-vsnip</code> as a snippet plugin.</p>
+<div class="highlight highlight-source-viml notranslate position-relative overflow-auto js-code-highlight">
+<pre><span class="pl-c1">call</span> <span class="pl-en">plug#begin</span>(<span class="pl-smi"><span class="pl-k">s:</span>plug_dir</span>)
+Plug <span class="pl-s"><span class="pl-pds">'</span>neovim/nvim-lspconfig<span class="pl-pds">'</span></span>
+Plug <span class="pl-s"><span class="pl-pds">'</span>hrsh7th/cmp-nvim-lsp</span></pre>…
+</div>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/hrsh7th/nvim-cmp" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Seguindo o melhor plugin para snippets, esse com certeza vai ser o melhor plugin para autocomplete que você irá ter no mercado até o momento. Seu design flexível permite incluir vários tipos diferentes de <em>sources</em>, desde sugestões de LSP, snippets, etc. A lista é <a href="https://github.com/hrsh7th/nvim-cmp/wiki/List-of-sources" rel="noopener noreferrer">gigantesca</a> e você também conta com uma excelente documentação caso queira criar os seus próprios sources também.</p>
+
+<h2>
+
+
+ #2
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/nvim-lualine" rel="noopener noreferrer">
+ nvim-lualine
+ </a> / <a href="https://github.com/nvim-lualine/lualine.nvim" rel="noopener noreferrer">
+ lualine.nvim
+ </a>
+ </h2>
+ <h3>
+ A blazing fast and easy to configure neovim statusline plugin written in pure lua.
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">lualine.nvim</h1>
+</div>
+<p><a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/6da4d97c7bbc5bccb5827dacee2c9ab833fc2f7d7e800a518b68e5304c0937dd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c616e6775616765732f636f64652d73697a652f6e76696d2d6c75616c696e652f6c75616c696e652e6e76696d3f7374796c653d666c61742d737175617265"><img src="https://camo.githubusercontent.com/6da4d97c7bbc5bccb5827dacee2c9ab833fc2f7d7e800a518b68e5304c0937dd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c616e6775616765732f636f64652d73697a652f6e76696d2d6c75616c696e652f6c75616c696e652e6e76696d3f7374796c653d666c61742d737175617265" alt="code size" /></a>
+<a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/626a72ea58ecf1972a27235993041be5014e1ef417a65b4988a1ef0a13457219/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6e76696d2d6c75616c696e652f6c75616c696e652e6e76696d3f7374796c653d666c61742d737175617265"><img src="https://camo.githubusercontent.com/626a72ea58ecf1972a27235993041be5014e1ef417a65b4988a1ef0a13457219/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6e76696d2d6c75616c696e652f6c75616c696e652e6e76696d3f7374796c653d666c61742d737175617265" alt="license" /></a>
+<a href="https://buymeacoffee.com/shadmansalj" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/ad541f86521726ee2afc61b260dcb24d3a359f0bdd4f8f7ddff53456eba73b64/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4275792532304d6525323061253230436f666665652d4637393431443f7374796c653d706c6173746963266c6f676f3d6275792d6d652d612d636f66666565266c6f676f436f6c6f723d7768697465" alt="Buy Me a Coffee" /></a></p>
+<p>A blazing fast and easy to configure Neovim statusline written in Lua.</p>
+<p><code>lualine.nvim</code> requires Neovim >= 0.7.</p>
+<p>For previous versions of neovim please use compatability tags for example
+compat-nvim-0.5</p>
+<div class="markdown-heading">
+<h2 class="heading-element">Contributing</h2>
+
+</div>
+<p>Feel free to create an issue/PR if you want to see anything else implemented.
+If you have some question or need help with configuration, start a <a href="https://github.com/nvim-lualine/lualine.nvim/discussions" rel="noopener noreferrer">discussion</a>.</p>
+<p>Please read <a href="https://github.com/nvim-lualine/lualine.nvim./CONTRIBUTING.md" rel="noopener noreferrer">CONTRIBUTING.md</a> before opening a PR.
+You can also help with documentation in the <a href="https://github.com/nvim-lualine/lualine.nvim/wiki" rel="noopener noreferrer">wiki</a>.</p>
+
+<div class="markdown-heading">
+<h2 class="heading-element">Screenshots</h2>
+
+</div>
+<p>Here is a preview of what lualine can look like.</p>
+<p>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/41551030/108650373-bb025580-74bf-11eb-8682-2c09321dd18e.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F41551030%2F108650373-bb025580-74bf-11eb-8682-2c09321dd18e.png" /></a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/41551030/108650377-bd64af80-74bf-11eb-9c55-fbfc51b39fe8.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F41551030%2F108650377-bd64af80-74bf-11eb-9c55-fbfc51b39fe8.png" /></a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/41551030/108650378-be95dc80-74bf-11eb-9718-82b242ecdd54.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F41551030%2F108650378-be95dc80-74bf-11eb-9718-82b242ecdd54.png" /></a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/41551030/108650381-bfc70980-74bf-11eb-9245-85c48f0f154a.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F41551030%2F108650381-bfc70980-74bf-11eb-9245-85c48f0f154a.png" /></a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/41551030/103467925-32372b00-4d54-11eb-88d6-6d39c46854d8.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F41551030%2F103467925-32372b00-4d54-11eb-88d6-6d39c46854d8.png" /></a>
+</p>
+
+<p>Screenshots of all available themes are listed in <a href="https://github.com/nvim-lualine/lualine.nvim./THEMES.md" rel="noopener noreferrer">THEMES.md</a></p>
+
+<p>For those who want to break the norms, you can create custom looks for lualine.</p>
+
+<p><strong>Example</strong> :</p>
+
+<ul>
+<li>
+<a href="https://github.com/nvim-lualine/lualine.nvimexamples/evil_lualine.lua" rel="noopener noreferrer">evil_lualine</a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/13149513/113875129-4453ba00-97d8-11eb-8f21-94a9ef565db3.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F13149513%2F113875129-4453ba00-97d8-11eb-8f21-94a9ef565db3.png" /></a>
+</li>
+<li>
+<a href="https://github.com/nvim-lualine/lualine.nvimexamples/slanted-gaps.lua" rel="noopener noreferrer">slanted-gaps</a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/13149513/143395518-f6d6f748-c1ca-491b-9dab-246d0a8cf23f.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F13149513%2F143395518-f6d6f748-c1ca-491b-9dab-246d0a8cf23f.png" /></a>
+</li>
+<li>
+<a href="https://github.com/nvim-lualine/lualine.nvimexamples/bubbles.lua" rel="noopener noreferrer">bubbles</a>
+<a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/20235646/131350468-fc556196-5f46-4bfe-a72e-960f6a58db2c.png"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F20235646%2F131350468-fc556196-5f46-4bfe-a72e-960f6a58db2c.png" /></a>
+</li>
+<li>
+<a href="https://github.com/nvim-lualine/lualine.nvimexamples/cosmicink.lua" rel="noopener noreferrer">cosmicink</a>
+<a rel="noopener noreferrer" href="https://private-user-images.githubusercontent.com/125453388/423959036-c8d3e4ba-4997-42e9-a1bb-d5e2a444bbfd.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDQ4MTIxMTAsIm5iZiI6MTc0NDgxMTgxMCwicGF0aCI6Ii8xMjU0NTMzODgvNDIzOTU5MDM2LWM4ZDNlNGJhLTQ5OTctNDJlOS1hMWJiLWQ1ZTJhNDQ0YmJmZC5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwNDE2JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDQxNlQxMzU2NTBaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0wOWU3Nzg3M2NhNzQ4YzY1ZjVhNTVmNDI2MDNkMTM3ODY5MzNiZjQ2NzYyMWE0MmFjYjk3ODkwYmI3ZmEzZWM0JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.nAz4h2CN1evpZdG8RPdAhGxdROfh57jmLX71a34_ZFE"><img width="700" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprivate-user-images.githubusercontent.com%2F125453388%2F423959036-c8d3e4ba-4997-42e9-a1bb-d5e2a444bbfd.png%3Fjwt%3DeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDQ4MTIxMTAsIm5iZiI6MTc0NDgxMTgxMCwicGF0aCI6Ii8xMjU0NTMzODgvNDIzOTU5MDM2LWM4ZDNlNGJhLTQ5OTctNDJlOS1hMWJiLWQ1ZTJhNDQ0YmJmZC5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwNDE2JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDQxNlQxMzU2NTBaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0wOWU3Nzg3M2NhNzQ4YzY1ZjVhNTVmNDI2MDNkMTM3ODY5MzNiZjQ2NzYyMWE0MmFjYjk3ODkwYmI3ZmEzZWM0JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.nAz4h2CN1evpZdG8RPdAhGxdROfh57jmLX71a34_ZFE" /></a>
+</li>
+</ul>
+
+<div class="markdown-heading">
+<h2 class="heading-element">Performance compared to other plugins</h2>
+
+</div>
+
+<p>Unlike other statusline plugins, lualine loads only the components you specify, and nothing else.</p>
+
+<p>Startup time performance measured with an amazing plugin <a href="https://github.com/dstein64/vim-startuptime" rel="noopener noreferrer">dstein64/vim-startuptime</a></p>
+
+<p>Times are measured with a…</p>
+</div>
+
+
+</div>
+<br />
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/nvim-lualine/lualine.nvim" rel="noopener noreferrer">View on GitHub</a></div>
+<br />
+</div>
+<br />
+
+
+<p>Minha escolha para plugin de statusline. Simples, rápido e bem flexível.</p>
+
+<h2>
+
+
+ #1
+</h2>
+
+
+<div class="ltag-github-readme-tag">
+ <div class="readme-overview">
+ <h2>
+ <img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" />
+ <a href="https://github.com/nvim-telescope" rel="noopener noreferrer">
+ nvim-telescope
+ </a> / <a href="https://github.com/nvim-telescope/telescope.nvim" rel="noopener noreferrer">
+ telescope.nvim
+ </a>
+ </h2>
+ <h3>
+ Find, Filter, Preview, Pick. All lua, all the time.
+ </h3>
+ </div>
+ <div class="ltag-github-body">
+
+<div id="readme" class="md">
+<div class="markdown-heading">
+<h1 class="heading-element">telescope.nvim</h1>
+</div>
+<p><a href="https://gitter.im/nvim-telescope/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/4768045434e6ee57bdd36b11fcf554e88d207acd01e0b78a2ef3c04b2f8a7d25/68747470733a2f2f6261646765732e6769747465722e696d2f6e76696d2d74656c6573636f70652f636f6d6d756e6974792e737667" alt="Gitter" /></a>
+<a href="https://luarocks.org/modules/Conni2461/telescope.nvim" rel="nofollow noopener noreferrer"><img src="https://camo.githubusercontent.com/4b597076c327f7a8f7005e116cdd17c5e936a7d2a8f99655b31f1c81468b6fc4/68747470733a2f2f696d672e736869656c64732e696f2f6c7561726f636b732f762f436f6e6e69323436312f74656c6573636f70652e6e76696d3f6c6f676f3d6c756126636f6c6f723d707572706c65" alt="LuaRocks" /></a></p>
+<p>Gaze deeply into unknown regions using the power of the moon.</p>
+<div class="markdown-heading">
+<h2 class="heading-element">What Is Telescope?</h2>
+</div>
+<p><code>telescope.nvim</code> is a highly extendable fuzzy finder over lists. Built on the
+latest awesome features from <code>neovim</code> core. Telescope is centered around
+modularity, allowing for easy customization.</p>
+<p>Community driven builtin <a href="https://github.com/nvim-telescope/telescope.nvim#pickers" rel="noopener noreferrer">pickers</a>, <a href="https://github.com/nvim-telescope/telescope.nvim#sorters" rel="noopener noreferrer">sorters</a> and
+<a href="https://github.com/nvim-telescope/telescope.nvim#previewers" rel="noopener noreferrer">previewers</a>.</p>
+<p><a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/5eb234defa4dcc0145ba0d8e327a03869f83af8ac0def9007828e4835dfecd32/68747470733a2f2f692e696d6775722e636f6d2f5454546a6136742e676966"><img src="https://camo.githubusercontent.com/5eb234defa4dcc0145ba0d8e327a03869f83af8ac0def9007828e4835dfecd32/68747470733a2f2f692e696d6775722e636f6d2f5454546a6136742e676966" alt="Preview" /></a>
+For more showcases of Telescope, please visit the <a href="https://github.com/nvim-telescope/telescope.nvim/wiki/Showcase" rel="noopener noreferrer">Showcase
+section</a> in the
+Telescope Wiki</p>
+<div class="markdown-heading">
+<h2 class="heading-element">Telescope Table of Contents</h2>
+</div>
+<ul>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#getting-started" rel="noopener noreferrer">Getting Started</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#usage" rel="noopener noreferrer">Usage</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#customization" rel="noopener noreferrer">Customization</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#default-mappings" rel="noopener noreferrer">Default Mappings</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#pickers" rel="noopener noreferrer">Pickers</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#previewers" rel="noopener noreferrer">Previewers</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#sorters" rel="noopener noreferrer">Sorters</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#layout-display" rel="noopener noreferrer">Layout</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#themes" rel="noopener noreferrer">Themes</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#vim-commands" rel="noopener noreferrer">Commands</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#autocmds" rel="noopener noreferrer">Autocmds</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#extensions" rel="noopener noreferrer">Extensions</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#api" rel="noopener noreferrer">API</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#media" rel="noopener noreferrer">Media</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim#contributing" rel="noopener noreferrer">Contributing</a></li>
+<li><a href="https://github.com/nvim-telescope/telescope.nvim/blob/master/doc/telescope_changelog.txt" rel="noopener noreferrer">Changelog</a></li>
+</ul>
+<div class="markdown-heading">
+<h2 class="heading-element">Getting Started</h2>
+
+</div>
+<p>This section should guide you to run your first builtin pickers.</p>
+<p><a href="https://github.com/neovim/neovim/releases/tag/v0.9.0" rel="noopener noreferrer">Neovim (v0.9.0)</a> or the
+latest neovim nightly commit is required for <code>telescope.nvim</code> to work.
+The neovim version also needs to be compiled with LuaJIT, we currently do not
+support Lua5.1 because of some ongoing issues.</p>
+<div class="markdown-heading">
+<h3 class="heading-element">Required dependencies</h3>
+
+</div>
+<ul>
+<li>
+<a href="https://github.com/nvim-lua/plenary.nvim" rel="noopener noreferrer">nvim-lua/plenary.nvim</a> is required.</li>
+</ul>
+<div class="markdown-heading">
+<h3 class="heading-element">Suggested dependencies</h3>
+
+</div>
+<ul>
+<li>
+<a href="https://github.com/BurntSushi/ripgrep" rel="noopener noreferrer">BurntSushi/ripgrep</a> is required for
+<code>live_grep</code> and <code>grep_string</code> and…</li>
+</ul>
+</div>
+ </div>
+ <div class="gh-btn-container"><a class="gh-btn" href="https://github.com/nvim-telescope/telescope.nvim" rel="noopener noreferrer">View on GitHub</a></div>
+</div>
+
+
+<p>Talvez o plugin mais essencial de todos hoje. Faça buscas por arquivo, texto e muito mais, numa velocidade absurda, com esse plugin fantástico.</p>
+
+<h2>
+
+
+ Conclusão
+</h2>
+
+<p>Com o ecossistema Lua se desenvolvendo cada dia mais, <a href="https://github.com/topics/neovim-plugin?l=lua" rel="noopener noreferrer">muitos plugins estão surgindo todos os dias</a> e com certeza essa lista provavelmente precisará ser atualizada em um futuro próximo.</p>
+
+<p><strong>Faltou algum plugin nessa lista? Me conta ae nos comentários!</strong></p>
+ Fri, 13 May 2022 17:56:41 +0000
+
+
+ PythonClub: Participe da Python Brasil 2021, a maior conferência de Python da América Latina
+ tag:pythonclub.com.br,2021-08-02:/python-brasil-2021.html
+ https://pythonclub.com.br/python-brasil-2021.html
+ <p><em>Python Brasil 2021, a maior conferência de Python da América Latina, acontecerá entre os dias 11 e 17 de outubro, reunindo pessoas desenvolvedoras, cientistas de dados e entusiastas da tecnologia.</em></p>
+<p><img alt="alt text" src="https://pythonclub.com.br/images/rafpyprog/pybr-banner.jpeg" title="Python Brasil 2021" /></p>
+<p>A <a href="https://2021.pythonbrasil.org.br/">Python Brasil 2021</a>, evento promovido pela comunidade brasileira de Python, traz nesta edição uma agenda imperdível para quem quer mergulhar no universo de uma das linguagens de programação mais utilizadas na atualidade.</p>
+<p>Uma das principais novidades deste ano é a trilha de atividades em espanhol, que vai trazer ao evento palestras de toda a América Latina, criando um ambiente de muita diversidade onde as comunidades Python de língua espanhola vão poder se conectar e trocar experiências.</p>
+<p>Em sua 17 edição, o evento será realizado de forma online, gratuito e aberto para qualquer pessoa de 11 a 17 de outubro. É esperado um público de mais de 10 mil participantes de todas as partes do Brasil e do mundo.
+Nos sete dias de imersão, os participantes poderão contribuir para projetos de software livre, participar de treinamentos e adquirir novos conhecimentos com outras pessoas da comunidade.</p>
+<p>E que tal falar sobre aquele projeto inovador, dar dicas sobre carreira ou mostrar formas de usar a tecnologia para causar impacto social, tudo isso em um ambiente inclusivo, seguro e acolhedor?! Até o dia 12 de agosto você poderá <a href="https://docs.google.com/forms/d/e/1FAIpQLSfXA7KGJbmoE6BHRgWAtK8LlBTEULv8QTS8ffHLIKUgeiLkZA/viewform">submeter sua proposta</a> para apresentação de palestras e tutoriais na maior conferência Python da América Latina.</p>
+<p>Você poderá acompanhar a Python Brasil pelo YouTube em tempo real e também participar de atividades e interagir com toda a comunidade pelo servidor do Discord criado especialmente para o evento.</p>
+<p>Não deixe de seguir o <a href="https://instagram.com/pythonbrasil?utm_medium=copy_link">perfil do evento no Instagram</a> ou <a href="https://twitter.com/pythonbrasil">Twitter</a> para ficar por dentro das últimas novidades!</p>
+ Mon, 02 Aug 2021 18:00:00 +0000
+
+
+ Filipe Saraiva: Ciclo de Entrevistas sobre as Pesquisas no PPGCC da UFPA – Inteligência Computacional
+ https://blog.filipesaraiva.info/?p=2250
+ https://blog.filipesaraiva.info/?p=2250
+ <p><img /></p>
+<p>A <a href="http://computacao.ufpa.br/">Faculdade de Computação</a> e o <a href="https://ppgcc.propesp.ufpa.br/">Programa de Pós-Graduação em Ciência da Computação</a> da <a href="https://ufpa.br/">UFPA</a> estão desenvolvendo um projeto que pretende atingir dois objetivos: o primeiro, fazer uma melhor divulgação para o público externo à universidade do que produzimos em nossas pesquisas; o segundo, uma melhor divulgação INTERNA da mesma coisa – o que desenvolvemos em nossas pesquisas.</p>
+<p>Sim, <strong>INTERNA</strong> – não bastasse de fato a comunicação deficitária e pulverizada do que fazemos para os nossos próprios alunos, a pandemia só veio a piorar esse quadro. Após mais de um ano sem contato próximo com as turmas, com aulas completamente à distância e sem maiores interações extra-classe, os alunos em geral estão perdidos sobre as possibilidades de temas para TCCs e pesquisas que eles podem realizar conosco.</p>
+<p>Dessa forma as duas subunidades estão entrevistando todos os professores para que falem um pouco sobre os temas que trabalham, o histórico de pesquisa, o que vem sendo feito e, mais interessante, o que pode vir a ser feito. As entrevistas ocorrem no canal do YouTube <a href="https://www.youtube.com/c/ComputaçãoUFPA/">Computação UFPA</a> e depois são retrabalhadas para aparecerem no <a href="https://open.spotify.com/show/7oGkFKS4eV4WM2XjbEFeJu">FacompCast</a>.</p>
+<p>Feitas as devidas introduções, nesta terça dia 22/06 às 11h eu e o amigo prof. Jefferson Morais iremos falar um pouco sobre as pesquisas em Inteligência Computacional (ou seria Artificial?) desenvolvidas por nós. Será um bom apanhado sobre os trabalhos em 4 áreas que atuamos – aprendizado de máquina, metaheurísticas, sistemas fuzzy e sistemas multiagentes -, expondo projetos atuais e novos para os interessados.</p>
+<p>Nos vemos portanto logo mais na <a href="https://www.youtube.com/watch?v=VRytpiFWTQI">sala da entrevista</a>.</p>
+<p><strong>UPDATE:</strong></p>
+<p>A gravação já está disponível, segue abaixo:</p>
+<p></p>
+ Mon, 21 Jun 2021 21:51:57 +0000
+
+
+ PythonClub: Orientação a objetos de outra forma: Property
+ tag:pythonclub.com.br,2021-05-17:/oo-de-outra-forma-6.html
+ https://pythonclub.com.br/oo-de-outra-forma-6.html
+ <p>Seguindo com a série, chegou a hora de discutir sobre encapsulamento, ou seja, ocultar detalhes de implementação de uma classe do resto do código. Em algumas linguagens de programação isso é feito utilizando <code>protected</code> ou <code>private</code>, e às vezes o acesso aos atributos é feito através de funções <em>getters</em> e <em>setters</em>. Nesse texto vamos ver como o Python lida com essas questões.</p>
+<h2>Métodos protegidos e privados</h2>
+<p>Diferente de linguagens como Java e PHP que possuem palavras-chave como <code>protected</code> e <code>private</code> para impedir que outras classes acessem determinados métodos ou atributos, Python deixa tudo como público. Porém isso não significa que todas as funções de uma classe podem ser chamadas por outras, ou todos os atributos podem ser lidos e alterados sem cuidados.</p>
+<p>Para que quem estiver escrevendo um código saiba quais as funções ou atributos que não deveriam ser acessados diretamente, segue-se o padrão de começá-los com <code>_</code>, de forma similar aos arquivos ocultos em sistemas UNIX, que começam com <code>.</code>. Esse padrão já foi seguido na classe <code>AutenticavelComRegistro</code> da postagem sobre <a href="https://dev.to/acaverna/orientacao-a-objetos-de-outra-forma-heranca-multiplas-e-mixins-31eb">mixins</a>, onde a função que pega a data do sistema foi nomeada <code>_get_data</code>. Entretanto isso é apenas uma sugestão, nada impede dela ser chamada, como no exemplo a baixo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
+
+
+<span class="k">class</span> <span class="nc">Exemplo</span><span class="p">:</span>
+ <span class="k">def</span> <span class="nf">_get_data</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'</span><span class="si">%d</span><span class="s1">/%m/%Y %T'</span><span class="p">)</span>
+
+
+<span class="n">obj</span> <span class="o">=</span> <span class="n">Exemplo</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">_get_data</span><span class="p">())</span>
+</pre></div>
+
+
+<p>Porém algumas bibliotecas também utilizam o <code>_</code> para indicar outras informações como metadados do objeto, e que podem ser acessados sem muitos problemas. Assim é possível utilizar esse símbolo duas vezes (<code>__</code>) para indicar que realmente essa variável ou função não deveria ser acessada de fora da classe, apresentando erro de que o atributo não foi encontrado ao tentar executar a função, porém ela ainda pode ser acessada:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
+
+
+<span class="k">class</span> <span class="nc">Exemplo</span><span class="p">:</span>
+ <span class="k">def</span> <span class="nf">__get_data</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'</span><span class="si">%d</span><span class="s1">/%m/%Y %T'</span><span class="p">)</span>
+
+
+<span class="n">obj</span> <span class="o">=</span> <span class="n">Exemplo</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">__get_data</span><span class="p">())</span> <span class="c1"># AttributeError</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">_Exemplo__get_data</span><span class="p">())</span> <span class="c1"># Executa a função</span>
+</pre></div>
+
+
+<h2>Property</h2>
+<p>Os <em>getters</em> e <em>setters</em> muitas vezes são usados para impedir que determinadas variáveis sejam alteradas, ou validar o valor antes de atribuir a variável, ou ainda processar um valor a partir de outras variáveis. Porém como o Python incentiva o acesso direto as variáveis, existe a <em>property</em>, que ao tentar acessar uma variável ou alterar um valor, uma função é chamada. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+ <span class="nd">@property</span>
+ <span class="k">def</span> <span class="nf">nome</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_nome</span>
+
+ <span class="nd">@property</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.nome}</span><span class="s1"> </span><span class="si">{self.sobrenome}</span><span class="s1">'</span>
+
+ <span class="nd">@nome_completo</span><span class="o">.</span><span class="n">setter</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="n">valor</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">' '</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_nome</span> <span class="o">=</span> <span class="n">valor</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">valor</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
+
+ <span class="nd">@property</span>
+ <span class="k">def</span> <span class="nf">idade</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_idade</span>
+
+ <span class="nd">@idade</span><span class="o">.</span><span class="n">setter</span>
+ <span class="k">def</span> <span class="nf">idade</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">valor</span> <span class="o"><</span> <span class="mi">0</span><span class="p">:</span>
+ <span class="k">raise</span> <span class="ne">ValueError</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_idade</span> <span class="o">=</span> <span class="n">valor</span>
+
+ <span class="k">def</span> <span class="nf">fazer_aniversario</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">+=</span> <span class="mi">1</span>
+</pre></div>
+
+
+<p>Nesse código algumas variáveis são acessíveis através de <em>properties</em>, de forma geral, as variáveis foram definidas começando com <code>_</code> e com uma <em>property</em> de mesmo nome (sem o <code>_</code>). O primeiro caso é o <code>nome</code>, que possui apenas o <em>getter</em>, sendo possível o seu acesso como <code>obj.nome</code>, porém ao tentar atribuir um valor, será lançado um erro (<code>AttributeError: can't set attribute</code>). Em relação ao <code>sobrenome</code>, como não é necessário nenhum tratamento especial, não foi utilizado um <em>property</em>, porém futuramente pode ser facilmente substituído por um sem precisar alterar os demais códigos. Porém a função <code>nome_completo</code> foi substituída por um <em>property</em>, permitindo tanto o acesso ao nome completo da pessoa, como se fosse uma variável, quanto trocar <code>nome</code> e <code>sobrenome</code> ao atribuir um novo valor para essa <em>property</em>. Quanto a <code>idade</code> utiliza o <em>setter</em> do <em>property</em> para validar o valor recebido, retornando erro para idades inválidas (negativas).</p>
+<p>Vale observar também que todas as funções de <em>getter</em> não recebem nenhum argumento (além do <code>self</code>), enquanto as funções de <em>setter</em> recebem o valor atribuído à variável.</p>
+<p>Utilizando a <a href="https://dev.to/acaverna/orientacao-a-objetos-de-outra-forma-abc-89b">ABC</a>, ainda é possível informar que alguma classe filha deverá implementar alguma <em>property</em>. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span>
+
+
+<span class="k">class</span> <span class="nc">Pessoa</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+ <span class="nd">@property</span>
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="k">class</span> <span class="nc">Brasileiro</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="nd">@property</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.nome}</span><span class="s1"> </span><span class="si">{self.sobrenome}</span><span class="s1">'</span>
+
+
+<span class="k">class</span> <span class="nc">Japones</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="nd">@property</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.sobrenome}</span><span class="s1"> </span><span class="si">{self.nome}</span><span class="s1">'</span>
+</pre></div>
+
+
+<h2>Considerações</h2>
+<p>Diferente de algumas linguagens que ocultam as variáveis dos objetos, permitindo o seu acesso apenas através de funções, Python seguem no sentido contrário, acessando as funções de <em>getter</em> e <em>setter</em> como se fossem variáveis, isso permite começar com uma classe simples e ir adicionando funcionalidades conforme elas forem necessárias, sem precisar mudar o código das demais partes da aplicação, além de deixar transparente para quem desenvolve, não sendo necessário lembrar se precisa usar <em>getteres</em> e <em>setteres</em> ou não.</p>
+<p>De forma geral, programação orientada a objetos consiste em seguir determinados padrões de código, e as linguagens que implementam esse paradigma oferecem facilidades para escrever código seguindo esses padrões, e às vezes até ocultando detalhes complexos de suas implementações. Nesse contexto, eu recomendo a palestra do autor do <a href="https://htop.dev/">htop</a> feita no FISL 16, onde ele comenta como usou <a href="http://hemingway.softwarelivre.org/fisl16/high/41f/sala_41f-high-201507091200.ogv">orientação a objetos em C</a>. E para quem ainda quiser se aprofundar no assunto de orientação a objetos no Python, recomendo os vídeos do <a href="https://www.youtube.com/playlist?list=PLOQgLBuj2-3L_L6ahsBVA_SzuGtKre3OK">Eduardo Mendes</a> (também conhecido como dunossauro).</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ Mon, 17 May 2021 21:00:00 +0000
+
+
+ Blog do PauloHRPinheiro: Iniciando com o ORM Pony no Python III - Erros e Exceções
+ https://paulohrpinheiro.xyz/texts/python/2021-05-16-iniciando-com-o-orm-pony-no-python-iii-erros-e-excecoes.html
+ https://paulohrpinheiro.xyz/texts/python/2021-05-16-iniciando-com-o-orm-pony-no-python-iii-erros-e-excecoes.html
+ Seguindo o trilha com o ORM Pony, neste terceiro texto, agora começaremos a gerar situações de erro e ver o que os objetos retornam ou que exceções são geradas.
+ Sun, 16 May 2021 00:00:00 +0000
+
+
+ PythonClub: Orientação a objetos de outra forma: ABC
+ tag:pythonclub.com.br,2021-05-10:/oo-de-outra-forma-5.html
+ https://pythonclub.com.br/oo-de-outra-forma-5.html
+ <p>Na discussão sobre <a href="https://dev.to/acaverna/orientacao-a-objetos-de-outra-forma-heranca-multiplas-e-mixins-31eb">herança e mixins</a> foram criadas várias classes, como <code>Autenticavel</code> e <code>AutenticavelComRegistro</code> que adicionam funcionalidades a outras classes e implementavam tudo o que precisavam para seu funcionamento. Entretanto podem existir casos em que não seja possível implementar todas as funções na própria classe, deixando com que as classes que a estende implemente essas funções. Uma forma de fazer isso é través das <a href="https://docs.python.org/pt-br/3/library/abc.html">ABC</a> (<em>abstract base classes</em>, ou classes base abstratas).</p>
+<h2>Sem uso de classes base abstratas</h2>
+<p>Um exemplo de classe que não é possível implementar todas as funcionalidades foi dada no texto <a href="https://dev.to/acaverna/encapsulamento-da-logica-do-algoritmo-298e">Encapsulamento da lógica do algoritmo</a>, que discutia a leitura de valores do teclado até que um valor válido fosse lido (ou que repete a leitura caso um valor inválido tivesse sido informado). Nesse caso a classe <code>ValidaInput</code> implementava a lógica base de funcionamento, porém eram suas classes filhas (<code>ValidaNomeInput</code> e <code>ValidaNotaInput</code>) que implementavam as funções para tratar o que foi lido do teclado e verificar se é um valor válido ou não.</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ValidaInput</span><span class="p">:</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Valor inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">raise</span> <span class="ne">NotImplementedError</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">raise</span> <span class="ne">NotImplementedError</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">valor</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNomeInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nome inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">entrada</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">title</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">valor</span> <span class="o">!=</span> <span class="s1">''</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNotaInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nota inválida!'</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">entrada</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">valor</span> <span class="o"><=</span> <span class="mi">10</span>
+</pre></div>
+
+
+<p>Entretanto, esse código permite a criação de objetos da classe <code>ValidaInput</code> mesmo sem ter uma implementação das funções <code>transformar_entrada</code> e <code>validar_valor</code>. E a única mensagem de erro ocorreria ao tentar executar essas funções, o que poderia estar longe do problema real, que é a criação de um objeto a partir de uma classe que não prove todas as implementações das suas funções, o que seria semelhante a uma classe abstrata em outras linguagens.</p>
+<div class="highlight"><pre><span></span><span class="n">obj</span> <span class="o">=</span> <span class="n">ValidaInput</span><span class="p">()</span>
+
+<span class="c1"># Diversas linhas de código</span>
+
+<span class="n">obj</span><span class="p">(</span><span class="s1">'Entrada: '</span><span class="p">)</span> <span class="c1"># Exceção NotImplementedError lançada</span>
+</pre></div>
+
+
+<h2>Com uso de classes base abstratas</h2>
+<p>Seguindo a documentação da <a href="https://docs.python.org/pt-br/3/library/abc.html">ABC</a>, para utilizá-las é necessário informar a metaclasse <code>ABCMeta</code> na criação da classe, ou simplesmente estender a classe <code>ABC</code>, e decorar com <code>abstractmethod</code> as funções que as classes que a estenderem deverão implementar. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaInput</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Valor inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="o">...</span>
+
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="o">...</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">valor</span>
+</pre></div>
+
+
+<p>Desta forma, ocorrerá um erro já ao tentar criar um objeto do tipo <code>ValidaInput</code>, dizendo quais são as funções que precisam ser implementadas. Porém funcionará normalmente ao criar objetos a partir das classes <code>ValidaNomeInput</code> e <code>ValidaNotaInput</code> visto que elas implementam essas funções.</p>
+<div class="highlight"><pre><span></span><span class="n">obj</span> <span class="o">=</span> <span class="n">ValidaInput</span><span class="p">()</span> <span class="c1"># Exceção TypeError lançada</span>
+
+<span class="n">nome_input</span> <span class="o">=</span> <span class="n">ValidaNomeInput</span><span class="p">()</span> <span class="c1"># Objeto criado</span>
+<span class="n">nota_input</span> <span class="o">=</span> <span class="n">ValidaNotaInput</span><span class="p">()</span> <span class="c1"># Objeto criado</span>
+</pre></div>
+
+
+<p>Como essas funções não utilizam a referência ao objeto (<code>self</code>), ainda é possível decorar as funções com <code>staticmethod</code>, como:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaInput</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Valor inválido!'</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="n">entrada</span><span class="p">):</span>
+ <span class="o">...</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="o">...</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">valor</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNomeInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nome inválido!'</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">entrada</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">title</span><span class="p">()</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">valor</span> <span class="o">!=</span> <span class="s1">''</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNotaInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nota inválida!'</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">entrada</span><span class="p">)</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">valor</span> <span class="o"><=</span> <span class="mi">10</span>
+</pre></div>
+
+
+<p>Isso também seria válido para funções decoradas com <code>classmethod</code>, que receberiam a referência a classe (<code>cls</code>).</p>
+<h2>Considerações</h2>
+<p>Não é necessário utilizar ABC para fazer o exemplo discutido, porém ao utilizar essa biblioteca ficou mais explícito quais as funções que precisavam ser implementados nas classes filhas, ainda mais que sem utilizar ABC a classe base poderia nem ter as funções, com:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ValidaInput</span><span class="p">:</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Valor inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">valor</span>
+</pre></div>
+
+
+<p>Como Python possui <a href="https://docs.python.org/pt-br/3/glossary.html#term-duck-typing">duck-typing</a>, não é necessário uma grande preocupação com os tipos, como definir e utilizar interfaces presentes em outras implementações de orientação a objetos, porém devido à herança múltipla, ABC pode ser utilizada como interface que não existe em Python, fazendo com que as classes implementem determinadas funções. Para mais a respeito desse assunto, recomendo as duas lives do dunossauro sobre ABC (<a href="https://www.youtube.com/watch?v=yLHV1__nZZw">1</a> e <a href="https://www.youtube.com/watch?v=erAXvsuihPQ">2</a>), e a apresentação do Luciano Ramalho sobre <a href="https://www.youtube.com/watch?v=AJK2LqrlnTE">type hints</a>.</p>
+<p>Uma classe filha também não é obrigada a implementar todas as funções decoradas com <code>abstractmethod</code>, mas assim como a classe pai, não será possível criar objetos a partir dessa classe, apenas de uma classe filha dela que implemente as demais funções. Como se ao aplicar um <code>abstractmethod</code> tornasse a classe abstrata, e qualquer classe filha só deixasse de ser abstrata quando a última função decorada com <code>abstractmethod</code> for sobrescrita. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
+
+
+<span class="k">class</span> <span class="nc">A</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">func1</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="o">...</span>
+
+ <span class="nd">@abstractmethod</span>
+ <span class="k">def</span> <span class="nf">func2</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="k">class</span> <span class="nc">B</span><span class="p">(</span><span class="n">A</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">func1</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'1'</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">C</span><span class="p">(</span><span class="n">B</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">func2</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'2'</span><span class="p">)</span>
+
+
+<span class="n">a</span> <span class="o">=</span> <span class="n">A</span><span class="p">()</span> <span class="c1"># Erro por não implementar func1 e func2</span>
+<span class="n">b</span> <span class="o">=</span> <span class="n">B</span><span class="p">()</span> <span class="c1"># Erro por não implementar func2</span>
+<span class="n">c</span> <span class="o">=</span> <span class="n">C</span><span class="p">()</span> <span class="c1"># Objeto criado</span>
+</pre></div>
+
+
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ Mon, 10 May 2021 15:00:00 +0000
+
+
+ PythonClub: Orientação a objetos de outra forma: Herança múltiplas e mixins
+ tag:pythonclub.com.br,2021-05-03:/oo-de-outra-forma-4.html
+ https://pythonclub.com.br/oo-de-outra-forma-4.html
+ <p>No <a href="https://dev.to/acaverna/orientacao-a-objetos-de-outra-forma-heranca-3dm7">texto anterior</a> foi apresentando o conceito de herança, que herda toda a estrutura e comportamento de uma classe, podendo estendê-la com outros atributos e comportamentos. Esse texto apresentará a ideia de <a href="https://pt.wikipedia.org/wiki/Heran%C3%A7a_m%C3%BAltipla">herança múltipla</a>, e uma forma para se aproveitar esse recurso, através de mixins.</p>
+<h2>Herança múltiplas</h2>
+<p>Voltando ao sistema para lidar com dados das pessoas, onde algumas dessas pessoas possuem a possibilidade de acessar o sistema através de usuário e senha, também deseja-se permitir que outros sistemas autentiquem e tenham acesso os dados através de uma <a href="https://pt.wikipedia.org/wiki/Interface_de_programa%C3%A7%C3%A3o_de_aplica%C3%A7%C3%B5es">API</a>. Isso pode ser feito criando uma classe para representar os sistemas que terão permissão para acessar os dados. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Sistema</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">=</span> <span class="n">senha</span>
+
+ <span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">==</span> <span class="n">usuario</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">==</span> <span class="n">senha</span>
+</pre></div>
+
+
+<p>Porém, esse código repete a implementação feita para <code>PessoaAutenticavel</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">PessoaAutenticavel</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">=</span> <span class="n">senha</span>
+
+ <span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">==</span> <span class="n">usuario</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">==</span> <span class="n">senha</span>
+</pre></div>
+
+
+<p>Aproveitando que Python, diferente de outras linguagens, possui herança múltipla, é possível extrair essa lógica das classes, centralizando a implementação em uma outra classe e simplesmente herdá-la. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Autenticavel</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">=</span> <span class="n">senha</span>
+
+ <span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">==</span> <span class="n">usuario</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">==</span> <span class="n">senha</span>
+
+
+<span class="k">class</span> <span class="nc">PessoaAutenticavel</span><span class="p">(</span><span class="n">Autenticavel</span><span class="p">,</span> <span class="n">Pessoa</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="k">class</span> <span class="nc">Sistema</span><span class="p">(</span><span class="n">Autenticavel</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="n">p</span> <span class="o">=</span> <span class="n">PessoaAutenticavel</span><span class="p">(</span><span class="n">nome</span><span class="o">=</span><span class="s1">'João'</span><span class="p">,</span> <span class="n">sobrenome</span><span class="o">=</span><span class="s1">'da Silva'</span><span class="p">,</span> <span class="n">idade</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span>
+ <span class="n">usuario</span><span class="o">=</span><span class="s1">'joao'</span><span class="p">,</span> <span class="n">senha</span><span class="o">=</span><span class="s1">'secreta'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>A primeira coisa a ser observada são os argumentos <code>*args</code> e <code>**kwargs</code> no <code>__init__</code> da classe <code>Autenticavel</code>, eles são usados uma vez que não se sabe todos os argumentos que o <code>__init__</code> da classe que estenderá o <code>Autenticavel</code> espera receber, funcionando de forma dinâmica (mais sobre esse recurso pode ser visto na <a href="https://docs.python.org/pt-br/3/tutorial/controlflow.html#more-on-defining-functions">documentação do Python</a>).</p>
+<p>A segunda coisa a ser verificada é que para a classe <code>PessoaAutenticavel</code>, agora cria em seus objetos, a estrutura tanto da classe <code>Pessoa</code>, quanto <code>Autenticavel</code>. Algo similar a versão sem orientação a objetos a baixo:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa_autenticavel.py</span>
+
+<span class="kn">import</span> <span class="nn">autenticavel</span>
+<span class="kn">import</span> <span class="nn">pessoa</span>
+
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="n">autenticavel</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Também vale observar que as classes <code>PessoaAutenticavel</code> e <code>Sistema</code> não precisam definir nenhuma função, uma vez que elas cumprem seus papéis apenas herdando outras classes, porém seria possível implementar funções específicas dessas classes, assim como sobrescrever as funções definidas por outras classes.</p>
+<h2>Ordem de resolução de métodos</h2>
+<p>Embora herança múltiplas sejam interessantes, existe um problema, se ambas as classes pai possuírem uma função com um mesmo nome, a classe filha deveria chamar qual das funções? A do primeiro pai? A do último? Para lidar com esse problema o Python usa o MRO (<em>method resolution order</em>, ordem de resolução do método), que consiste em uma tupla com a ordem de qual classe o Python usará para encontrar o método a ser chamado. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="n">PessoaAutenticavel</span><span class="o">.</span><span class="vm">__mro__</span><span class="p">)</span>
+<span class="c1"># (<class '__main__.PessoaAutenticavel'>, <class '__main__.Autenticavel'>, <class '__main__.Pessoa'>, <class 'object'>)</span>
+</pre></div>
+
+
+<p>Por esse motivo que também foi possível chamar o <code>super().__init__</code> dentro de <code>Autenticavel</code>, que devido ao MRO, o Python chama o <code>__init__</code> da outra classe pai da classe que estendeu <code>Autenticavel</code>, em vez de precisar fazer um método <code>__init__</code> em <code>PessoaAutenticavel</code> chamando o <code>__init__</code> de todas as suas classes pais, como foi feito na versão sem orientação a objetos. E por isso a ordem <code>Autenticavel</code> e <code>Pessoa</code> na herança de <code>PessoaAutenticavel</code>, para fazer o MRO procurar os métodos primeiro em <code>Autenticavel</code> e depois em <code>Pessoa</code>.</p>
+<p>Para tentar fugir da complexidade que pode ser herança múltipla, é possível escrever classes que tem por objetivo unicamente incluir alguma funcionalidade em outra, como o caso da classe <code>Autenticavel</code>, que pode ser herdada por qualquer outra classe do sistema para permitir o acesso ao sistema. Essas classes recebem o nome de mixins, e adiciona uma funcionalidade bem definida.</p>
+<h2>Estendendo mixins</h2>
+<p>Imagine se além de permitir o acesso ao sistema, também gostaríamos de registrar algumas tentativas de acesso, informando quando houve a tentativa e se o acesso foi concedido ou não. Como <code>Autenticavel</code> é uma classe, é possível extendê-la para implementar essa funcionalidade na função <code>autenticar</code>. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
+
+
+<span class="k">class</span> <span class="nc">AutenticavelComRegistro</span><span class="p">(</span><span class="n">Autenticavel</span><span class="p">):</span>
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">_get_data</span><span class="p">():</span>
+ <span class="k">return</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'</span><span class="si">%d</span><span class="s1">/%m/%Y %T'</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'{self._get_data()} Tentativa de acesso de </span><span class="si">{usuario}</span><span class="s1">'</span><span class="p">)</span>
+ <span class="n">acesso</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">acesso</span><span class="p">:</span>
+ <span class="n">acesso_str</span> <span class="o">=</span> <span class="s1">'permitido'</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="n">acesso_str</span> <span class="o">=</span> <span class="s1">'negado'</span>
+ <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'{self._get_data()} Acesso de </span><span class="si">{usuario}</span><span class="s1"> </span><span class="si">{acesso_str}</span><span class="s1">'</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">acesso</span>
+
+
+<span class="k">class</span> <span class="nc">PessoaAutenticavelComRegistro</span><span class="p">(</span><span class="n">AutenticavelComRegistro</span><span class="p">,</span> <span class="n">Pessoa</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="k">class</span> <span class="nc">SistemaAutenticavelComRegistro</span><span class="p">(</span><span class="n">AutenticavelComRegistro</span><span class="p">,</span> <span class="n">Sistema</span><span class="p">):</span>
+ <span class="o">...</span>
+
+
+<span class="n">p</span> <span class="o">=</span> <span class="n">PessoaAutenticavelComRegistro</span><span class="p">(</span>
+ <span class="n">nome</span><span class="o">=</span><span class="s1">'João'</span><span class="p">,</span> <span class="n">sobrenome</span><span class="o">=</span><span class="s1">'da Silva'</span><span class="p">,</span> <span class="n">idade</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span>
+ <span class="n">usuario</span><span class="o">=</span><span class="s1">'joao'</span><span class="p">,</span> <span class="n">senha</span><span class="o">=</span><span class="s1">'secreta'</span><span class="p">,</span>
+<span class="p">)</span>
+<span class="n">p</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">)</span>
+<span class="c1"># Saída na tela:</span>
+<span class="c1"># 23/04/2021 16:56:58 Tentativa de acesso de joao</span>
+<span class="c1"># 23/04/2021 16:56:58 Acesso de joao permitido</span>
+</pre></div>
+
+
+<p>Essa implementação utiliza-se do <code>super()</code> para acessar a função <code>autenticar</code> da classe <code>Autenticavel</code> para não precisar reimplementar a autenticação. Porém, antes de chamá-la, manipula seus argumentos para registrar quem tentou acessar o sistema, assim como também manipula o seu retorno para registrar se o acesso foi permitido ou não.</p>
+<p>Essa classe também permite analisar melhor a ordem em que as classes são consultadas quando uma função é chamada:</p>
+<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="n">PessoaAutenticavelComRegistro</span><span class="o">.</span><span class="vm">__mro__</span><span class="p">)</span>
+<span class="c1"># (<class '__main__.PessoaAutenticavelComRegistro'>, <class '__main__.AutenticavelComRegistro'>, <class '__main__.Autenticavel'>, <class '__main__.Pessoa'>, <class 'object'>)</span>
+</pre></div>
+
+
+<p>Que também pode ser visto na forma de um digrama de classes:</p>
+<p><img alt="Diagrama de classes" src="https://pythonclub.com.br/feeds/images/eduardoklosowski/oo-de-outra-forma-4/mro.png" /></p>
+<p>Onde é feito uma <a href="https://pt.wikipedia.org/wiki/Busca_em_profundidade">busca em profundidade</a>, como se a função fosse chamada no primeiro pai, e só se ela não for encontrada, busca-se no segundo pai e assim por diante. Também é possível observar a classe <code>object</code>, que sempre será a última classe, e é a classe pai de todas as outras classes do Python quando elas não possuirem um pai declarado explicitamente.</p>
+<h2>Considerações</h2>
+<p>Herança múltipla pode dificultar bastante o entendimento do código, principalmente para encontrar onde determinada função está definida, porém pode facilitar bastante o código. Um exemplo que usa bastante herança e mixins são as <em>views</em> baseadas em classe do django (<a href="https://docs.djangoproject.com/pt-br/3.2/topics/class-based-views/"><em>class-based views</em></a>), porém para facilitar a visualização existe o site <a href="https://ccbv.co.uk/">Classy Class-Based Views</a> que lista todas as classes, e os mixins utilizados em cada uma, como pode ser visto em "Ancestors" como na <a href="https://ccbv.co.uk/projects/Django/3.1/django.views.generic.edit/UpdateView/">UpdateView</a>, que é usado para criar uma página com formulário para editar um registro já existente no banco, assim ela usa mixins para pegar um objeto do banco (<code>SingleObjectMixin</code>), processar formulário baseado em uma tabela do banco (<code>ModelFormMixin</code>) e algumas outras funcionalidades necessárias para implementar essa página.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ Mon, 03 May 2021 18:00:00 +0000
+
+
+ PythonClub: Orientação a objetos de outra forma: Herança
+ tag:pythonclub.com.br,2021-04-26:/oo-de-outra-forma-3.html
+ https://pythonclub.com.br/oo-de-outra-forma-3.html
+ <p>Algo que ajuda no desenvolvimento é a reutilização de código. Em orientação a objetos, essa reutilização pode ocorrer através de herança, onde um objeto pode se comportar como um objeto da sua própria classe, como também da classe que herdou.</p>
+<h2>Adicionando funcionalidades</h2>
+<p>Uma das utilidades da herança é estender uma classe para adicionar funcionalidades. Pensando no contexto das postagens anteriores, poderíamos querer criar um usuário e senha para algumas pessoas poderem acessar o sistema. Isso poderia ser feito adicionando atributos usuário e senha para as pessoas, além de uma função para validar se os dados estão corretos, e assim permitir o acesso ao sistema. Porém isso não pode ser feito para todas as pessoas, e sim apenas para aqueles que possuem permissão de acesso.</p>
+<h3>Sem orientação a objetos</h3>
+<p>Voltando a solução com dicionários (sem utilizar orientação a objetos), isso consistiria em criar um dicionário com a estrutura de uma pessoa, e em seguida estender essa estrutura com os novos campos de usuário e senha nesse mesmo dicionário, algo como:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa.py</span>
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'nome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'sobrenome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'idade'</span><span class="p">]</span> <span class="o">=</span> <span class="n">idade</span>
+
+
+<span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="n">pessoa</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{pessoa['nome']}</span><span class="s2"> </span><span class="si">{pessoa['sobrenome']}</span><span class="s2">"</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa_autenticavel.py</span>
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'usuario'</span><span class="p">]</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'senha'</span><span class="p">]</span> <span class="o">=</span> <span class="n">senha</span>
+
+
+<span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">pessoa</span><span class="p">[</span><span class="s1">'usuario'</span><span class="p">]</span> <span class="o">==</span> <span class="n">usuario</span> <span class="ow">and</span> <span class="n">pessoa</span><span class="p">[</span><span class="s1">'senha'</span><span class="p">]</span> <span class="o">==</span> <span class="n">senha</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+<span class="kn">import</span> <span class="nn">pessoa_autenticavel</span>
+
+<span class="n">p</span> <span class="o">=</span> <span class="p">{}</span>
+<span class="n">pessoa</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">pessoa_autenticavel</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">pessoa</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">(</span><span class="n">p</span><span class="p">))</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">pessoa_autenticavel</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">))</span>
+</pre></div>
+
+
+<p>Porém nessa solução é possível que o programador esqueça de chamar as duas funções <code>init</code> diferentes, e como queremos que todo dicionário com a estrutura de <code>pessoa_autenticavel</code> contenha também a estrutura de <code>pessoa</code>, podemos chamar o <code>init</code> de pessoa dentro do <code>init</code> de <code>pessoa_autenticavel</code>:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa_autenticavel.py</span>
+
+<span class="kn">import</span> <span class="nn">pessoa</span>
+
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="n">p</span><span class="p">[</span><span class="s1">'usuario'</span><span class="p">]</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="n">p</span><span class="p">[</span><span class="s1">'senha'</span><span class="p">]</span> <span class="o">=</span> <span class="n">senha</span>
+
+
+<span class="o">...</span> <span class="c1"># Demais funções</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+<span class="kn">import</span> <span class="nn">pessoa_autenticavel</span>
+
+<span class="n">p</span> <span class="o">=</span> <span class="p">{}</span>
+<span class="n">pessoa_autenticavel</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">pessoa</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">(</span><span class="n">p</span><span class="p">))</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">pessoa_autenticavel</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">))</span>
+</pre></div>
+
+
+<p>Nesse caso foi necessário alterar o nome do argumento <code>pessoa</code> da função <code>pessoa_autenticavel.init</code> para não conflitar com o outro módulo importado com esse mesmo nome. Porém ao chamar um <code>init</code> dentro de outro, temos a garantia de que o dicionário será compatível tanto com a estrutura pedida para ser criada pelo programador, quanto pelas estruturas pais dela.</p>
+<h3>Com orientação a objetos</h3>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.nome}</span><span class="s1"> </span><span class="si">{self.sobrenome}</span><span class="s1">'</span>
+
+
+<span class="k">class</span> <span class="nc">PessoaAutenticavel</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="n">Pessoa</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">=</span> <span class="n">senha</span>
+
+ <span class="k">def</span> <span class="nf">autenticar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">==</span> <span class="n">usuario</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">==</span> <span class="n">senha</span>
+
+
+<span class="n">p</span> <span class="o">=</span> <span class="n">PessoaAutenticavel</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">Pessoa</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">(</span><span class="n">p</span><span class="p">))</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">PessoaAutenticavel</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">))</span>
+</pre></div>
+
+
+<p>A principal novidade desse exemplo é que ao declarar a classe <code>PessoaAutenticavel</code> (filha), foi declarado a classe <code>Pessoa</code> (pai) entre parênteses, isso faz o interpretador Python criar uma cópia dessa classe estendendo-a com as novas funções que estamos criando. Porém pode ser um pouco redundante chamar <code>Pessoa.__init__</code> dentro da função <code>__init__</code> sendo que já foi declarado que ela estende <code>Pessoa</code>, podendo ser trocado por <code>super()</code>, que aponta para a classe que foi estendida. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">PessoaAutenticavel</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">,</span> <span class="n">usuario</span><span class="p">,</span> <span class="n">senha</span><span class="p">):</span>
+ <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">usuario</span> <span class="o">=</span> <span class="n">usuario</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">senha</span> <span class="o">=</span> <span class="n">senha</span>
+
+ <span class="o">...</span> <span class="c1"># Demais funções</span>
+</pre></div>
+
+
+<p>Assim se evita repetir o nome da classe, e já passa automaticamente a referência para <code>self</code>, assim como quando usamos o açúcar sintático apresentado na primeira postagem dessa série. E esse açúcar sintática também pode ser usado para chamar tanto as funções declaradas em <code>Pessoa</code> quanto em <code>PessoaAutenticavel</code>. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">p</span> <span class="o">=</span> <span class="n">PessoaAutenticavel</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">())</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">autenticar</span><span class="p">(</span><span class="s1">'joao'</span><span class="p">,</span> <span class="s1">'secreta'</span><span class="p">))</span>
+</pre></div>
+
+
+<p>Esse método também facilita a utilização das funções, uma vez que não é necessário lembrar em qual classe que cada função foi declarada. Na verdade, como <code>PessoaAutenticavel</code> estende <code>Pessoa</code>, seria possível executar também <code>PessoaAutenticavel.nome_completo</code>, porém eles apontam para a mesma função.</p>
+<h2>Sobrescrevendo uma função</h2>
+<p>A classe <code>Pessoa</code> possui a função <code>nome_completo</code> que retorna uma <code>str</code> contento nome e sobrenome. Porém no Japão, assim como em outros países asiáticos, o sobrenome vem primeiro, e até <a href="https://noticias.uol.com.br/ultimas-noticias/efe/2019/06/24/japao-quer-voltar-a-ordem-tradicional-dos-nomes-abe-shinzo-nao-shinzo-abe.htm">estão pedindo para seguir a tradição deles ao falarem os nomes de japoneses</a>, como o caso do primeiro-ministro, mudando de Shinzo Abe para Abe Shinzo.</p>
+<h3>Com orientação a objetos</h3>
+<p>Isso também pode ser feito no sistema usando herança, porém em vez de criar uma nova função com outro nome, é possível criar uma função com o mesmo nome, sobrescrevendo a anterior, porém apenas para os objetos da classe filha. Algo semelhante ao que já foi feito com a função <code>__init__</code>. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Japones</span><span class="p">(</span><span class="n">Pessoa</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.sobrenome}</span><span class="s1"> </span><span class="si">{self.nome}</span><span class="s1">'</span>
+
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">p2</span> <span class="o">=</span> <span class="n">Japones</span><span class="p">(</span><span class="s1">'Shinzo'</span><span class="p">,</span> <span class="s1">'Abe'</span><span class="p">,</span> <span class="mi">66</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">())</span> <span class="c1"># João da Silva</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p2</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">())</span> <span class="c1"># Abe Shinzo</span>
+</pre></div>
+
+
+<p>Essa relação de herança traz algo interessante, todo objeto da classe <code>Japones</code> se comporta como um objeto da classe <code>Pessoa</code>, porém a relação inversa não é verdade. Assim como podemos dizer que todo japonês é uma pessoa, mas nem todas as pessoas são japonesas. Ser japonês é um caso mais específico de pessoa, assim como as demais nacionalidades.</p>
+<h3>Sem orientação a objetos</h3>
+<p>Esse comportamento de sobrescrever a função <code>nome_completo</code> não é tão simples de replicar em uma estrutura de dicionário, porém é possível fazer. Porém como uma pessoa pode ser tanto japonês quanto não ser, não é possível saber de antemão para escrever no código <code>pessoa.nome_completo</code> ou <code>japones.nome_completo</code>, que diferente do exemplo da autenticação, agora são duas funções diferentes, isso precisa ser descoberto dinamicamente quando se precisar chamar a função.</p>
+<p>Uma forma de fazer isso é guardar uma referência para a função que deve ser chamada dentro da própria estrutura. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa.py</span>
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'nome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'sobrenome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'idade'</span><span class="p">]</span> <span class="o">=</span> <span class="n">idade</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'nome_completo'</span><span class="p">]</span> <span class="o">=</span> <span class="n">nome_completo</span>
+
+
+<span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="n">pessoa</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{pessoa['nome']}</span><span class="s2"> </span><span class="si">{pessoa['sobrenome']}</span><span class="s2">"</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: japones.py</span>
+
+<span class="kn">import</span> <span class="nn">pessoa</span>
+
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">japones</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">(</span><span class="n">japones</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="n">japones</span><span class="p">[</span><span class="s1">'nome_completo'</span><span class="p">]</span> <span class="o">=</span> <span class="n">nome_completo</span>
+
+
+<span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="n">japones</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{pessoa['sobrenome']}</span><span class="s2"> </span><span class="si">{pessoa['nome']}</span><span class="s2">"</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+<span class="kn">import</span> <span class="nn">japones</span>
+
+<span class="n">p1</span> <span class="o">=</span> <span class="p">{}</span>
+<span class="n">pessoa</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">p2</span> <span class="o">=</span> <span class="p">{}</span>
+<span class="n">japones</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">p2</span><span class="p">,</span> <span class="s1">'Shinzo'</span><span class="p">,</span> <span class="s1">'Abe'</span><span class="p">,</span> <span class="mi">66</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="p">[</span><span class="s1">'nome_completo'</span><span class="p">](</span><span class="n">p1</span><span class="p">))</span> <span class="c1"># João da Silva</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p2</span><span class="p">[</span><span class="s1">'nome_completo'</span><span class="p">](</span><span class="n">p2</span><span class="p">))</span> <span class="c1"># Abe Shinzo</span>
+</pre></div>
+
+
+<p>Perceba que a forma de chamar a função foi alterada. O que acontece na prática é que toda função que pode ser sobrescrita não é chamada diretamente, e sim a partir de uma referência, e isso gera um custo computacional adicional. Como esse custo não é tão alto (muitas vezes sendo quase irrelevante), esse é o comportamento adotado em várias linguagens, porém em C++, por exemplo, existe a palavra-chave <code>virtual</code> para descrever quando uma função pode ser sobrescrita ou não.</p>
+<h2>Considerações</h2>
+<p>Herança é um mecanismo interessante para ser explorado com o objetivo de reaproveitar código e evitar repeti-lo. Porém isso pode vir com alguns custos, seja computacional durante sua execução, seja durante a leitura do código, sendo necessário verificar diversas classes para saber o que de fato está sendo executado, porém isso também pode ser usado para ocultar e abstrair lógicas mais complicadas, como eu já comentei em outra <a href="https://dev.to/acaverna/encapsulamento-da-logica-do-algoritmo-298e">postagem</a>.</p>
+<p>Herança também permite trabalhar com generalização e especialização, podendo descrever o comportamento mais geral, ou mais específico. Ou simplesmente só adicionar mais funcionalidades a uma classe já existente.</p>
+<p>Assim como foi utilizado o <code>super()</code> para chamar a função <code>__init__</code> da classe pai, é possível utilizá-lo para chamar qualquer outra função. Isso permite, por exemplo, tratar os argumentos da função, aplicando modificações antes de chamar a função original, ou seu retorno, executando algum processamento em cima do retorno dela, não precisando rescrever toda a função.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ Mon, 26 Apr 2021 20:00:00 +0000
+
+
+ PythonClub: Orientação a objetos de outra forma: Métodos estáticos e de classes
+ tag:pythonclub.com.br,2021-04-19:/oo-de-outra-forma-2.html
+ https://pythonclub.com.br/oo-de-outra-forma-2.html
+ <p>Na <a href="https://dev.to/acaverna/orientacao-a-objetos-de-outra-forma-classes-e-objetos-3mfd">postagem anterior</a> foi apresentado o <code>self</code>, nessa postagem será discutido mais a respeito desse argumento, considerando opções para ele e suas aplicações.</p>
+<h2>Métodos estáticos</h2>
+<p>Nem todas as funções de uma classe precisam receber uma referência de um objeto para lê-lo ou alterá-lo, muitas vezes uma função pode fazer o seu papel apenas com os dados passados como argumento, por exemplo, receber um nome e validar se ele possui pelo menos três caracteres sem espaço. Dessa forma, essa função poderia ser colocada fora do escopo da classe, porém para facilitar sua chamada, e possíveis alterações (que será discutido em outra postagem), é possível colocar essa função dentro da classe e informar que ela não receberá o argumento <code>self</code> com o decorador <code>@staticmethod</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="o">...</span> <span class="c1"># Demais funções</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">valida_nome</span><span class="p">(</span><span class="n">nome</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">nome</span><span class="p">)</span> <span class="o">>=</span> <span class="mi">3</span> <span class="ow">and</span> <span class="s1">' '</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">nome</span>
+</pre></div>
+
+
+<p>Dessa forma, essa função pode ser chamada diretamente de um objeto pessoa, ou até mesmo diretamente da classe, sem precisar criar um objeto primeiro:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Chamando diretamente da classe</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">Pessoa</span><span class="o">.</span><span class="n">valida_nome</span><span class="p">(</span><span class="s1">'João'</span><span class="p">))</span>
+
+<span class="c1"># Chamando através de um objeto do tipo Pessoa</span>
+<span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">valida_nome</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">nome</span><span class="p">))</span>
+</pre></div>
+
+
+<p>E essa função também pode ser utilizada dendro de outras funções, como validar o nome na criação de uma pessoa, de forma que caso o nome informado seja válido, será criado um objeto do tipo Pessoa, e caso o nome seja inválido, será lançado uma exceção:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">valida_nome</span><span class="p">(</span><span class="n">nome</span><span class="p">):</span>
+ <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s1">'Nome inválido'</span><span class="p">)</span>
+
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+ <span class="o">...</span> <span class="c1"># Demais funções</span>
+
+ <span class="nd">@staticmethod</span>
+ <span class="k">def</span> <span class="nf">valida_nome</span><span class="p">(</span><span class="n">nome</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">nome</span><span class="p">)</span> <span class="o">>=</span> <span class="mi">3</span> <span class="ow">and</span> <span class="s1">' '</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">nome</span>
+
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span> <span class="c1"># Cria objeto</span>
+<span class="n">p2</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'a'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span> <span class="c1"># Lança ValueError: Nome inválido</span>
+</pre></div>
+
+
+<h2>Métodos da classe</h2>
+<p>Entretanto algumas funções podem precisar de um meio termo, necessitar acessar o contexto da classe, porém sem necessitar de um objeto. Isso é feito através do decorador <code>@classmethod</code>, onde a função decorada com ele, em vez de receber um objeto como primeiro argumento, recebe a própria classe.</p>
+<p>Para demonstrar essa funcionalidade será implementado um <em>id</em> auto incremental para os objetos da classe <code>Pessoa</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="n">total_de_pessoas</span> <span class="o">=</span> <span class="mi">0</span>
+
+ <span class="nd">@classmethod</span>
+ <span class="k">def</span> <span class="nf">novo_id</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span>
+ <span class="bp">cls</span><span class="o">.</span><span class="n">total_de_pessoas</span> <span class="o">+=</span> <span class="mi">1</span>
+ <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">total_de_pessoas</span>
+
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">id</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">novo_id</span><span class="p">()</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">id</span><span class="p">)</span> <span class="c1"># Imprime 1</span>
+<span class="n">p2</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'Maria'</span><span class="p">,</span> <span class="s1">'dos Santos'</span><span class="p">,</span> <span class="mi">18</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p2</span><span class="o">.</span><span class="n">id</span><span class="p">)</span> <span class="c1"># Imprime 2</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">Pessoa</span><span class="o">.</span><span class="n">total_de_pessoas</span><span class="p">)</span> <span class="c1"># Imprime 2</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">total_de_pessoas</span><span class="p">)</span> <span class="c1"># Imprime 2</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p2</span><span class="o">.</span><span class="n">total_de_pessoas</span><span class="p">)</span> <span class="c1"># Imprime 2</span>
+</pre></div>
+
+
+<p>Nesse código é criado uma variável <code>total_de_pessoas</code> dentro do escopo da classe <code>Pessoas</code>, e que é compartilhado tanto pela classe, como pelos objetos dessa classe, diferente de declará-la com <code>self.</code> dentro do <code>__init__</code>, onde esse valor pertenceria apenas ao objeto, e não é compartilhado com os demais objetos. Declarar variáveis dentro do contexto da classe é similar ao se declarar variáveis com <code>static</code> em outras linguagens, assim como o <code>@classmethod</code> é semelhante a declaração de funções com <code>static</code>.</p>
+<p>As funções declaradas com <code>@classmethod</code> também podem ser chamadas sem a necessidade de se criar um objeto, como <code>Pessoa.novo_id()</code>, embora que para essa função específica isso não faça muito sentido, ou receber outros argumentos, tudo depende do que essa função fará.</p>
+<h2>Considerações</h2>
+<p>Embora possa parecer confuso identificar a diferença de uma função de um objeto (função sem decorador), função de uma classe (com decorador <code>@classmethod</code>) e função sem acesso a nenhum outro contexto (com decorador <code>@staticmethod</code>), essa diferença fica mais clara ao se analisar o primeiro argumento recebido por cada tipo de função. Podendo ser a referência a um objeto (<code>self</code>) e assim necessitando que um objeto seja criado anteriormente, ser uma classe (<code>cls</code>) e não necessitando receber um objeto, ou simplesmente não recebendo nenhum argumento especial, apenas os demais argumentos necessários para a função. Sendo diferenciados pelo uso dos decoradores.</p>
+<p>Na orientação a objetos implementada pelo Python, algumas coisas podem ficar confusas quando se mistura com nomenclaturas de outras linguagens que possuem implementações diferentes. A linguagem Java, por exemplo, utiliza a palavra-chave <code>static</code> para definir os atributos e métodos de classe, enquanto no Python um método estático é aquele que não acessa nem um objeto, nem uma classe, devendo ser utilizado o escopo da classe e o decorador <code>@classmethod</code> para se criar atributos e métodos da classe.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ Mon, 19 Apr 2021 20:00:00 +0000
+
+
+ PythonClub: Orientação a objetos de outra forma: Classes e objetos
+ tag:pythonclub.com.br,2021-04-12:/oo-de-outra-forma-1.html
+ https://pythonclub.com.br/oo-de-outra-forma-1.html
+ <p>Nas poucas e raríssimas lives que eu fiz na <a href="https://www.twitch.tv/eduardoklosowski">Twitch</a>, surgiu a ideia de escrever sobre programação orientada a objetos em <a href="https://www.python.org/">Python</a>, principalmente por algumas diferenças de como ela foi implementada nessa linguagem. Aproveitando o tema, vou fazer uma série de postagens dando uma visão diferente sobre orientação a objetos. E nessa primeira postagem falarei sobre classes e objetos.</p>
+<h2>Usando um dicionário</h2>
+<p>Entretanto, antes de começar com orientação a objetos, gostaria de apresentar e discutir alguns exemplos sem utilizar esse paradigma de programação.</p>
+<p>Pensando em um sistema que precise manipular dados de pessoas, é possível utilizar os <a href="https://docs.python.org/pt-br/3/library/stdtypes.html#mapping-types-dict">dicionários</a> do Python para agrupar os dados de uma pessoa em uma única variável, como no exemplo a baixo:</p>
+<div class="highlight"><pre><span></span><span class="n">pessoa</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'nome'</span><span class="p">:</span> <span class="s1">'João'</span><span class="p">,</span>
+ <span class="s1">'sobrenome'</span><span class="p">:</span> <span class="s1">'da Silva'</span><span class="p">,</span>
+ <span class="s1">'idade'</span><span class="p">:</span> <span class="mi">20</span><span class="p">,</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>Onde os dados poderiam ser acessados através da variável e do nome do dado desejado, como:</p>
+<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="n">pessoa</span><span class="p">[</span><span class="s1">'nome'</span><span class="p">])</span> <span class="c1"># Imprimindo João</span>
+</pre></div>
+
+
+<p>Assim, todos os dados de uma pessoa ficam agrupados em uma variável, o que facilita bastante a programação, visto que não é necessário criar uma variável para cada dado, e quando se manipula os dados de diferentes pessoas fica muito mais fácil identificar de qual pessoa aquele dado se refere, bastando utilizar variáveis diferentes.</p>
+<h3>Função para criar o dicionário</h3>
+<p>Apesar de prático, é necessário replicar essa estrutura de dicionário toda vez que se desejar utilizar os dados de uma nova pessoa. Para evitar a repetição de código, a criação desse dicionário pode ser feita dentro de uma função que pode ser colocada em um módulo <code>pessoa</code> (arquivo, nesse caso com o nome de <code>pessoa.py</code>):</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa.py</span>
+
+<span class="k">def</span> <span class="nf">nova</span><span class="p">(</span><span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="k">return</span> <span class="p">{</span>
+ <span class="s1">'nome'</span><span class="p">:</span> <span class="n">nome</span><span class="p">,</span>
+ <span class="s1">'sobrenome'</span><span class="p">:</span> <span class="n">sobrenome</span><span class="p">,</span>
+ <span class="s1">'idade'</span><span class="p">:</span> <span class="n">idade</span><span class="p">,</span>
+ <span class="p">}</span>
+</pre></div>
+
+
+<p>E para criar o dicionário que representa uma pessoa, basta importar esse módulo (arquivo) e chamar a função <code>nova</code>:</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">pessoa</span><span class="o">.</span><span class="n">nova</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">p2</span> <span class="o">=</span> <span class="n">pessoa</span><span class="o">.</span><span class="n">nova</span><span class="p">(</span><span class="s1">'Maria'</span><span class="p">,</span> <span class="s1">'dos Santos'</span><span class="p">,</span> <span class="mi">18</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Desta forma, garante-se que todos os dicionários representando pessoas terão os campos desejados e devidamente preenchidos.</p>
+<h3>Função com o dicionário</h3>
+<p>Também é possível criar algumas funções para executar operações com os dados desses dicionários, como pegar o nome completo da pessoa, trocar o seu sobrenome, ou fazer aniversário (o que aumentaria a idade da pessoa em um ano):</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa.py</span>
+
+<span class="k">def</span> <span class="nf">nova</span><span class="p">(</span><span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="o">...</span> <span class="c1"># Código abreviado</span>
+
+
+<span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="n">pessoa</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{pessoa['nome']}</span><span class="s2"> </span><span class="si">{pessoa['sobrenome']}</span><span class="s2">"</span>
+
+
+<span class="k">def</span> <span class="nf">trocar_sobrenome</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'sobrenome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">sobrenome</span>
+
+
+<span class="k">def</span> <span class="nf">fazer_aniversario</span><span class="p">(</span><span class="n">pessoa</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'idade'</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
+</pre></div>
+
+
+<p>E sendo usado como:</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">pessoa</span><span class="o">.</span><span class="n">nova</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">pessoa</span><span class="o">.</span><span class="n">trocar_sobrenome</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="s1">'dos Santos'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">pessoa</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">(</span><span class="n">p1</span><span class="p">))</span>
+<span class="n">pessoa</span><span class="o">.</span><span class="n">fazer_aniversario</span><span class="p">(</span><span class="n">p1</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="p">[</span><span class="s1">'idade'</span><span class="p">])</span>
+</pre></div>
+
+
+<p>Nesse caso, pode-se observar que todas as funções aqui implementadas seguem o padrão de receber o dicionário que representa a pessoa como primeiro argumento, podendo ter outros argumentos ou não conforme a necessidade, acessando e alterando os valores desse dicionário.</p>
+<h2>Versão com orientação a objetos</h2>
+<p>Antes de entrar na versão orientada a objetos propriamente dita dos exemplos anteriores, vou fazer uma pequena alteração para facilitar o entendimento posterior. A função <code>nova</code> será separada em duas partes, a primeira que criará um dicionário, e chamará uma segunda função (<code>init</code>), que receberá esse dicionário como primeiro argumento (seguindo o padrão das demais funções) e criará sua estrutura com os devidos valores.</p>
+<div class="highlight"><pre><span></span><span class="c1"># Arquivo: pessoa.py</span>
+
+<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'nome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'sobrenome'</span><span class="p">]</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="n">pessoa</span><span class="p">[</span><span class="s1">'idade'</span><span class="p">]</span> <span class="o">=</span> <span class="n">idade</span>
+
+
+<span class="k">def</span> <span class="nf">nova</span><span class="p">(</span><span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="n">pessoa</span> <span class="o">=</span> <span class="p">{}</span>
+ <span class="n">init</span><span class="p">(</span><span class="n">pessoa</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">pessoa</span>
+
+
+<span class="o">...</span> <span class="c1"># Demais funções do arquivo</span>
+</pre></div>
+
+
+<p>Porém isso não muda a forma de uso:</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pessoa</span>
+
+<span class="n">p1</span> <span class="o">=</span> <span class="n">pessoa</span><span class="o">.</span><span class="n">nova</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+</pre></div>
+
+
+<h3>Função para criar uma pessoa</h3>
+<p>A maioria das linguagens de programação que possuem o paradigma de programação orientado a objetos faz o uso de classes para definir a estrutura dos objetos. O Python também utiliza classes, que podem ser definidas com a palavra-chave <code>class</code> seguidas de um nome para ela. E dentro dessa estrutura, podem ser definidas funções para manipular os objetos daquela classe, que em algumas linguagens também são chamadas de métodos (funções declaradas dentro do escopo uma classe).</p>
+<p>Para converter o dicionário para uma classe, o primeiro passo é implementar uma função para criar a estrutura desejada. Essa função deve possui o nome <code>__init__</code>, e é bastante similar a função <code>init</code> do código anterior:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+</pre></div>
+
+
+<p>As diferenças são que agora o primeiro parâmetro se chama <code>self</code>, que é um padrão utilizado no Python, e em vez de usar colchetes e aspas para acessar os dados, aqui basta utilizar o ponto e o nome do dado desejado (que aqui também pode ser chamado de atributo, visto que é uma variável do objeto). A função <code>nova</code> implementada anteriormente não é necessária, a própria linguagem cria um objeto e passa ele como primeiro argumento para o <code>__init__</code>. E assim para se criar um objeto da classe <code>Pessoa</code> basta chamar a classe como se fosse uma função, ignorando o argumento <code>self</code> e informando os demais, como se estivesse chamando a função <code>__init__</code> diretamente:</p>
+<div class="highlight"><pre><span></span><span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'da Silva'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Nesse caso, como a própria classe cria um contexto diferente para as funções (escopo ou <em>namespace</em>), não está mais sendo utilizado arquivos diferentes, porém ainda é possível fazê-lo, sendo necessário apenas fazer o <code>import</code> adequado. Mas para simplificação, tanto a declaração da classe, como a criação do objeto da classe <code>Pessoa</code> podem ser feitas no mesmo arquivo, assim como os demais exemplos dessa postagem.</p>
+<h3>Outras funções</h3>
+<p>As demais funções feitas anteriormente para o dicionário também podem ser feitas na classe <code>Pessoa</code>, seguindo as mesmas diferenças já apontadas anteriormente:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pessoa</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">,</span> <span class="n">idade</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">nome</span> <span class="o">=</span> <span class="n">nome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">=</span> <span class="n">idade</span>
+
+ <span class="k">def</span> <span class="nf">nome_completo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{self.nome}</span><span class="s1"> </span><span class="si">{self.sobrenome}</span><span class="s1">'</span>
+
+ <span class="k">def</span> <span class="nf">trocar_sobrenome</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sobrenome</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">sobrenome</span> <span class="o">=</span> <span class="n">sobrenome</span>
+
+ <span class="k">def</span> <span class="nf">fazer_aniversario</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">idade</span> <span class="o">+=</span> <span class="mi">1</span>
+</pre></div>
+
+
+<p>Para se chamar essas funções, basta acessá-las através do contexto da classe, passando o objeto criado anteriormente como primeiro argumento:</p>
+<div class="highlight"><pre><span></span><span class="n">p1</span> <span class="o">=</span> <span class="n">Pessoa</span><span class="p">(</span><span class="s1">'João'</span><span class="p">,</span> <span class="s1">'dos Santos'</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
+<span class="n">Pessoa</span><span class="o">.</span><span class="n">trocar_sobrenome</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="s1">'dos Santos'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">Pessoa</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">(</span><span class="n">p1</span><span class="p">))</span>
+<span class="n">Pessoa</span><span class="o">.</span><span class="n">fazer_aniversario</span><span class="p">(</span><span class="n">p1</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">idade</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Essa sintaxe é bastante semelhante a versão sem orientação a objetos implementada anteriormente. Porém quando se está utilizando objetos, é possível chamar essas funções com uma outra sintaxe, informando primeiro o objeto, seguido de ponto e o nome da função desejada, com a diferença de que não é mais necessário informar o objeto como primeiro argumento. Como a função foi chamada através de um objeto, o próprio Python se encarrega de passá-lo para o argumento <code>self</code>, sendo necessário informar apenas os demais argumentos:</p>
+<div class="highlight"><pre><span></span><span class="n">p1</span><span class="o">.</span><span class="n">trocar_sobrenome</span><span class="p">(</span><span class="s1">'dos Santos'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">nome_completo</span><span class="p">())</span>
+<span class="n">p1</span><span class="o">.</span><span class="n">fazer_aniversario</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">idade</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Existem algumas diferenças entre as duas sintaxes, porém isso será tratado posteriormente. Por enquanto a segunda sintaxe pode ser vista como um <a href="https://pt.wikipedia.org/wiki/A%C3%A7%C3%BAcar_sint%C3%A1tico">açúcar sintático</a> da primeira, ou seja, uma forma mais rápida e fácil de fazer a mesma coisa que a primeira, e por isso sendo a recomendada.</p>
+<h2>Considerações</h2>
+<p>Como visto nos exemplos, programação orientada a objetos é uma técnica para juntar variáveis em uma mesma estrutura e facilitar a escrita de funções que seguem um determinado padrão, recebendo a estrutura como argumento, porém a sintaxe mais utilizada no Python para chamar as funções de um objeto (métodos) posiciona a variável que guarda a estrutura antes do nome da função, em vez do primeiro argumento.</p>
+<p>No Python, o argumento da estrutura ou objeto (<code>self</code>) aparece explicitamente como primeiro argumento da função, enquanto em outras linguagens essa variável pode receber outro nome (como <code>this</code>) e não aparece explicitamente nos argumentos da função, embora essa variável tenha que ser criada dentro do contexto da função para permitir manipular o objeto.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ Mon, 12 Apr 2021 18:00:00 +0000
+
+
+ PythonClub: Funções in place ou cópia de valor
+ tag:pythonclub.com.br,2021-03-29:/funcao-inplace-ou-copia-de-valor.html
+ https://pythonclub.com.br/funcao-inplace-ou-copia-de-valor.html
+ <p>Eventualmente observo dificuldades de algumas pessoas em usar corretamente alguma função, seja porque a função deveria ser executada isoladamente, e utilizado a própria variável que foi passada como argumento posteriormente, seja porque deveria se atribuir o retorno da função a alguma variável, e utilizar essa nova variável. No Python, essa diferença pode ser observada nos métodos das listas <code>sort</code> e <code>reverse</code> para as funções <code>sorted</code> e <code>reversed</code>, que são implementadas com padrões diferentes, <em>in place</em> e cópia de valor respectivamente. Assim pretendo discutir esses dois padrões de funções, comentando qual a diferença e o melhor caso de aplicação de cada padrão.</p>
+<h2>Função de exemplo</h2>
+<p>Para demonstrar como esses padrões funcionam, será implementado uma função que recebe uma lista e calcula o dobro dos valores dessa lista. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">entrada</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+
+<span class="c1"># Execução da função</span>
+
+<span class="n">resultado</span> <span class="o">=</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">8</span><span class="p">]</span>
+</pre></div>
+
+
+<h3>Função com in place</h3>
+<p>A ideia do padrão <em>in place</em> é alterar a própria variável recebida pela função (ou o próprio objeto, caso esteja lidando com orientação a objetos). Neste caso, bastaria calcular o dobro do valor de cada posição da lista, e sobrescrever a posição com seu resultado. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span>
+
+
+<span class="k">def</span> <span class="nf">dobro_inplace</span><span class="p">(</span><span class="n">lista</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
+ <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">lista</span><span class="p">)):</span>
+ <span class="n">lista</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">lista</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
+
+
+<span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">retorno</span> <span class="o">=</span> <span class="n">dobro_inplace</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: valores | Tipo: {type(valores)} | Valor: </span><span class="si">{valores}</span><span class="s1">'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: retorno | Tipo: {type(retorno)} | Valor: </span><span class="si">{retorno}</span><span class="s1">'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Resultado da execução:</p>
+<div class="highlight"><pre><span></span><span class="err">Variável: valores | Tipo: <class 'list'> | Valor: [10, 4, 16, 12, 8]</span>
+<span class="err">Variável: retorno | Tipo: <class 'NoneType'> | Valor: None</span>
+</pre></div>
+
+
+<p>Com essa execução é possível observar que os valores da lista foram alterados, e que o retorno da função é nulo (<code>None</code>), ou seja, a função alterou a própria lista passada como argumento. Outro ponto importante a ser observado é a assinatura da função (tipo dos argumentos e do retorno da função), que recebe uma lista de inteiros e não tem retorno ou é nulo (<code>None</code>). Dessa forma embora seja possível chamar essa função diretamente quando está se informando os argumentos de outra função, como <code>print(dobro_inplace(valores))</code>, a função <code>print</code> receberia <code>None</code> e não a lista como argumento.</p>
+<h3>Função com cópia de valor</h3>
+<p>A ideia do padrão cópia de valor é criar uma cópia do valor passado como argumento e retornar essa cópia, sem alterar a variável recebida (ou criando um novo objeto, no caso de orientação a objetos). Neste caso, é necessário criar uma nova lista e adicionar nela os valores calculados. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span>
+
+
+<span class="k">def</span> <span class="nf">dobro_copia</span><span class="p">(</span><span class="n">lista</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
+ <span class="n">nova_lista</span> <span class="o">=</span> <span class="p">[]</span>
+
+ <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">lista</span><span class="p">)):</span>
+ <span class="n">nova_lista</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">lista</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
+
+ <span class="k">return</span> <span class="n">nova_lista</span>
+
+
+<span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">retorno</span> <span class="o">=</span> <span class="n">dobro_copia</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: valores | Tipo: {type(valores)} | Valor: </span><span class="si">{valores}</span><span class="s1">'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: retorno | Tipo: {type(retorno)} | Valor: </span><span class="si">{retorno}</span><span class="s1">'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Resultado da execução:</p>
+<div class="highlight"><pre><span></span><span class="err">Variável: valores | Tipo: <class 'list'> | Valor: [5, 2, 8, 6, 4]</span>
+<span class="err">Variável: retorno | Tipo: <class 'list'> | Valor: [10, 4, 16, 12, 8]</span>
+</pre></div>
+
+
+<p>Com essa execução é possível observar que a variável <code>valores</code> continua com os valores que tinha antes da execução da função, e a variável retorno apresenta uma lista com os dobros, ou seja, a função não altera a lista passada como argumento e retorna uma nova lista com os valores calculados. Observado a assinatura da função, ela recebe uma lista de inteiros e retorna uma lista de inteiros. Isso permite chamar essa função diretamente nos argumentos para outra função, como <code>print(dobro_copia(valores))</code>, nesse caso a função <code>print</code> receberia a lista de dobros como argumento. Porém caso o retorno da função não seja armazenado, parecerá que a função não fez nada, ou não funcionou. Então em alguns casos, quando o valor anterior não é mais necessário, pode-se reatribuir o retorno da função a própria variável passada como argumento:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="n">dobro_copia</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+</pre></div>
+
+
+<h3>Função híbrida</h3>
+<p>Ainda é possível mesclar os dois padrões de função, alterando o valor passado e retornando-o. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span>
+
+
+<span class="k">def</span> <span class="nf">dobro_hibrido</span><span class="p">(</span><span class="n">lista</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
+ <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">lista</span><span class="p">)):</span>
+ <span class="n">lista</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">lista</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
+
+ <span class="k">return</span> <span class="n">lista</span>
+
+
+<span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">retorno</span> <span class="o">=</span> <span class="n">dobro_hibrido</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: valores | Tipo: {type(valores)} | Valor: </span><span class="si">{valores}</span><span class="s1">'</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Variável: retorno | Tipo: {type(retorno)} | Valor: </span><span class="si">{retorno}</span><span class="s1">'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Resultado da execução:</p>
+<div class="highlight"><pre><span></span><span class="err">Variável: valores | Tipo: <class 'list'> | Valor: [10, 4, 16, 12, 8]</span>
+<span class="err">Variável: retorno | Tipo: <class 'list'> | Valor: [10, 4, 16, 12, 8]</span>
+</pre></div>
+
+
+<p>Nesse caso, pode-se apenas chamar a função, como também utilizá-la nos argumentos de outras funções. Porém para se ter os valores originais, deve-se fazer uma cópia manualmente antes de executar a função.</p>
+<h2>Exemplo na biblioteca padrão</h2>
+<p>Na biblioteca padrão do Python, existem os métodos <code>sort</code> e <code>reverse</code> que seguem o padrão <em>in place</em>, e as funções <code>sorted</code> e <code>reversed</code> que seguem o padrão cópia de valor, podendo ser utilizados para ordenar e inverter os valores de uma lista, por exemplo. Quando não é mais necessário uma cópia da lista com a ordem original, é preferível utilizar funções <em>in place</em>, que alteram a própria lista, e como não criam uma cópia da lista, utilizam menos memória. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">valores</span><span class="o">.</span><span class="n">sort</span><span class="p">()</span>
+<span class="n">valores</span><span class="o">.</span><span class="n">reverse</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Se for necessário manter uma cópia da lista inalterada, deve-se optar pelas funções de cópia de valor. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">novos_valores</span> <span class="o">=</span> <span class="nb">reversed</span><span class="p">(</span><span class="nb">sorted</span><span class="p">(</span><span class="n">valores</span><span class="p">))</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">novos_valores</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Porém esse exemplo cria duas cópias da lista, uma em cada função. Para criar apenas uma cópia, pode-se misturar funções <em>in place</em> com cópia de valor. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">novos_valores</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+<span class="n">novos_valores</span><span class="o">.</span><span class="n">reverse</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">novos_valores</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Também vale observar que algumas utilizações dessas funções podem dar a impressão de que elas não funcionaram, como:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+
+<span class="nb">sorted</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">valores</span><span class="p">)</span> <span class="c1"># Imprime a lista original, e não a ordenada</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">valores</span><span class="o">.</span><span class="n">sort</span><span class="p">())</span> <span class="c1"># Imprime None e não a lista</span>
+</pre></div>
+
+
+<h2>Considerações</h2>
+<p>Nem sempre é possível utilizar o padrão desejado, <em>strings</em> no Python (<code>str</code>) são imutáveis, logo todas as funções que manipulam elas seguiram o padrão cópia de valor, e para outros tipos, pode ocorrer de só existir funções <em>in place</em>, sendo necessário fazer uma cópia manualmente antes de chamar a função, caso necessário. Para saber qual padrão a função implementa, é necessário consultar sua documentação, ou verificando sua assinatura, embora ainda possa existir uma dúvida entre cópia de valor e híbrida, visto que a assinatura dos dois padrões são iguais.</p>
+<p>Os exemplos aqui dados são didáticos. Caso deseja-se ordenar de forma reversa, tanto o método <code>sort</code>, quanto a função <code>sorted</code> podem receber como argumento <code>reverse=True</code>, e assim já fazer a ordenação reversa. Assim como é possível criar uma nova lista já com os valores, sem precisar adicionar manualmente item por item, como os exemplos:</p>
+<div class="highlight"><pre><span></span><span class="n">valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
+<span class="n">partes_dos_valores</span> <span class="o">=</span> <span class="n">valores</span><span class="p">[</span><span class="mi">2</span><span class="p">:]</span>
+<span class="n">novos_valores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span> <span class="o">*</span> <span class="n">valor</span> <span class="k">for</span> <span class="n">valor</span> <span class="ow">in</span> <span class="n">valores</span><span class="p">]</span>
+</pre></div>
+
+
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ Mon, 29 Mar 2021 15:00:00 +0000
+
+
+ PythonClub: Encapsulamento da lógica do algoritmo
+ tag:pythonclub.com.br,2021-03-02:/encapsulamento-da-logica-do-algoritmo.html
+ https://pythonclub.com.br/encapsulamento-da-logica-do-algoritmo.html
+ <p>Muitas listas de exercícios de lógica de programação pedem em algum momento que um valor seja lido do teclado, e caso esse valor seja inválido, deve-se avisar, e repetir a leitura até que um valor válido seja informado. Utilizando a ideia de <a href="https://dev.to/acaverna/otimizando-o-algoritmo-passo-a-passo-4co0">otimização do algoritmo passo a passo</a>, começando com uma solução simples, pretendo estudar como reduzir a duplicação de código alterando o algoritmo, encapsulando a lógica em funções, e encapsulando em classes.</p>
+<h2>Exercício</h2>
+<p>Um exemplo de exercício que pede esse tipo de validação é a leitura de notas, que devem estar entre 0 e 10. A solução mais simples, consiste em ler um valor, e enquanto esse valor for inválido, dar o aviso e ler outro valor. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">))</span>
+<span class="k">while</span> <span class="n">nota</span> <span class="o"><</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">nota</span> <span class="o">></span> <span class="mi">10</span><span class="p">:</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'Nota inválida'</span><span class="p">)</span>
+ <span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">))</span>
+</pre></div>
+
+
+<p>Esse algoritmo funciona, porém existe uma duplicação no código que faz a leitura da nota (uma antes do <em>loop</em> e outra dentro). Caso seja necessário uma alteração, como a mudança da nota para um valor inteiro entre 0 e 100, deve-se alterar os dois lugares, e se feito em apenas um lugar, o algoritmo poderia processar valores inválidos.</p>
+<h3>Alterando o algoritmo</h3>
+<p>Visando remover a repetição de código, é possível unificar a leitura do valor dentro do <em>loop</em>, uma vez que é necessário repetir essa instrução até que o valor válido seja obtido. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">))</span>
+ <span class="k">if</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">nota</span> <span class="o"><=</span> <span class="mi">10</span><span class="p">:</span>
+ <span class="k">break</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'Nota inválida!'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Dessa forma, não existe mais a repetição de código. A condição de parada, que antes verificava se o valor era inválido (o que pode ter uma leitura não tão intuitiva), agora verifica se é um valor válido (que é geralmente é mais fácil de ler e escrever a condição). E a ordem dos comandos dentro do <em>loop</em>, que agora estão em uma ordem que facilita a leitura, visto que no algoritmo anterior era necessário tem em mente o que era executado antes do <em>loop</em>.</p>
+<p>Porém esses algoritmos validam apenas o valor lido, apresentando erro caso seja informado um valor com formato inválido, como letras em vez de números. Isso pode ser resolvido tratando as exceções lançadas. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">))</span>
+ <span class="k">if</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">nota</span> <span class="o"><=</span> <span class="mi">10</span><span class="p">:</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'Nota inválida!'</span><span class="p">)</span>
+</pre></div>
+
+
+<h3>Encapsulamento da lógica em função</h3>
+<p>Caso fosse necessário ler várias notas, com os algoritmos apresentados até então, seria necessário repetir todo esse trecho de código, ou utilizá-lo dentro de uma estrutura de repetição. Para facilitar sua reutilização, evitando a duplicação de código, é possível encapsular esse algoritmo dentro de uma função. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">nota_input</span><span class="p">(</span><span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">nota</span> <span class="o"><=</span> <span class="mi">10</span><span class="p">:</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s1">'Nota inválida!'</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">nota</span>
+
+
+<span class="n">nota1</span> <span class="o">=</span> <span class="n">nota_input</span><span class="p">(</span><span class="s1">'Digite a primeira nota: '</span><span class="p">)</span>
+<span class="n">nota2</span> <span class="o">=</span> <span class="n">nota_input</span><span class="p">(</span><span class="s1">'Digite a segunda nota: '</span><span class="p">)</span>
+</pre></div>
+
+
+<h3>Encapsulamento da lógica em classes</h3>
+<p>Em vez de encapsular essa lógica em uma função, é possível encapsulá-la em uma classe, o que permitiria separar cada etapa do algoritmo em métodos, assim como ter um método responsável por controlar qual etapa deveria ser chamada em qual momento. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ValidaNotaInput</span><span class="p">:</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nota inválida!'</span>
+
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">entrada</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">validar_nota</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nota</span><span class="p">):</span>
+ <span class="k">return</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">nota</span> <span class="o"><=</span> <span class="mi">10</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">nota</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_nota</span><span class="p">(</span><span class="n">nota</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">nota</span>
+
+
+<span class="n">nota_input</span> <span class="o">=</span> <span class="n">ValidaNotaInput</span><span class="p">()</span>
+
+
+<span class="n">nota</span> <span class="o">=</span> <span class="n">nota_input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Vale observar que o método <code>__call__</code> permite que o objeto criado a partir dessa classe seja chamado como se fosse uma função. Nesse caso ele é o responsável por chamar cada etapa do algoritmo, como: <code>ler_entrada</code> que é responsável por ler o que foi digitado no teclado, <code>transformar_entrada</code> que é responsável por converter o texto lido para o tipo desejado (converter de <code>str</code> para <code>float</code>), e <code>validar_nota</code> que é responsável por dizer se o valor é válido ou não. Vale observar que ao dividir o algoritmo em métodos diferentes, seu código principal virou uma espécie de código comentado, descrevendo o que está sendo feito e onde está sendo feito.</p>
+<p>Outra vantagem de encapsular a lógica em classe, em vez de uma função, é a possibilidade de generalizá-la. Se fosse necessário validar outro tipo de entrada, encapsulando em uma função, seria necessário criar outra função repetindo todo o algoritmo, alterando apenas a parte referente a transformação do valor lido, e validação, o que gera uma espécie de repetição de código. Ao encapsular em classes, é possível se aproveitar dos mecanismos de herança para evitar essa repetição. Exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ValidaInput</span><span class="p">:</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Valor inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">ler_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">raise</span> <span class="ne">NotImplementedError</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">raise</span> <span class="ne">NotImplementedError</span>
+
+ <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">):</span>
+ <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ler_entrada</span><span class="p">(</span><span class="n">prompt</span><span class="p">))</span>
+ <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validar_valor</span><span class="p">(</span><span class="n">valor</span><span class="p">):</span>
+ <span class="k">break</span>
+ <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mensagem_valor_invalido</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">valor</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNomeInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nome inválido!'</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">entrada</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">title</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">valor</span> <span class="o">!=</span> <span class="s1">''</span>
+
+
+<span class="k">class</span> <span class="nc">ValidaNotaInput</span><span class="p">(</span><span class="n">ValidaInput</span><span class="p">):</span>
+ <span class="n">mensagem_valor_invalido</span> <span class="o">=</span> <span class="s1">'Nota inválida!'</span>
+
+ <span class="k">def</span> <span class="nf">transformar_entrada</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">entrada</span><span class="p">):</span>
+ <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">entrada</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">validar_valor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="k">return</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">valor</span> <span class="o"><=</span> <span class="mi">10</span>
+
+
+<span class="n">nome_input</span> <span class="o">=</span> <span class="n">ValidaNomeInput</span><span class="p">()</span>
+<span class="n">nota_input</span> <span class="o">=</span> <span class="n">ValidaNotaInput</span><span class="p">()</span>
+
+
+<span class="n">nome</span> <span class="o">=</span> <span class="n">nome_input</span><span class="p">(</span><span class="s1">'Digite o nome: '</span><span class="p">)</span>
+<span class="n">nota</span> <span class="o">=</span> <span class="n">nota_input</span><span class="p">(</span><span class="s1">'Digite a nota: '</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Dessa forma, é possível reutilizar o código já existente para criar outras validações, sendo necessário implementar apenas como converter a <code>str</code> lida do teclado para o tipo desejado, e como esse valor deve ser validado. Não é necessário entender e repetir a lógica de ler o valor, validá-lo, imprimir a mensagem de erro, e repetir até que seja informado um valor válido.</p>
+<h2>Considerações</h2>
+<p>É possível encapsular a lógica de um algoritmo em funções ou em classes. Embora para fazê-lo em uma classe exija conhecimentos de programação orientada a objetos, o seu reaproveitamento é facilitado, abstraindo toda a complexidade do algoritmo, que pode ser disponibilizado através de uma biblioteca, exigindo apenas a implementações de métodos simples por quem for a utilizar.</p>
+<p>Ainda poderia ser discutido outras formas de fazer essa implementação, como passar funções como parâmetro e a utilização de <a href="https://docs.python.org/pt-br/3/library/asyncio-task.html">corrotinas</a> no encapsulamento do algoritmo em função, assim como a utilização de <a href="https://docs.python.org/pt-br/3/library/functions.html#classmethod">classmethod</a>, <a href="https://docs.python.org/pt-br/3/library/functions.html#staticmethod">staticmethod</a> e <a href="https://docs.python.org/pt-br/3/library/abc.html">ABC</a> no encapsulamento do algoritmo em classes.</p>
+<hr />
+<p>Esse artigo foi publicado originalmente no <a href="https://eduardoklosowski.github.io/blog/">meu blog</a>, passe por lá, ou siga-me no <a href="https://dev.to/eduardoklosowski">DEV</a> para ver mais artigos que eu escrevi.</p>
+ Tue, 02 Mar 2021 18:00:00 +0000
+
+
+ PythonClub: Fazendo backup do banco de dados no Django
+ tag:pythonclub.com.br,2020-10-30:/fazendo-backup-do-banco-de-dados-no-django.html
+ https://pythonclub.com.br/fazendo-backup-do-banco-de-dados-no-django.html
+ <h2>Apresentação</h2>
+<p>Em algum momento, durante o seu processo de desenvolvimento com Django, pode ser que surja a necessidade de criar e restaurar o banco de dados da aplicação. Pensando nisso, resolvi fazer um pequeno tutorial, básico, de como realizar essa operação.</p>
+<p>Nesse tutorial, usaremos o <a href="https://github.com/django-dbbackup/django-dbbackup">django-dbbackup</a>, um pacote desenvolvido especificamente para isso.</p>
+<h2>Configurando nosso ambiente</h2>
+<p>Primeiro, partindo do início, vamos criar uma pasta para o nosso projeto e, nela, isolar o nosso ambiente de desenvolvimento usando uma <a href="https://virtualenv.pypa.io/en/latest/index.html">virtualenv</a>:</p>
+<div class="highlight"><pre><span></span>mkdir projeto_db <span class="o">&&</span> <span class="nb">cd</span> projeto_db <span class="c1">#criando a pasta do nosso projeto</span>
+
+virtualenv -p python3.8 env <span class="o">&&</span> <span class="nb">source</span> env/bin/activate <span class="c1">#criando e ativando a nossa virtualenv</span>
+</pre></div>
+
+
+<p>Depois disso e com o nosso ambiente já ativo, vamos realizar os seguintes procedimentos:</p>
+<div class="highlight"><pre><span></span>pip install -U pip <span class="c1">#com isso, atualizamos a verão do pip instalado</span>
+</pre></div>
+
+
+<h2>Instalando as dependências</h2>
+<p>Agora, vamos instalar o <a href="https://www.djangoproject.com/">Django</a> e o pacote que usaremos para fazer nossos backups.</p>
+<div class="highlight"><pre><span></span>pip install <span class="nv">Django</span><span class="o">==</span><span class="m">3</span>.1.2 <span class="c1">#instalando o Django</span>
+
+pip install django-dbbackup <span class="c1">#instalando o django-dbbackup</span>
+</pre></div>
+
+
+<h2>Criando e configurando projeto</h2>
+<p>Depois de instaladas nossas dependências, vamos criar o nosso projeto e configurar o nosso pacote nas configurações do Django.</p>
+<div class="highlight"><pre><span></span>django-admin startproject django_db . <span class="c1">#dentro da nossa pasta projeto_db, criamos um projeto Django com o nome de django_db.</span>
+</pre></div>
+
+
+<p>Depois de criado nosso projeto, vamos criar e popular o nosso banco de dados.</p>
+<div class="highlight"><pre><span></span>python manage.py migrate <span class="c1">#com isso, sincronizamos o estado do banco de dados com o conjunto atual de modelos e migrações.</span>
+</pre></div>
+
+
+<p>Criado nosso banco de dados, vamos criar um superusuário para podemos o painel admin do nosso projeto.</p>
+<div class="highlight"><pre><span></span>python manage.py createsuperuser
+</pre></div>
+
+
+<p>Perfeito. Já temos tudo que precisamos para executar nosso projeto. Para execução dele, é só fazermos:</p>
+<div class="highlight"><pre><span></span>python manage.py runserver
+</pre></div>
+
+
+<p>Você terá uma imagem assim do seu projeto:</p>
+<p><img alt="" src="https://jacksonosvaldo.github.io/img/django_db.png" /></p>
+<h2>Configurando o django-dbbackup</h2>
+<p>Dentro do seu projeto, vamos acessar o arquivo settings.py, como expresso abaixo:</p>
+<div class="highlight"><pre><span></span>django_db/
+├── settings.py
+</pre></div>
+
+
+<p>Dentro desse arquivos iremos, primeiro, adiconar o django-dbbackup às apps do projeto:</p>
+<div class="highlight"><pre><span></span><span class="n">INSTALLED_APPS</span> <span class="o">=</span> <span class="p">(</span>
+ <span class="o">...</span>
+ <span class="s1">'dbbackup'</span><span class="p">,</span> <span class="c1"># adicionando django-dbbackup</span>
+<span class="p">)</span>
+</pre></div>
+
+
+<p>Depois de adicionado às apps, vamos dizer para o Django o que vamos salvar no backup e, depois, indicar a pasta para onde será encaminhado esse arquivo. Essa inserção deve ou pode ser feita no final do arquivo <em>settings.py</em>:</p>
+<div class="highlight"><pre><span></span><span class="n">DBBACKUP_STORAGE</span> <span class="o">=</span> <span class="s1">'django.core.files.storage.FileSystemStorage'</span> <span class="c1">#o que salvar</span>
+<span class="n">DBBACKUP_STORAGE_OPTIONS</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'location'</span><span class="p">:</span> <span class="s1">'backups/'</span><span class="p">}</span> <span class="c1"># onde salvar</span>
+</pre></div>
+
+
+<p>Percebam que dissemos para o Django salvar o backup na pasta <em>backups</em>, mas essa pasta ainda não existe no nosso projeto. Por isso, precisamos criá-la [fora da pasta do projeto]:</p>
+<div class="highlight"><pre><span></span>mkdir backups
+</pre></div>
+
+
+<h2>Criando e restaurando nosso backup</h2>
+<p>Já temos tudo pronto. Agora, vamos criar o nosso primeiro backup:</p>
+<div class="highlight"><pre><span></span>python manage.py dbbackup
+</pre></div>
+
+
+<p>Depois de exetudado, será criado um arquivo -- no nosso exemplo, esse arquivo terá uma extensão .dump --, salvo na pasta <em>backups</em>. Esse arquivo contem todo backup do nosso banco de dados.</p>
+<p>Para recuperarmos nosso banco, vamos supor que migramos nosso sistema de um servidor antigo para um novo e, por algum motivo, nossa base de dados foi corrompida, inviabilizando seu uso. Ou seja, estamos com o sistema/projeto sem banco de dados -- ou seja, exlua ou mova a a sua base dados .sqlite3 para que esse exemplo seja útil --, mas temos os backups. Com isso, vamos restaurar o banco:</p>
+<div class="highlight"><pre><span></span>python manage.py dbrestore
+</pre></div>
+
+
+<p>Prontinho, restauramos nosso banco de dados. O interessante do django-dbbackup, dentre outras coisas, é que ele gera os backups com datas e horários específicos, facilitando o processo de recuperação das informações mais recentes.</p>
+<p>Por hoje é isso, pessoal. Até a próxima. ;) </p>
+ Fri, 30 Oct 2020 13:40:00 +0000
+
+
+ Filipe Saraiva: Seqtembro de eventos virtuais e gratuitos sobre Qt e KDE
+ https://blog.filipesaraiva.info/?p=2185
+ https://blog.filipesaraiva.info/?p=2185
+ <p>(Ok a piada com <em>seqtembro</em> funciona melhor na versão em inglês, <em>seqtember</em>, mas simbora)</p>
+<p>Por uma grande coincidência, obra do destino, ou nada disso, teremos um Setembro de 2020 repleto de eventos virtuais e gratuitos de alta qualidade sobre <a href="https://www.qt.io/" target="_blank" rel="noopener noreferrer">Qt</a> e <a href="https://kde.org/" target="_blank" rel="noopener noreferrer">KDE</a>.</p>
+<p>Começando de 4 à 11 do referido mês teremos o <a href="https://akademy.kde.org/2020/" target="_blank" rel="noopener noreferrer">Akademy 2020</a>, o grande encontro mundial da comunidade KDE que esse ano, por motivos que todos sabemos, acontecerá de forma virtual. A <a href="https://akademy.kde.org/2020/program" target="_blank" rel="noopener noreferrer">programação</a> do Akademy traz palestras, treinamentos, <em>hacking sessions</em>, discussões com foco em aplicações KDE específicas, e mais, reunindo hackers, designers, gerentes de projetos, tradutores, e colabores dos mais diversos segmentos para discutir e planejar o KDE e seus futuros passos.</p>
+<p><a href="https://blog.filipesaraiva.info/?attachment_id=2190" rel="attachment wp-att-2190"><img /></a></p>
+<p>E como falamos em KDE, por extensão, também falamos em Qt – afinal, grande parte das aplicações é escrita nesse framework. Portanto, mesmo que você trabalhe com Qt mas não use nada do KDE, vale a pena participar do evento – e também se perguntar “porque diabos não estou usando e desenvolvendo aplicações do KDE?”.</p>
+<p>Um incentivo extra é que durante o Akademy, entre 7 e 11, acontecerá o <a href="https://www.qtdesktopdays.com/" target="_blank" rel="noopener noreferrer">Qt Desktop Days</a>, evento da <a href="https://www.kdab.com/" target="_blank" rel="noopener noreferrer">KDAB</a> voltado para Qt no desktop (surpresa?). A <a href="https://www.qtdesktopdays.com/program/" target="_blank" rel="noopener noreferrer">programação preliminar</a> já está disponível e será muito interessante ver os avanços da tecnologia em um campo que pode parecer menos <em>sexy</em> hoje em dia, por conta da muita atenção dada a projetos mobile ou embarcados, mas que pelo contrário, continua vibrante e recebendo muito investimento.</p>
+<p><img /></p>
+<p>Após uma rápida pausa para um respiro, temos a <a href="http://bit.do/maratonaqt" target="_blank" rel="noopener noreferrer">Maratona Qt</a>. Nosso amigo <a href="https://sandroandrade.org/" target="_blank" rel="noopener noreferrer">Sandro Andrade</a>, professor do IFBA e colaborador de longa data do KDE, resolveu dedicar uma semana inteira, de 14 à 18 de setembro, para apresentar 5 tópicos sobre o Qt tratando de seus fundamentos e passos iniciais de cada um. O programa cobre QML, C++ e Qt, Qt no Android, no iOS, na web (sim!), computação gráfica e mesmo jogos! Extremamente recomendada pra todo mundo que conhece ou quer conhecer o framework.</p>
+<p>A Maratona Qt vai servir como um esquenta para a <a href="https://br.qtcon.org/" target="_blank" rel="noopener noreferrer">QtCon Brasil 2020</a>, esse ano também virtual. Em 26 e 27 de setembro o pessoal da <a href="http://qmob.solutions/" target="_blank" rel="noopener noreferrer">qmob.solutions</a> reunirá desenvolvedores Qt de vários países para apresentarem, entre outras coisas, trabalhos com Wayland, visão computacional e IA, análise de dados, Python, containers, prototipagem, embarcados, e outros, tudo envolvendo Qt! E também haverá uma apresentação sobre a próxima versão <em>major</em> da ferramenta, Qt 6.</p>
+<p>Portanto pessoal, reservem este mês para uma grande imersão nos vários aspectos e possibilidades disponibilizadas pelo Qt.</p>
+ Sat, 29 Aug 2020 18:48:00 +0000
+
+
+ Gabbleblotchits: MinHashing all the things: a quick analysis of MAG search results
+ tag:blog.luizirber.org,2020-07-24:/2020/07/24/mag-results/
+ https://blog.luizirber.org/2020/07/24/mag-results/
+ <p><a href="https://blog.luizirber.org/2020/07/22/mag-search/">Last time</a> I described a way to search MAGs in metagenomes,
+and teased about interesting results.
+Let's dig in some of them!</p>
+<p>I prepared a <a href="https://github.com/luizirber/2020-07-22-mag-search/">repo</a> with the data and a notebook with the analysis I did in this
+post.
+You can also follow along in <a href="https://mybinder.org">Binder</a>,
+as well as do your own analysis! <a href="https://mybinder.org/v2/gh/luizirber/2020-07-22-mag-search/master?filepath=index.ipynb"><img alt="Binder" src="https://mybinder.org/badge_logo.svg" /></a></p>
+<h2>Preparing some metadata</h2>
+<p>The supplemental materials for <a href="https://www.nature.com/articles/sdata2017203">Tully et al</a> include more details about each MAG,
+so let's download them.
+I prepared a small snakemake workflow to do that,
+as well as downloading information about the SRA datasets from Tara Oceans
+(the dataset used to generate the MAGs),
+as well as from <a href="https://www.nature.com/articles/s41564-017-0012-7">Parks et al</a>,
+which also generated MAGs from Tara Oceans.
+Feel free to include them in your analysis,
+but I was curious to find matches in other metagenomes.</p>
+<h2>Loading the data</h2>
+<p>The results from the MAG search are in a CSV file,
+with a column for the MAG name,
+another for the SRA dataset ID for the metagenome and a third column for the
+containment of the MAG in the metagenome.
+I also fixed the names to make it easier to query,
+and finally removed the Tara and Parks metagenomes
+(because we already knew they contained these MAGs).</p>
+<p>This left us with 23,644 SRA metagenomes with matches,
+covering 2,291 of the 2,631 MAGs.
+These are results for a fairly low containment (10%),
+so if we limit to MAGs with more than 50% containment we still have 1,407 MAGs and 2,938 metagenomes left.</p>
+<h2>TOBG_NP-110, I choose you!</h2>
+<p>That's still a lot,
+so I decided to pick a candidate to check before doing any large scale analysis.
+I chose TOBG_NP-110 because there were many matches above 50% containment,
+and even some at 99%.
+Turns out it is also an Archaeal MAG that failed to be classified further than Phylum level (Euryarchaeota),
+with a 70.3% complete score in the original analysis.
+Oh, let me dissect the name a bit:
+TOBG is "Tara Ocean Binned Genome" and "NP" is North Pacific.</p>
+<p>And so I went checking where the other metagenome matches came from.
+5 of the 12 matches above 50% containment come from one study,
+<a href="https://trace.ncbi.nlm.nih.gov/Traces/sra/?study=SRP044185">SRP044185</a>,
+with samples collected from a column of water in a station in Manzanillo, Mexico.
+Other 3 matches come from
+<a href="https://trace.ncbi.nlm.nih.gov/Traces/sra/?study=SRP003331">SRP003331</a>,
+in the South Pacific ocean (in northern Chile).
+Another match,
+<a href="https://trace.ncbi.nlm.nih.gov/Traces/sra/?run=ERR3256923">ERR3256923</a>,
+also comes from the South Pacific.</p>
+<h2>What else can I do?</h2>
+<p>I'm curious to follow <a href="http://merenlab.org/data/refining-mags/">the refining MAGs</a> tutorial from the Meren Lab and see where this goes,
+and especially in using <a href="https://genomebiology.biomedcentral.com/articles/10.1186/s13059-020-02066-4"><code>spacegraphcats</code></a>
+to extract neighborhoods from the MAG and better evaluate what is missing or if there are other interesting bits that
+the MAG generation methods ended up discarding.</p>
+<p>So, for now that's it.
+But more important,
+I didn't want to sit on these results until there is a publication in press,
+especially when there are people that can do so much more with these,
+so I decided to make it all public.
+It is way more exciting to see this being used to know more about these
+organisms than me being the only one with access to this info.</p>
+<p>And yesterday I saw <a href="https://twitter.com/DrJonathanRosa/status/1286381346605027328">this tweet</a> by
+<a href="https://twitter.com/DrJonathanRosa/status/1286381346605027328">@DrJonathanRosa</a>,
+saying:</p>
+<blockquote>
+<p>I don’t know who told students that the goal of research is to find some
+previously undiscovered research topic, claim individual ownership over it,
+& fiercely protect it from theft, but that almost sounds like, well,
+colonialism, capitalism, & policing </p>
+</blockquote>
+<p>Amen.</p>
+<h2>I want to run this with my data!</h2>
+<p>Next time. But we will have a discussion about scientific infrastructure and
+sustainability first =]</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://twitter.com/luizirber/status/1286700888111738880">Thread on Twitter</a></li>
+</ul>
+ Fri, 24 Jul 2020 15:00:00 +0000
+
+
+ Gabbleblotchits: MinHashing all the things: searching for MAGs in the SRA
+ tag:blog.luizirber.org,2020-07-22:/2020/07/22/mag-search/
+ https://blog.luizirber.org/2020/07/22/mag-search/
+ <p>(or: Top-down and bottom-up approaches for working around sourmash limitations)</p>
+<p>In the last month I updated <a href="https://wort.oxli.org">wort</a>,
+the system I developed for computing sourmash signature for public genomic databases,
+and started calculating signatures
+for the <a href="https://www.ncbi.nlm.nih.gov/sra/?term=%22METAGENOMIC%22%5Bsource%5D+NOT+amplicon%5BAll+Fields%5D)">metagenomes</a> in the <a href="https://www.ncbi.nlm.nih.gov/sra/">Sequence Read Archive</a>.
+This is a more challenging subset than the <a href="https://blog.luizirber.org/2016/12/28/soursigs-arch-1/">microbial datasets</a> I was doing previously,
+since there are around 534k datasets from metagenomic sources in the SRA,
+totalling 447 TB of data.
+Another problem is the size of the datasets,
+ranging from a couple of MB to 170 GB.
+Turns out that the workers I have in <code>wort</code> are very good for small-ish datasets,
+but I still need to figure out how to pull large datasets faster from the SRA,
+because the large ones take forever to process...</p>
+<p>The good news is that I managed to calculate signatures for almost 402k of them
+<sup id="sf-mag-search-1-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-mag-search-1" class="simple-footnote" title="pulling about a 100 TB in 3 days, which was pretty fun to see because I ended up DDoS myself because I couldn't download the generated sigs fast enough from the S3 bucket where they are temporarily stored =P">1</a></sup>,
+which already let us work on some pretty exciting problems =]</p>
+<h2>Looking for MAGs in the SRA</h2>
+<p>Metagenome-assembled genomes are essential for studying organisms that are hard to isolate and culture in lab,
+especially for environmental metagenomes.
+<a href="https://www.nature.com/articles/sdata2017203">Tully et al</a> published 2,631 draft MAGs from 234 samples collected during the Tara Oceans expedition,
+and I wanted to check if they can also be found in other metagenomes besides the Tara Oceans ones.
+The idea is to extract the reads from these other matches and evaluate how the MAG can be improved,
+or at least evaluate what is missing in them.
+I choose to use environmental samples under the assumption they are easier to deposit on the SRA and have public access,
+but there are many human gut microbiomes in the SRA and this MAG search would work just fine with those too.</p>
+<p>Moreover,
+I want to search for containment,
+and not similarity.
+The distinction is subtle,
+but similarity takes into account both datasets sizes
+(well, the size of the union of all elements in both datasets),
+while containment only considers the size of the query.
+This is relevant because the similarity of a MAG and a metagenome is going to be very small (and is symmetrical),
+but the containment of the MAG in the metagenome might be large
+(and is asymmetrical, since the containment of the metagenome in the MAG is likely very small because the metagenome is so much larger than the MAG).</p>
+<h2>The computational challenge: indexing and searching</h2>
+<p>sourmash signatures are a small fraction of the original size of the datasets,
+but when you have hundreds of thousands of them the collection ends up being pretty large too.
+More precisely, 825 GB large.
+That is way bigger than any index I ever built for sourmash,
+and it would also have pretty distinct characteristics than what we usually do:
+we tend to index genomes and run <code>search</code> (to find similar genomes) or <code>gather</code>
+(to decompose metagenomes into their constituent genomes),
+but for this MAG search I want to find which metagenomes have my MAG query above a certain containment threshold.
+Sort of a <code>sourmash search --containment</code>,
+but over thousands of metagenome signatures.
+The main benefit of an SBT index in this context is to avoid checking all signatures because we can prune the search early,
+but currently SBT indices need to be totally loaded in memory during <code>sourmash index</code>.
+I will have to do this in the medium term,
+but I want a solution NOW! =]</p>
+<p><a href="https://github.com/dib-lab/sourmash/releases/tag/v3.4.0">sourmash 3.4.0</a> introduced <code>--from-file</code> in many commands,
+and since I can't build an index I decided to use it to load signatures for the metagenomes.
+But... <code>sourmash search</code> tries to load all signatures in memory,
+and while I might be able to find a cluster machine with hundreds of GBs of RAM available,
+that's not very practical.</p>
+<p>So, what to do?</p>
+<h2>The top-down solution: a snakemake workflow</h2>
+<p>I don't want to modify sourmash now,
+so why not make a workflow and use snakemake to run one <code>sourmash search --containment</code> for each metagenome?
+That means 402k tasks,
+but at least I can use <a href="https://snakemake.readthedocs.io/en/stable/executing/cli.html#dealing-with-very-large-workflows">batches</a> and <a href="https://slurm.schedmd.com/job_array.html">SLURM job arrays</a> to submit reasonably-sized jobs to our HPC queue.
+After running all batches I summarized results for each task,
+and it worked well for a proof of concept.</p>
+<p>But... it was still pretty resource intensive:
+each task was running one query MAG against one metagenome,
+and so each task needed to do all the overhead of starting the Python interpreter and parsing the query signature,
+which is exactly the same for all tasks.
+Extending it to support multiple queries to the same metagenome would involve duplicating tasks,
+and 402k metagenomes times 2,631 MAGs is...
+a very large number of jobs.</p>
+<p>I also wanted to avoid clogging the job queues,
+which is not very nice to the other researchers using the cluster.
+This limited how many batches I could run in parallel...</p>
+<h2>The bottom-up solution: Rust to the rescue!</h2>
+<p>Thinking a bit more about the problem,
+here is another solution:
+what if we load all the MAGs in memory
+(as they will be queried frequently and are not that large),
+and then for each metagenome signature load it,
+perform all MAG queries,
+and then unload the metagenome signature from memory?
+This way we can control memory consumption
+(it's going to be proportional to all the MAG sizes plus the size of the largest metagenome)
+and can also efficiently parallelize the code because each task/metagenome is independent
+and the MAG signatures can be shared freely (since they are read-only).</p>
+<p>This could be done with the sourmash Python API plus <code>multiprocessing</code> or some
+other parallelization approach (maybe dask?),
+but turns out that everything we need comes from the Rust API.
+Why not enjoy a bit of the <a href="https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html">fearless concurrency</a> that is one of the major Rust goals?</p>
+<p><a href="https://github.com/luizirber/phd/blob/aa1ed9eb33ba71fdf9b3f2c92931701be6df00cd/experiments/wort/sra_search/src/main.rs">The whole code</a> ended up being 176 lines long,
+including command line parsing using <a href="https://docs.rs/structopt/latest/structopt/">strucopt</a> and parallelizing the search using <a href="https://docs.rs/rayon/latest/rayon/">rayon</a>
+and a <a href="https://doc.rust-lang.org/std/sync/mpsc/fn.channel.html">multiple-producer, single-consumer channel</a> to write results to an output
+(either the terminal or a file).
+This version took 11 hours to run,
+using less than 5GB of RAM and 32 processors,
+to search 2k MAGs against 402k metagenomes.
+And, bonus! It can also be parallelized again if you have multiple machines,
+so it potentially takes a bit more than an hour to run if you can allocate 10 batch jobs,
+with each batch 1/10 of the metagenome signatures.</p>
+<h2>So, is bottom-up always the better choice?</h2>
+<p>I would like to answer "Yes!",
+but bioinformatics software tends to be organized as command line interfaces,
+not as libraries.
+Libraries also tend to have even less documentation than CLIs,
+and this particular case is not a fair comparison because...
+Well, I wrote most of the library,
+and the Rust API is not that well documented for general use.</p>
+<p>But I'm pretty happy with how the sourmash CLI is viable both for the top-down approach
+(and whatever workflow software you want to use) as well as how the Rust core worked for the bottom-up approach.
+I think the most important is having the option to choose which way to go,
+especially because now I can use the bottom-up approach to make the sourmash CLI
+and Python API better.
+The top-down approach is also way more accessible in general,
+because you can pick your favorite workflow software and use all the tricks you're comfortable with.</p>
+<h2>But, what about the results?!?!?!</h2>
+<p>Next time. But I did find MAGs with over 90% containment in very different locations,
+which is pretty exciting!</p>
+<p>I also need to find a better way of distributing all these signature,
+because storing 4 TB of data in S3 is somewhat cheap,
+but transferring data is very expensive.
+All signatures are also available on IPFS,
+but I need more people to host them and share.
+Get in contact if you're interested in helping =]</p>
+<p>And while I'm asking for help,
+any tips on pulling data faster from the SRA are greatly appreciated!</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://twitter.com/luizirber/status/1285782732790849537">Thread on Twitter</a></li>
+</ul><hr /><h2>Footnotes</h2><ol><li id="sf-mag-search-1"><p>pulling about a 100 TB in 3 days, which was pretty fun to see because I
+ended up DDoS myself because I couldn't download the generated sigs fast enough
+from the S3 bucket where they are temporarily stored =P <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-mag-search-1-back" class="simple-footnote-back">↩</a></p></li></ol>
+ Wed, 22 Jul 2020 15:00:00 +0000
+
+
+ Gabbleblotchits: Putting it all together
+ tag:blog.luizirber.org,2020-05-11:/2020/05/11/sbt-zip/
+ https://blog.luizirber.org/2020/05/11/sbt-zip/
+ <p>sourmash <a href="https://twitter.com/ctitusbrown/status/1257418140729868291">3.3</a> was released last week,
+and it is the first version supporting <a href="http://ivory.idyll.org/blog/2020-sourmash-databases-as-zip-files.html">zipped databases</a>.
+Here is my personal account of how that came to be =]</p>
+<h2>What is a sourmash database?</h2>
+<p>A sourmash database contains signatures (typically Scaled MinHash sketches built from genomic datasets) and
+an index for allowing efficient similarity and containment queries over these signatures.
+The two types of index are SBT,
+a hierarchical index that uses less memory by keeping data on disk,
+and LCA,
+an inverted index that uses more memory but is potentially faster.
+Indices are described as JSON files,
+with LCA storing all the data in one JSON file and SBT opting for saving a description of the index structure in JSON,
+and all the data into a hidden directory with many files.</p>
+<p>We distribute some <a href="https://sourmash.readthedocs.io/en/v3.3.0/databases.html">prepared databases</a> (with SBT indices) for Genbank and RefSeq as compressed TAR files.
+The compressed file is ~8GB,
+but after decompressing it turns into almost 200k files in a hidden directory,
+using about 40 GB of disk space.</p>
+<h2>Can we avoid generating so many hidden files?</h2>
+<p>The initial issue in this saga is <a href="https://github.com/dib-lab/sourmash/issues/490">dib-lab/sourmash#490</a>,
+and the idea was to take the existing support for multiple data storages
+(hidden dir,
+TAR files,
+IPFS and Redis) and save the index description in the storage,
+allowing loading everything from the storage.
+Since we already had the databases as TAR files,
+the first test tried to use them but it didn't take long to see it was a doomed approach:
+TAR files are terrible from random access
+(or at least the <code>tarfile</code> module in Python is).</p>
+<p>Zip files showed up as a better alternative,
+and it helps that Python has the <code>zipfile</code> module already available in the
+standard library.
+Initial tests were promising,
+and led to <a href="https://github.com/dib-lab/sourmash/pull/648">dib-lab/sourmash#648</a>.
+The main issue was performance:
+compressing and decompressing was slow,
+but there was also another limitation...</p>
+<h2>Loading Nodegraphs from a memory buffer</h2>
+<p>Another challenge was efficiently loading the data from a storage.
+The two core methods in a storage are <code>save(location, content)</code>,
+where <code>content</code> is a bytes buffer,
+and <code>load(location)</code>,
+which returns a bytes buffer that was previously saved.
+This didn't interact well with the <code>khmer</code> <code>Nodegraph</code>s (the Bloom Filter we use for SBTs),
+since <code>khmer</code> only loads data from files,
+not from memory buffers.
+We ended up doing a temporary file dance,
+which made things slower for the default storage (hidden dir),
+where it could have been optimized to work directly with files,
+and involved interacting with the filesystem for the other storages
+(IPFS and Redis could be pulling data directly from the network,
+for example).</p>
+<p>This one could be fixed in <code>khmer</code> by exposing C++ stream methods,
+and I did a <a href="https://github.com/luizirber/2018-cython-streams">small PoC</a> to test the idea.
+While doable,
+this is something that was happening while the sourmash conversion to Rust was underway,
+and depending on <code>khmer</code> was a problem for my Webassembly aspirations...
+so,
+having the Nodegraph <a href="https://github.com/luizirber/sourmash-rust/pull/15">implemented in Rust</a> seemed like a better direction,
+That has actually been quietly living in the sourmash codebase for quite some time,
+but it was never exposed to the Python (and it was also lacking more extensive
+tests).</p>
+<p>After the release of sourmash 3 and the replacement of the C++ for the Rust implementation,
+all the pieces for exposing the Nodegraph where in place,
+so <a href="https://github.com/dib-lab/sourmash/pull/799">dib-lab/sourmash#799</a> was the next step.
+It wasn't a priority at first because other optimizations
+(that were released in 3.1 and 3.2)
+were more important,
+but then it was time to check how this would perform.
+And...</p>
+<h2>Your Rust code is not so fast, huh?</h2>
+<p>Turns out that my Nodegraph loading code was way slower than <code>khmer</code>.
+The Nodegraph binary format <a href="https://khmer.readthedocs.io/en/latest/dev/binary-file-formats.html#nodegrap://khmer.readthedocs.io/en/latest/dev/binary-file-formats.html#nodegraph">is well documented</a>,
+and doing an initial implementation wasn't so hard by using the <code>byteorder</code> crate
+to read binary data with the right endianess,
+and then setting the appropriate bits in the internal <code>fixedbitset</code> in memory.
+But the khmer code doesn't parse bit by bit:
+it <a href="https://github.com/dib-lab/khmer/blob/fe0ce116456b296c522ba24294a0cabce3b2648b/src/oxli/storage.cc#L233">reads</a> a long <code>char</code> buffer directly,
+and that is many orders of magnitude faster than setting bit by bit.</p>
+<p>And there was no way to replicate this behavior directly with <code>fixedbitset</code>.
+At this point I could either bit-indexing into a large buffer
+and lose all the useful methods that <code>fixedbitset</code> provides,
+or try to find a way to support loading the data directly into <code>fixedbitset</code> and
+open a PR.</p>
+<p><a href="https://github.com/petgraph/fixedbitset/pull/42">I chose the PR</a> (and even got #42! =]).</p>
+<p>It was more straightforward than I expected,
+but it did expose the internal representation of <code>fixedbitset</code>,
+so I was a bit nervous it wasn't going to be merged.
+But <a href="https://github.com/bluss">bluss</a> was super nice,
+and his suggestions made the PR way better!
+This <a href="https://github.com/dib-lab/sourmash/blob/9a695fb03b99c060bb8d1384ab78bb3797c5eb65/src/core/src/sketch/nodegraph.rs#L235L261">simplified</a> the final <code>Nodegraph</code> code,
+and actually was more correct
+(because I was messing a few corner cases when doing the bit-by-bit parsing before).
+Win-win!</p>
+<h2>Nodegraphs are kind of large, can we compress them?</h2>
+<p>Being able to save and load <code>Nodegraph</code>s in Rust allowed using memory buffers,
+but also opened the way to support other operations not supported in khmer <code>Nodegraph</code>s.
+One example is loading/saving compressed files,
+which is supported for <code>Countgraph</code>
+(another khmer data structure,
+based on Count-Min Sketch)
+but not in <code>Nodegraph</code>.</p>
+<p>If only there was an easy way to support working with compressed files...</p>
+<p>Oh wait, there is! <a href="https://github.com/luizirber/niffler">niffler</a> is a crate that I made with <a href="https://twitter.com/pierre_marijon">Pierre Marijon</a> based
+on some functionality I saw in one of his projects,
+and we iterated a bit on the API and documented everything to make it more
+useful for a larger audience.
+<code>niffler</code> tries to be as transparent as possible,
+with very little boilerplate when using it but with useful features nonetheless
+(like auto detection of the compression format).
+If you want more about the motivation and how it happened,
+check this <a href="https://twitter.com/luizirber/status/1253445504622424064">Twitter thread</a>.</p>
+<p>The cool thing is that adding compressed files support in <code>sourmash</code> was mostly
+<a href="https://github.com/dib-lab/sourmash/pull/799/files#diff-313a7ff0fdb14f408a64b3f010f46f65R220">one-line changes</a> for loading
+(and <a href="https://github.com/dib-lab/sourmash/pull/648/files#diff-d80ae1dd777d07300d7b6066b3318397L249-R273">a bit more</a> for saving,
+but mostly because converting compression levels could use some refactoring).</p>
+<h2>Putting it all together: zipped SBT indices</h2>
+<p>With all these other pieces in places,
+it's time to go back to <a href="https://github.com/dib-lab/sourmash/pull/648">dib-lab/sourmash#648</a>.
+Compressing and decompressing with the Python <code>zipfile</code> module is slow,
+but Zip files can also be used just for storage,
+handing back the data without extracting it.
+And since we have compression/decompression implemented in Rust with <code>niffler</code>,
+that's what the zipped sourmash databases are:
+data is loaded and saved into the Zip file without using the Python module
+compression/decompression,
+and all the work is done before (or after) in the Rust side.</p>
+<p>This allows keeping the Zip file with similar sizes to the original TAR files we started with,
+but with very low overhead for decompression.
+For compression we opted for using Gzip level 1,
+which doesn't compress perfectly but also doesn't take much longer to run:</p>
+<table>
+<thead>
+<tr>
+<th>Level</th>
+<th>Size</th>
+<th>Time</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>0</td>
+<td>407 MB</td>
+<td>16s</td>
+</tr>
+<tr>
+<td>1</td>
+<td>252 MB</td>
+<td>21s</td>
+</tr>
+<tr>
+<td>5</td>
+<td>250 MB</td>
+<td>39s</td>
+</tr>
+<tr>
+<td>9</td>
+<td>246 MB</td>
+<td>1m48s</td>
+</tr>
+</tbody>
+</table>
+<p>In this table, <code>0</code> is without compression,
+while <code>9</code> is the best compression.
+The size difference from <code>1</code> to <code>9</code> is only 6 MB (~2% difference)
+but runs 5x faster,
+and it's only 30% slower than saving the uncompressed data.</p>
+<p>The last challenge was updating an existing Zip file.
+It's easy to support appending new data,
+but if any of the already existing data in the file changes
+(which happens when internal nodes change in the SBT,
+after a new dataset is inserted) then there is no easy way to replace the data in the Zip file.
+Worse,
+the Python <code>zipfile</code> will add the new data while keeping the old one around,
+leading to ginormous files over time<sup id="sf-sbt-zip-1-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sbt-zip-1" class="simple-footnote" title="The zipfile module does throw a UserWarning pointing that duplicated files were inserted, which is useful during development but generally doesn't show during regular usage...">1</a></sup>
+So, what to do?</p>
+<p>I ended up opting for dealing with the complexity and <a href="https://github.com/dib-lab/sourmash/pull/648/files#diff-a99b088adcc872e1b408fbdcca20ebebR110-R248">complicating the ZipStorage</a> implementation a bit,
+by keeping a buffer for new data.
+If it's a new file or it already exists but there are no insertions
+the buffer is ignored and all works as before.</p>
+<p>If the file exists and new data is inserted,
+then it is first stored in the buffer
+(where it might also replace a previous entry with the same name).
+In this case we also need to check the buffer when trying to load some data
+(because it might exist only in the buffer,
+and not in the original file).</p>
+<p>Finally,
+when the <code>ZipStorage</code> is closed it needs to verify if there are new items in the buffer.
+If not,
+it is safe just to close the original file.
+If there are new items but they were not present in the original file,
+then we can append the new data to the original file.
+The final case is if there are new items that were also in the original file,
+and in this case a new Zip file is created and all the content from buffer and
+original file are copied to it,
+prioritizing items from the buffer.
+The original file is replaced by the new Zip file.</p>
+<p>Turns out this worked quite well! And so the PR was merged =]</p>
+<h2>The future</h2>
+<p>Zipped databases open the possibility of distributing extra data that might be
+useful for some kinds of analysis.
+One thing we are already considering is adding <a href="https://github.com/dib-lab/sourmash/issues/969">taxonomy information</a>,
+let's see what else shows up.</p>
+<p>Having <code>Nodegraph</code> in Rust is also pretty exciting,
+because now we can change the internal representation for something that uses
+less memory (maybe using <a href="https://alexbowe.com/rrr/">RRR encoding</a>?),
+but more importantly:
+now they can also be used with Webassembly,
+which opens many possibilities for running not only <a href="https://blog.luizirber.org/2018/08/27/sourmash-wasm/">signature computation</a> but
+also <code>search</code> and <code>gather</code> in the browser,
+since now we have all the pieces to build it.</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://twitter.com/luizirber/status/1260031886744621059">Thread on Twitter</a></li>
+</ul><hr /><h2>Footnotes</h2><ol><li id="sf-sbt-zip-1"><p>The <code>zipfile</code> module does throw a <code>UserWarning</code> pointing that duplicated files were inserted,
+which is useful during development but generally doesn't show during regular usage... <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sbt-zip-1-back" class="simple-footnote-back">↩</a></p></li></ol>
+ Mon, 11 May 2020 15:00:00 +0000
+
+
+ PythonClub: Criando um CI de uma aplicação Django usando Github Actions
+ tag:pythonclub.com.br,2020-01-24:/django-ci-github-actions.html
+ https://pythonclub.com.br/django-ci-github-actions.html
+ <p>Fala pessoal, tudo bom?</p>
+<p>Nos vídeo abaixo vou mostrar como podemos configurar um CI de uma aplicação Django usando Github Actions.</p>
+<p><a class="reference external" href="https://www.youtube.com/watch?v=KpSlY8leYFY">https://www.youtube.com/watch?v=KpSlY8leYFY</a>.</p>
+<div class="youtube youtube-16x9"></div>
+ Fri, 24 Jan 2020 15:10:00 +0000
+
+
+ Gabbleblotchits: Oxidizing sourmash: PR walkthrough
+ tag:blog.luizirber.org,2020-01-10:/2020/01/10/sourmash-pr/
+ https://blog.luizirber.org/2020/01/10/sourmash-pr/
+ <p>sourmash 3 was released last week,
+finally landing the Rust backend.
+But, what changes when developing new features in sourmash?
+I was thinking about how to best document this process,
+and since <a href="https://github.com/dib-lab/sourmash/pull/826/files">PR #826</a> is a short example touching all the layers I decided to do a
+small walkthrough.</p>
+<p>Shall we?</p>
+<h2>The problem</h2>
+<p>The first step is describing the problem,
+and trying to convince reviewers (and yourself) that the changes bring enough benefits to justify a merge.
+This is the description I put in the PR:</p>
+<blockquote>
+<p>Calling <code>.add_hash()</code> on a MinHash sketch is fine,
+but if you're calling it all the time it's better to pass a list of hashes and call <code>.add_many()</code> instead.
+Before this PR <code>add_many</code> just called <code>add_hash</code> for each hash it was passed,
+but now it will pass the full list to Rust (and that's way faster).</p>
+<p>No changes for public APIs,
+and I changed the <code>_signatures</code> method in LCA to accumulate hashes for each sig first,
+and then set them all at once.
+This is way faster,
+but might use more intermediate memory (I'll evaluate this now).</p>
+</blockquote>
+<p>There are many details that sound like jargon for someone not familiar with the codebase,
+but if I write something too long I'll probably be wasting the reviewers time too.
+The benefit of a very detailed description is extending the knowledge for other people
+(not necessarily the maintainers),
+but that also takes effort that might be better allocated to solve other problems.
+Or, more realistically, putting out other fires =P</p>
+<p>Nonetheless,
+some points I like to add in PR descriptions:
+- why is there a problem with the current approach?
+- is this the minimal viable change, or is it trying to change too many things
+ at once? The former is way better, in general.
+- what are the trade-offs? This PR is using more memory to lower the runtime,
+ but I hadn't measure it yet when I opened it.
+- Not changing public APIs is always good to convince reviewers.
+ If the project follows a <a href="https://semver.org/">semantic versioning</a> scheme,
+ changes to the public APIs are major version bumps,
+ and that can brings other consequences for users.</p>
+<h2>Setting up for changing code</h2>
+<p>If this was a bug fix PR,
+the first thing I would do is write a new test triggering the bug,
+and then proceed to fix it in the code
+(Hmm, maybe that would be another good walkthrough?).
+But this PR is making performance claims ("it's going to be faster"),
+and that's a bit hard to codify in tests.
+<sup id="sf-sourmash-pr-1-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-1" class="simple-footnote" title="We do have https://asv.readthedocs.io/ set up for micro-benchmarks, and now that I think about it... I could have started by writing a benchmark for add_many, and then showing that it is faster. I will add this approach to the sourmash PR checklist =]">1</a></sup>
+Since it's also proposing to change a method (<code>_signatures</code> in LCA indices) that is better to benchmark with a real index (and not a toy example),
+I used the same data and command I run in <a href="https://github.com/luizirber/sourmash_resources">sourmash_resources</a> to check how memory consumption and runtime changed.
+For reference, this is the command: </p>
+<div class="highlight"><pre><span></span>sourmash search -o out.csv --scaled <span class="m">2000</span> -k <span class="m">51</span> HSMA33OT.fastq.gz.sig genbank-k51.lca.json.gz
+</pre></div>
+
+
+<p>I'm using the <code>benchmark</code> feature from <a href="https://snakemake.readthedocs.io/">snakemake</a> in <a href="https://github.com/luizirber/sourmash_resources/blob/83ea237397d242e48c9d95eb0d9f50ceb4ad95c7/Snakefile#L99L114">sourmash_resources</a> to
+track how much memory, runtime and I/O is used for each command (and version) of sourmash,
+and generate the plots in the README in that repo.
+That is fine for a high-level view ("what's the maximum memory used?"),
+but not so useful for digging into details ("what method is consuming most memory?").</p>
+<p>Another additional problem is the dual<sup id="sf-sourmash-pr-2-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-2" class="simple-footnote" title="or triple, if you count C">2</a></sup> language nature of sourmash,
+where we have Python calling into Rust code (via CFFI).
+There are great tools for measuring and profiling Python code,
+but they tend to not work with extension code...</p>
+<p>So, let's bring two of my favorite tools to help!</p>
+<h3>Memory profiling: heaptrack</h3>
+<p><a href="https://github.com/KDE/heaptrack">heaptrack</a> is a heap profiler, and I first heard about it from <a href="https://www.vincentprouillet.com/">Vincent Prouillet</a>.
+Its main advantage over other solutions (like valgrind's massif) is the low
+overhead and... how easy it is to use:
+just stick <code>heaptrack</code> in front of your command,
+and you're good to go!</p>
+<p>Example output:</p>
+<div class="highlight"><pre><span></span>$ heaptrack sourmash search -o out.csv --scaled <span class="m">2000</span> -k <span class="m">51</span> HSMA33OT.fastq.gz.sig genbank-k51.lca.json.gz
+
+heaptrack stats:
+ allocations: <span class="m">1379353</span>
+ leaked allocations: <span class="m">1660</span>
+ temporary allocations: <span class="m">168984</span>
+Heaptrack finished! Now run the following to investigate the data:
+
+ heaptrack --analyze heaptrack.sourmash.66565.gz
+</pre></div>
+
+
+<p><code>heaptrack --analyze</code> is a very nice graphical interface for analyzing the results,
+but for this PR I'm mostly focusing on the Summary page (and overall memory consumption).
+Tracking allocations in Python doesn't give many details,
+because it shows the CPython functions being called,
+but the ability to track into the extension code (Rust) allocations is amazing
+for finding bottlenecks (and memory leaks =P).
+<sup id="sf-sourmash-pr-3-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-3" class="simple-footnote" title="It would be super cool to have the unwinding code from py-spy in heaptrack, and be able to see exactly what Python methods/lines of code were calling the Rust parts...">3</a></sup></p>
+<h3>CPU profiling: py-spy</h3>
+<p>Just as other solutions exist for profiling memory,
+there are many for profiling CPU usage in Python,
+including <code>profile</code> and <code>cProfile</code> in the standard library.
+Again, the issue is being able to analyze extension code,
+and bringing the cannon (the <code>perf</code> command in Linux, for example) looses the
+benefit of tracking Python code properly (because we get back the CPython
+functions, not what you defined in your Python code).</p>
+<p>Enters <a href="https://github.com/benfred/py-spy">py-spy</a> by <a href="https://www.benfrederickson.com">Ben Frederickson</a>,
+based on the <a href="https://github.com/rbspy/rbspy">rbspy</a> project by <a href="https://jvns.ca">Julia Evans</a>.
+Both use a great idea:
+read the process maps for the interpreters and resolve the full stack trace information,
+with low overhead (because it uses sampling).
+<a href="https://github.com/benfred/py-spy">py-spy</a> also goes further and resolves <a href="https://www.benfrederickson.com/profiling-native-python-extensions-with-py-spy/">native Python extensions</a> stack traces,
+meaning we can get the complete picture all the way from the Python CLI to the
+Rust core library!<sup id="sf-sourmash-pr-4-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-4" class="simple-footnote" title="Even if py-spy doesn't talk explicitly about Rust, it works very very well, woohoo!">4</a></sup></p>
+<p><code>py-spy</code> is also easy to use:
+stick <code>py-spy record --output search.svg -n --</code> in front of the command,
+and it will generate a flamegraph in <code>search.svg</code>.
+The full command for this PR is</p>
+<div class="highlight"><pre><span></span>py-spy record --output search.svg -n -- sourmash search -o out.csv --scaled <span class="m">2000</span> -k <span class="m">51</span> HSMA.fastq.sig genbank-k51.lca.json.gz
+</pre></div>
+
+
+<h2>Show me the code!</h2>
+<p>OK, OK, sheesh. But it's worth repeating: the code is important, but there are
+many other aspects that are just as important =]</p>
+<h3>Replacing <code>add_hash</code> calls with one <code>add_many</code></h3>
+<p>Let's start at the <a href="https://github.com/dib-lab/sourmash/pull/826/files#diff-adf06d14c535d5b22da9fb3862e4a487"><code>_signatures()</code></a> method on LCA indices.
+This is the original method:</p>
+<div class="highlight"><pre><span></span><span class="nd">@cached_property</span>
+<span class="k">def</span> <span class="nf">_signatures</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="s2">"Create a _signatures member dictionary that contains {idx: minhash}."</span>
+ <span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">MinHash</span>
+
+ <span class="n">minhash</span> <span class="o">=</span> <span class="n">MinHash</span><span class="p">(</span><span class="n">n</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">ksize</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">ksize</span><span class="p">,</span> <span class="n">scaled</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">scaled</span><span class="p">)</span>
+
+ <span class="n">debug</span><span class="p">(</span><span class="s1">'creating signatures for LCA DB...'</span><span class="p">)</span>
+ <span class="n">sigd</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="n">minhash</span><span class="o">.</span><span class="n">copy_and_clear</span><span class="p">)</span>
+
+ <span class="k">for</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">hashval_to_idx</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="k">for</span> <span class="n">vv</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span>
+ <span class="n">sigd</span><span class="p">[</span><span class="n">vv</span><span class="p">]</span><span class="o">.</span><span class="n">add_hash</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
+
+ <span class="n">debug</span><span class="p">(</span><span class="s1">'=> </span><span class="si">{}</span><span class="s1"> signatures!'</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">sigd</span><span class="p">))</span>
+ <span class="k">return</span> <span class="n">sigd</span>
+</pre></div>
+
+
+<p><code>sigd[vv].add_hash(k)</code> is the culprit.
+Each call to <code>.add_hash</code> has to go thru CFFI to reach the extension code,
+and the overhead is significant.
+It is a similar situation to accessing array elements in NumPy:
+it works,
+but it is way slower than using operations that avoid crossing from Python to
+the extension code.
+What we want to do instead is call <code>.add_many(hashes)</code>,
+which takes a list of hashes and process it entirely in Rust
+(ideally. We will get there).</p>
+<p>But, to have a list of hashes, there is another issue with this code.</p>
+<div class="highlight"><pre><span></span><span class="k">for</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">hashval_to_idx</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="k">for</span> <span class="n">vv</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span>
+ <span class="n">sigd</span><span class="p">[</span><span class="n">vv</span><span class="p">]</span><span class="o">.</span><span class="n">add_hash</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
+</pre></div>
+
+
+<p>There are two nested for loops,
+and <code>add_hash</code> is being called with values from the inner loop.
+So... we don't have the list of hashes beforehand.</p>
+<p>But we can change the code a bit to save the hashes for each signature
+in a temporary list,
+and then call <code>add_many</code> on the temporary list.
+Like this:</p>
+<div class="highlight"><pre><span></span><span class="n">temp_vals</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span>
+
+<span class="k">for</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">hashval_to_idx</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="k">for</span> <span class="n">vv</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span>
+ <span class="n">temp_vals</span><span class="p">[</span><span class="n">vv</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
+
+<span class="k">for</span> <span class="n">sig</span><span class="p">,</span> <span class="n">vals</span> <span class="ow">in</span> <span class="n">temp_vals</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="n">sigd</span><span class="p">[</span><span class="n">sig</span><span class="p">]</span><span class="o">.</span><span class="n">add_many</span><span class="p">(</span><span class="n">vals</span><span class="p">)</span>
+</pre></div>
+
+
+<p>There is a trade-off here:
+if we save the hashes in temporary lists,
+will the memory consumption be so high that the runtime gains of calling
+<code>add_many</code> in these temporary lists be cancelled?</p>
+<p>Time to measure it =]</p>
+<table>
+<thead>
+<tr>
+<th align="left">version</th>
+<th align="left">mem</th>
+<th align="left">time</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td align="left">original</td>
+<td align="left">1.5 GB</td>
+<td align="left">160s</td>
+</tr>
+<tr>
+<td align="left"><code>list</code></td>
+<td align="left">1.7GB</td>
+<td align="left">173s</td>
+</tr>
+</tbody>
+</table>
+<p>Wait, it got worse?!?! Building temporary lists only takes time and memory,
+and bring no benefits!</p>
+<p>This mystery goes away when you look at the <a href="https://github.com/dib-lab/sourmash/pull/826/files#diff-2f53b2a5be4083c39a0275847c87f88fR190">add_many method</a>:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">add_many</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">hashes</span><span class="p">):</span>
+ <span class="s2">"Add many hashes in at once."</span>
+ <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">hashes</span><span class="p">,</span> <span class="n">MinHash</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_methodcall</span><span class="p">(</span><span class="n">lib</span><span class="o">.</span><span class="n">kmerminhash_add_from</span><span class="p">,</span> <span class="n">hashes</span><span class="o">.</span><span class="n">_objptr</span><span class="p">)</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="k">for</span> <span class="nb">hash</span> <span class="ow">in</span> <span class="n">hashes</span><span class="p">:</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_methodcall</span><span class="p">(</span><span class="n">lib</span><span class="o">.</span><span class="n">kmerminhash_add_hash</span><span class="p">,</span> <span class="nb">hash</span><span class="p">)</span>
+</pre></div>
+
+
+<p>The first check in the <code>if</code> statement is a shortcut for adding hashes from
+another <code>MinHash</code>, so let's focus on <code>else</code> part...
+And turns out that <code>add_many</code> is lying!
+It doesn't process the <code>hashes</code> in the Rust extension,
+but just loops and call <code>add_hash</code> for each <code>hash</code> in the list.
+That's not going to be any faster than what we were doing in <code>_signatures</code>.</p>
+<p>Time to fix <code>add_many</code>!</p>
+<h3>Oxidizing <code>add_many</code></h3>
+<p>The idea is to change this loop in <code>add_many</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">for</span> <span class="nb">hash</span> <span class="ow">in</span> <span class="n">hashes</span><span class="p">:</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">_methodcall</span><span class="p">(</span><span class="n">lib</span><span class="o">.</span><span class="n">kmerminhash_add_hash</span><span class="p">,</span> <span class="nb">hash</span><span class="p">)</span>
+</pre></div>
+
+
+<p>with a call to a Rust extension function:</p>
+<div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">_methodcall</span><span class="p">(</span><span class="n">lib</span><span class="o">.</span><span class="n">kmerminhash_add_many</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">hashes</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">hashes</span><span class="p">))</span>
+</pre></div>
+
+
+<p><code>self._methodcall</code> is a convenience method defined in <a href="https://github.com/dib-lab/sourmash/blob/c6cbdf0398ef836797492e13371a38373c544ae1/sourmash/utils.py#L24">RustObject</a>
+which translates a method-like call into a function call,
+since our C layer only has functions.
+This is the C prototype for this function:</p>
+<div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">kmerminhash_add_many</span><span class="p">(</span><span class="w"></span>
+<span class="w"> </span><span class="n">KmerMinHash</span><span class="w"> </span><span class="o">*</span><span class="n">ptr</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">uint64_t</span><span class="w"> </span><span class="o">*</span><span class="n">hashes_ptr</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="kt">uintptr_t</span><span class="w"> </span><span class="n">insize</span><span class="w"></span>
+<span class="w"> </span><span class="p">);</span><span class="w"></span>
+</pre></div>
+
+
+<p>You can almost read it as a Python method declaration,
+where <code>KmerMinHash *ptr</code> means the same as the <code>self</code> in Python methods.
+The other two arguments are a common idiom when passing pointers to data in C,
+with <code>insize</code> being how many elements we have in the list.
+<sup id="sf-sourmash-pr-5-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-5" class="simple-footnote" title="Let's not talk about lack of array bounds checks in C...">5</a></sup>.
+<code>CFFI</code> is very good at converting Python lists into pointers of a specific type,
+as long as the type is of a primitive type
+(<code>uint64_t</code> in our case, since each hash is a 64-bit unsigned integer number).</p>
+<p>And the Rust code with the implementation of the function:</p>
+<div class="highlight"><pre><span></span><span class="n">ffi_fn</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">kmerminhash_add_many</span><span class="p">(</span><span class="w"></span>
+<span class="w"> </span><span class="n">ptr</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">KmerMinHash</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">hashes_ptr</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="kt">u64</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">insize</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="p">)</span><span class="w"> </span>-> <span class="nb">Result</span><span class="o"><</span><span class="p">()</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">mh</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="o">!</span><span class="n">ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">());</span><span class="w"></span>
+<span class="w"> </span><span class="o">&</span><span class="k">mut</span><span class="w"> </span><span class="o">*</span><span class="n">ptr</span><span class="w"></span>
+<span class="w"> </span><span class="p">};</span><span class="w"></span>
+
+<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">hashes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="o">!</span><span class="n">hashes_ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">());</span><span class="w"></span>
+<span class="w"> </span><span class="n">slice</span>::<span class="n">from_raw_parts</span><span class="p">(</span><span class="n">hashes_ptr</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u64</span><span class="p">,</span><span class="w"> </span><span class="n">insize</span><span class="p">)</span><span class="w"></span>
+<span class="w"> </span><span class="p">};</span><span class="w"></span>
+
+<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">hash</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">hashes</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="n">mh</span><span class="p">.</span><span class="n">add_hash</span><span class="p">(</span><span class="o">*</span><span class="n">hash</span><span class="p">);</span><span class="w"></span>
+<span class="w"> </span><span class="p">}</span><span class="w"></span>
+
+<span class="w"> </span><span class="nb">Ok</span><span class="p">(())</span><span class="w"></span>
+<span class="p">}</span><span class="w"></span>
+<span class="p">}</span><span class="w"></span>
+</pre></div>
+
+
+<p>Let's break what's happening here into smaller pieces.
+Starting with the function signature:</p>
+<div class="highlight"><pre><span></span><span class="n">ffi_fn</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="k">unsafe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">kmerminhash_add_many</span><span class="p">(</span><span class="w"></span>
+<span class="w"> </span><span class="n">ptr</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">KmerMinHash</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">hashes_ptr</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="kt">u64</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">insize</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="p">)</span><span class="w"> </span>-> <span class="nb">Result</span><span class="o"><</span><span class="p">()</span><span class="o">></span><span class="w"></span>
+</pre></div>
+
+
+<p>The weird <code>ffi_fn! {}</code> syntax around the function is a macro in Rust:
+it changes the final generated code to convert the return value (<code>Result<()></code>) into something that is valid C code (in this case, <code>void</code>).
+What happens if there is an error, then?
+The Rust extension has code for passing back an error code and message to Python,
+as well as capturing panics (when things go horrible bad and the program can't recover)
+in a way that Python can then deal with (raising exceptions and cleaning up).
+It also sets the <code>#[no_mangle]</code> attribute in the function,
+meaning that the final name of the function will follow C semantics (instead of Rust semantics),
+and can be called more easily from C and other languages.
+This <code>ffi_fn!</code> macro comes from <a href="https://github.com/getsentry/symbolic">symbolic</a>,
+a big influence on the design of the Python/Rust bridge in sourmash.</p>
+<p><code>unsafe</code> is the keyword in Rust to disable some checks in the code to allow
+potentially dangerous things (like dereferencing a pointer),
+and it is required to interact with C code.
+<code>unsafe</code> doesn't mean that the code is always unsafe to use:
+it's up to whoever is calling this to verify that valid data is being passed and invariants are being preserved.</p>
+<p>If we remove the <code>ffi_fn!</code> macro and the <code>unsafe</code> keyword,
+we have</p>
+<div class="highlight"><pre><span></span><span class="k">fn</span> <span class="nf">kmerminhash_add_many</span><span class="p">(</span><span class="w"></span>
+<span class="w"> </span><span class="n">ptr</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">KmerMinHash</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">hashes_ptr</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="kt">u64</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="n">insize</span>: <span class="kt">usize</span>
+ <span class="p">);</span><span class="w"></span>
+</pre></div>
+
+
+<p>At this point we can pretty much map between Rust and the C function prototype:</p>
+<div class="highlight"><pre><span></span><span class="kt">void</span><span class="w"> </span><span class="nf">kmerminhash_add_many</span><span class="p">(</span><span class="w"></span>
+<span class="w"> </span><span class="n">KmerMinHash</span><span class="w"> </span><span class="o">*</span><span class="n">ptr</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">uint64_t</span><span class="w"> </span><span class="o">*</span><span class="n">hashes_ptr</span><span class="p">,</span><span class="w"></span>
+<span class="w"> </span><span class="kt">uintptr_t</span><span class="w"> </span><span class="n">insize</span><span class="w"></span>
+<span class="w"> </span><span class="p">);</span><span class="w"></span>
+</pre></div>
+
+
+<p>Some interesting points:</p>
+<ul>
+<li>We use <code>fn</code> to declare a function in Rust.</li>
+<li>The type of an argument comes after the name of the argument in Rust,
+ while it's the other way around in C.
+ Same for the return type (it is omitted in the Rust function, which means it
+ is <code>-> ()</code>, equivalent to a <code>void</code> return type in C).</li>
+<li>In Rust everything is <strong>immutable</strong> by default, so we need to say that we want
+ a mutable pointer to a <code>KmerMinHash</code> item: <code>*mut KmerMinHash</code>).
+ In C everything is mutable by default.</li>
+<li><code>u64</code> in Rust -> <code>uint64_t</code> in C</li>
+<li><code>usize</code> in Rust -> <code>uintptr_t</code> in C</li>
+</ul>
+<p>Let's check the implementation of the function now.
+We start by converting the <code>ptr</code> argument (a raw pointer to a <code>KmerMinHash</code> struct)
+into a regular Rust struct:</p>
+<div class="highlight"><pre><span></span><span class="kd">let</span><span class="w"> </span><span class="n">mh</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="o">!</span><span class="n">ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">());</span><span class="w"></span>
+<span class="w"> </span><span class="o">&</span><span class="k">mut</span><span class="w"> </span><span class="o">*</span><span class="n">ptr</span><span class="w"></span>
+<span class="p">};</span><span class="w"></span>
+</pre></div>
+
+
+<p>This block is asserting that <code>ptr</code> is not a null pointer,
+and if so it dereferences it and store in a mutable reference.
+If it was a null pointer the <code>assert!</code> would panic (which might sound extreme,
+but is way better than continue running because dereferencing a null pointer is
+BAD).
+Note that functions always need all the types in arguments and return values,
+but for variables in the body of the function
+Rust can figure out types most of the time,
+so no need to specify them.</p>
+<p>The next block prepares our list of hashes for use:</p>
+<div class="highlight"><pre><span></span><span class="kd">let</span><span class="w"> </span><span class="n">hashes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="o">!</span><span class="n">hashes_ptr</span><span class="p">.</span><span class="n">is_null</span><span class="p">());</span><span class="w"></span>
+<span class="w"> </span><span class="n">slice</span>::<span class="n">from_raw_parts</span><span class="p">(</span><span class="n">hashes_ptr</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u64</span><span class="p">,</span><span class="w"> </span><span class="n">insize</span><span class="p">)</span><span class="w"></span>
+<span class="p">};</span><span class="w"></span>
+</pre></div>
+
+
+<p>We are again asserting that the <code>hashes_ptr</code> is not a null pointer,
+but instead of dereferencing the pointer like before we use it to create a <code>slice</code>,
+a dynamically-sized view into a contiguous sequence.
+The list we got from Python is a contiguous sequence of size <code>insize</code>,
+and the <code>slice::from_raw_parts</code> function creates a slice from a pointer to data and a size.</p>
+<p>Oh, and can you spot the bug?
+I created the slice using <code>*mut u64</code>,
+but the data is declared as <code>*const u64</code>.
+Because we are in an <code>unsafe</code> block Rust let me change the mutability,
+but I shouldn't be doing that,
+since we don't need to mutate the slice.
+Oops.</p>
+<p>Finally, let's add hashes to our MinHash!
+We need a <code>for</code> loop, and call <code>add_hash</code> for each <code>hash</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">for</span><span class="w"> </span><span class="n">hash</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">hashes</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
+<span class="w"> </span><span class="n">mh</span><span class="p">.</span><span class="n">add_hash</span><span class="p">(</span><span class="o">*</span><span class="n">hash</span><span class="p">);</span><span class="w"></span>
+<span class="p">}</span><span class="w"></span>
+
+<span class="nb">Ok</span><span class="p">(())</span><span class="w"></span>
+</pre></div>
+
+
+<p>We finish the function with <code>Ok(())</code> to indicate no errors occurred.</p>
+<p>Why is calling <code>add_hash</code> here faster than what we were doing before in Python?
+Rust can optimize these calls and generate very efficient native code,
+while Python is an interpreted language and most of the time don't have the same
+guarantees that Rust can leverage to generate the code.
+And, again,
+calling <code>add_hash</code> here doesn't need to cross FFI boundaries or,
+in fact,
+do any dynamic evaluation during runtime,
+because it is all statically analyzed during compilation.</p>
+<h2>Putting it all together</h2>
+<p>And... that's the PR code.
+There are some other unrelated changes that should have been in new PRs,
+but since they were so small it would be more work than necessary.
+OK, that's a lame excuse:
+it's confusing for reviewers to see these changes here,
+so avoid doing that if possible!</p>
+<p>But, did it work?</p>
+<table>
+<thead>
+<tr>
+<th align="left">version</th>
+<th align="left">mem</th>
+<th align="left">time</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td align="left">original</td>
+<td align="left">1.5 GB</td>
+<td align="left">160s</td>
+</tr>
+<tr>
+<td align="left"><code>list</code></td>
+<td align="left">1.7GB</td>
+<td align="left">73s</td>
+</tr>
+</tbody>
+</table>
+<p>We are using 200 MB of extra memory,
+but taking less than half the time it was taking before.
+I think this is a good trade-off,
+and so did the <a href="https://github.com/dib-lab/sourmash/pull/826#pullrequestreview-339020803">reviewer</a> and the PR was approved.</p>
+<p>Hopefully this was useful, 'til next time!</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/103461534713587975">Thread on Mastodon</a></li>
+<li><a href="https://twitter.com/luizirber/status/1215772245928235008">Thread on Twitter</a></li>
+<li><a href="https://lobste.rs/s/xnaugq/oxidizing_sourmash_pr_walkthrough">Lobste.rs submission</a></li>
+</ul>
+<h2>Bonus: <code>list</code> or <code>set</code>?</h2>
+<p>The first version of the PR used a <code>set</code> instead of a <code>list</code> to accumulate hashes.
+Since a <code>set</code> doesn't have repeated elements,
+this could potentially use less memory.
+The code:</p>
+<div class="highlight"><pre><span></span><span class="n">temp_vals</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">set</span><span class="p">)</span>
+
+<span class="k">for</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">hashval_to_idx</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="k">for</span> <span class="n">vv</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span>
+ <span class="n">temp_vals</span><span class="p">[</span><span class="n">vv</span><span class="p">]</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
+
+<span class="k">for</span> <span class="n">sig</span><span class="p">,</span> <span class="n">vals</span> <span class="ow">in</span> <span class="n">temp_vals</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="n">sigd</span><span class="p">[</span><span class="n">sig</span><span class="p">]</span><span class="o">.</span><span class="n">add_many</span><span class="p">(</span><span class="n">vals</span><span class="p">)</span>
+</pre></div>
+
+
+<p>The runtime was again half of the original,
+but...</p>
+<table>
+<thead>
+<tr>
+<th align="left">version</th>
+<th align="left">mem</th>
+<th align="left">time</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td align="left">original</td>
+<td align="left">1.5 GB</td>
+<td align="left">160s</td>
+</tr>
+<tr>
+<td align="left"><code>set</code></td>
+<td align="left">3.8GB</td>
+<td align="left">80s</td>
+</tr>
+<tr>
+<td align="left"><code>list</code></td>
+<td align="left">1.7GB</td>
+<td align="left">73s</td>
+</tr>
+</tbody>
+</table>
+<p>... memory consumption was almost 2.5 times the original! WAT</p>
+<p>The culprit this time? The new <code>kmerminhash_add_many</code> call in the <code>add_many</code>
+method.
+This one:</p>
+<div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">_methodcall</span><span class="p">(</span><span class="n">lib</span><span class="o">.</span><span class="n">kmerminhash_add_many</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">hashes</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">hashes</span><span class="p">))</span>
+</pre></div>
+
+
+<p><code>CFFI</code> doesn't know how to convert a <code>set</code> into something that C understands,
+so we need to call <code>list(hashes)</code> to convert it into a list.
+Since Python (and <code>CFFI</code>) can't know if the data is going to be used later
+<sup id="sf-sourmash-pr-6-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-6" class="simple-footnote" title="something that the memory ownership model in Rust does, BTW">6</a></sup>
+it needs to keep it around
+(and be eventually deallocated by the garbage collector).
+And that's how we get at least double the memory being allocated...</p>
+<p>There is another lesson here.
+If we look at the <code>for</code> loop again:</p>
+<div class="highlight"><pre><span></span><span class="k">for</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">hashval_to_idx</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
+ <span class="k">for</span> <span class="n">vv</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span>
+ <span class="n">temp_vals</span><span class="p">[</span><span class="n">vv</span><span class="p">]</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
+</pre></div>
+
+
+<p>each <code>k</code> is already unique because they are keys in the <code>hashval_to_idx</code> dictionary,
+so the initial assumption
+(that a <code>set</code> might save memory because it doesn't have repeated elements)
+is... irrelevant for the problem =]</p><hr /><h2>Footnotes</h2><ol><li id="sf-sourmash-pr-1"><p>We do have https://asv.readthedocs.io/ set up for micro-benchmarks,
+and now that I think about it...
+I could have started by writing a benchmark for <code>add_many</code>,
+and then showing that it is faster.
+I will add this approach to the sourmash PR checklist =] <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-1-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-pr-2"><p>or triple, if you count C <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-2-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-pr-3"><p>It would be super cool to have the unwinding code from py-spy in heaptrack,
+and be able to see exactly what Python methods/lines of code were calling the
+Rust parts... <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-3-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-pr-4"><p>Even if py-spy doesn't talk explicitly about Rust,
+it works very very well, woohoo! <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-4-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-pr-5"><p>Let's not talk about lack of array bounds checks in C... <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-5-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-pr-6"><p>something that the memory ownership model in Rust does, BTW <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-pr-6-back" class="simple-footnote-back">↩</a></p></li></ol>
+ Fri, 10 Jan 2020 15:00:00 +0000
+
+
+ Gabbleblotchits: Interoperability #rust2020
+ tag:blog.luizirber.org,2019-12-01:/2019/12/01/rust-2020/
+ https://blog.luizirber.org/2019/12/01/rust-2020/
+ <p>In January I wrote a <a href="https://blog.luizirber.org/2019/01/05/rust-2019/">post</a> for the Rust 2019 call for blogs.
+The <a href="https://blog.rust-lang.org/2019/10/29/A-call-for-blogs-2020.html">2020 call</a> is aiming for an RFC and roadmap earlier this time,
+so here is my 2020 post =]</p>
+<h3>Last call review: what happened?</h3>
+<h4>An attribute proc-macro like <code>#[wasm_bindgen]</code> but for FFI</h4>
+<p>This sort of happened... because WebAssembly is growing =]</p>
+<p>I was very excited when <a href="https://hacks.mozilla.org/2019/08/webassembly-interface-types/">Interface Types</a> showed up in August,
+and while it is still very experimental it is moving fast and bringing saner
+paths for interoperability than raw C FFIs.
+David Beazley even point this at the end of his <a href="https://www.youtube.com/watch?v=r-A78RgMhZU">PyCon India keynote</a>,
+talking about how easy is to get information out of a WebAssembly module
+compared to what had to be done for SWIG.</p>
+<p>This doesn't solve the problem where strict C compatibility is required,
+or for platforms where a WebAssembly runtime is not available,
+but I think it is a great solution for scientific software
+(or, at least, for my use cases =]).</p>
+<h4>"More -sys and Rust-like crates for interoperability with the larger ecosystems" and "More (bioinformatics) tools using Rust!"</h4>
+<p>I did some of those this year (<a href="https://crates.io/crates/bbhash-sys">bbhash-sys</a> and <a href="https://crates.io/crates/mqf">mqf</a>),
+and also found some great crates to use in my projects.
+Rust is picking up steam in bioinformatics,
+being used as the primary choice for high quality software
+(like <a href="https://varlociraptor.github.io/">varlociraptor</a>,
+or the many coming from <a href="https://github.com/10XGenomics/">10X Genomics</a>)
+but it is still somewhat hard to find more details
+(I mostly find it on Twitter,
+and sometime Google Scholar alerts).
+It would be great to start bringing this info together,
+which leads to...</p>
+<h4>"A place to find other scientists?"</h4>
+<p>Hey, this one happened! <a href="https://twitter.com/algo_luca/status/1081966759048028162">Luca Palmieri</a> started a conversation on <a href="https://www.reddit.com/r/rust/comments/ae77gt/scientific_computingmachine_learning_do_we_want_a/">reddit</a> and
+the <a href="https://discord.gg/EXTSq4v">#science-and-ai</a> Discord channel on the Rust community server was born!
+I think it works pretty well,
+and Luca also has being doing a great job running <a href="https://github.com/LukeMathWalker/ndarray-koans">workshops</a>
+and guiding the conversation around <a href="https://github.com/rust-ml/discussion">rust-ml</a>.</p>
+<h2>Rust 2021: Interoperability</h2>
+<p>Rust is amazing because it is very good at bringing many concepts and ideas that
+seem contradictory at first,
+but can really shine when <a href="https://rust-lang.github.io/rustconf-2018-keynote/#127">synthesized</a>.
+But can we share this combined wisdom and also improve the situation in other
+places too?
+Despite the "Rewrite it in Rust" meme,
+increased interoperability is something that is already driving a lot of the
+best aspects of Rust:</p>
+<ul>
+<li>
+<p>Interoperability with other languages: as I said before,
+ with WebAssembly (and Rust being having the best toolchain for it)
+ there is a clear route to achieve this,
+ but it will not replace all the software that already exist and can benefit
+ from FFI and C compatibility.
+ Bringing together developers from the many language specific binding
+ generators (<a href="https://github.com/tildeio/helix">helix</a>, <a href="https://github.com/neon-bindings/neon">neon</a>, <a href="https://github.com/rusterlium/rustler">rustler</a>, <a href="https://github.com/PyO3/pyo3">PyO3</a>...) and figuring out what's missing from
+ them (or what is the common parts that can be shared) also seems productive.</p>
+</li>
+<li>
+<p>Interoperability with new and unexplored domains.
+ I think Rust benefits enormously from not focusing only in one domain,
+ and choosing to prioritize CLI, WebAssembly, Networking and Embedded is a good
+ subset to start tackling problems,
+ but how to guide other domains to also use Rust and come up with new
+ contributors and expose missing pieces of the larger picture?</p>
+</li>
+</ul>
+<p>Another point extremely close to interoperability is training.
+A great way to interoperate with other languages and domains is having good
+documentation and material from transitioning into Rust without having to figure
+everything at once.
+Rust documentation is already amazing,
+especially considering the many books published by each working group.
+But... there is a gap on the transitions,
+both from understanding the basics of the language and using it,
+to the progression from beginner to intermediate and expert.</p>
+<p>I see good resources for <a href="https://github.com/yoshuawuyts/rust-for-js-people">JavaScript</a> and <a href="https://github.com/rochacbruno/py2rs">Python</a> developers,
+but we are still covering a pretty small niche:
+programmers curious enough to go learn another language,
+or looking for solutions for problems in their current language.</p>
+<p>Can we bring more people into Rust?
+<a href="https://rustbridge.com/">RustBridge</a> is obviously the reference here,
+but there is space for much,
+much more.
+Using Rust in <a href="https://carpentries.org/">The Carpentries</a> lessons?
+Creating <code>RustOpenSci</code>,
+mirroring the communities of practice of <a href="https://ropensci.org/about/">rOpenSci</a> and <a href="https://www.pyopensci.org/">pyOpenSci</a>?</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/103236549475802733">Thread on Mastodon</a></li>
+<li><a href="https://twitter.com/luizirber/status/1201373423592562690">Thread on Twitter</a></li>
+</ul>
+ Sun, 01 Dec 2019 15:00:00 +0000
+
+
+ PythonClub: Criando dicts a partir de outros dicts
+ tag:pythonclub.com.br,2019-10-01:/crie_dict-a-partir-de-outros-dicts.html
+ https://pythonclub.com.br/crie_dict-a-partir-de-outros-dicts.html
+ <p>Neste tutorial, será abordado o processo de criação de um <em>dict</em> ou dicionário, a partir de um ou mais <em>dicts</em> em Python. </p>
+<p>Como já é de costume da linguagem, isso pode ser feito de várias maneiras diferentes.</p>
+<h2>Abordagem inicial</h2>
+<p>Pra começar, vamos supor que temos os seguintes dicionários:</p>
+<div class="highlight"><pre><span></span><span class="n">dict_1</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'a'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
+ <span class="s1">'b'</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
+<span class="p">}</span>
+
+<span class="n">dict_2</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'b'</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
+ <span class="s1">'c'</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>Como exemplo, vamos criar um novo dicionário chamado <strong>new_dict</strong> com os valores de <strong>dict_1</strong> e <strong>dict_2</strong> logo acima. Uma abordagem bem conhecida é utilizar o método <em>update</em>.</p>
+<div class="highlight"><pre><span></span><span class="n">new_dict</span> <span class="o">=</span> <span class="p">{}</span>
+
+<span class="n">new_dcit</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">dict_1</span><span class="p">)</span>
+<span class="n">new_dcit</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">dict_2</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Assim, temos que <strong>new_dict</strong> será:</p>
+<div class="highlight"><pre><span></span><span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">)</span>
+<span class="p">{</span>
+ <span class="s1">'a'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
+ <span class="s1">'b'</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
+ <span class="s1">'c'</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>Este método funciona bem, porém temos de chamar o método <em>update</em> para cada <em>dict</em> que desejamos mesclar em <strong>new_dict</strong>. Não seria interessante se fosse possível passar todos os <em>dicts</em> necessários já na inicialização de <strong>new_dict</strong>?</p>
+<h3>Novidades do Python 3</h3>
+<p>O Python 3 introduziu uma maneira bem interessante de se fazer isso, utilizando os operadores <code>**</code>.</p>
+<div class="highlight"><pre><span></span><span class="n">new_dict</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="o">**</span><span class="n">dict_1</span><span class="p">,</span>
+ <span class="o">**</span><span class="n">dict_2</span><span class="p">,</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>Assim, de maneira semelhante ao exemplo anterior, temos que <strong>new_dict</strong> será :</p>
+<div class="highlight"><pre><span></span><span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'a'</span><span class="p">])</span>
+<span class="mi">1</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'b'</span><span class="p">])</span>
+<span class="mi">3</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'c'</span><span class="p">])</span>
+<span class="mi">4</span>
+</pre></div>
+
+
+<h2>Cópia real de <em>dicts</em></h2>
+<p>Ao utilizamos o procedimento de inicialização acima, devemos tomar conseiderar alguns fatores. Apenas os valores do primeiro nível serão realmente duplicados no novo dicionário. Como exemplo, vamos alterar uma chave presente em ambos os <em>dicts</em> e verificar se as mesmas possuem o mesmo valor:</p>
+<div class="highlight"><pre><span></span><span class="o">>></span> <span class="n">dict_1</span><span class="p">[</span><span class="s1">'a'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">10</span>
+<span class="o">>></span> <span class="n">new_dict</span><span class="p">[</span><span class="s1">'a'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">11</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">dict_1</span><span class="p">[</span><span class="s1">'a'</span><span class="p">])</span>
+<span class="mi">10</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'a'</span><span class="p">])</span>
+<span class="mi">11</span>
+</pre></div>
+
+
+<p>Porém isso muda quando um dos valores de <strong>dict_1</strong> for uma <em>list</em>, outro <em>dict</em> ou algum objeto complexo. Por exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">dict_3</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'a'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
+ <span class="s1">'b'</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
+ <span class="s1">'c'</span><span class="p">:</span> <span class="p">{</span>
+ <span class="s1">'d'</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
+ <span class="p">}</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>e agora, vamos criar um novo <em>dict</em> a partir desse:</p>
+<div class="highlight"><pre><span></span><span class="n">new_dict</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="o">**</span><span class="n">dict_3</span><span class="p">,</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<p>Como no exemplo anterior, podemos imaginar que foi realizado uma cópia de todos os elementos de <strong>dict_3</strong>, porém isso não é totalmente verdade. O que realmente aconteceu é que foi feita uma cópia <em>superficial</em> dos valores de <strong>dict_3</strong>, ou seja, apenas os valores de <em>primeiro nível</em> foram duplicados. Observe o que acontece quando alteramos o valor do <em>dict</em> presente na chave <strong>c</strong>.</p>
+<div class="highlight"><pre><span></span><span class="o">>></span> <span class="n">new_dict</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">11</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">])</span>
+<span class="mi">11</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">dict_3</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">])</span>
+<span class="mi">11</span>
+<span class="c1"># valor anterior era 5</span>
+</pre></div>
+
+
+<p>No caso da chave <strong>c</strong>, ela contem uma referência para outra estrutura de dados (um <em>dict</em>, no caso). Quando alteramos algum valor de <strong>dict_3['c']</strong>, isso reflete em todos os <em>dict</em> que foram inicializados com <strong>dict_3</strong>. Em outras palavras, deve-se ter cuidado ao inicializar um <em>dict</em> a partir de outros <strong>dicts</strong> quando os mesmos possuírem valores complexos, como <em>list</em>, <em>dict</em> ou outros objetos (os atributos deste objeto não serão duplicados).</p>
+<p>De modo a contornar este inconveniente, podemos utilizar o método <em>deepcopy</em> da <em>lib</em> nativa <a href="https://docs.python.org/2/library/copy.html">copy</a>. Agora, ao inicializarmos <strong>new_dict</strong>:</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">copy</span>
+
+<span class="n">dict_3</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'a'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
+ <span class="s1">'b'</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
+ <span class="s1">'c'</span><span class="p">:</span> <span class="p">{</span>
+ <span class="s1">'d'</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
+ <span class="p">}</span>
+<span class="p">}</span>
+
+<span class="n">new_dict</span> <span class="o">=</span> <span class="n">copy</span><span class="o">.</span><span class="n">deepcopy</span><span class="p">(</span><span class="n">dict_3</span><span class="p">)</span>
+</pre></div>
+
+
+<p>O método <em>deepcopy</em> realiza uma cópia recursiva de cada elemento de <strong>dict_3</strong>, resolvendo nosso problema. Veja mais um exemplo:</p>
+<div class="highlight"><pre><span></span><span class="o">>></span> <span class="n">new_dict</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">11</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">new_dict</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">])</span>
+<span class="mi">11</span>
+<span class="o">>></span> <span class="nb">print</span><span class="p">(</span><span class="n">dict_3</span><span class="p">[</span><span class="s1">'c'</span><span class="p">][</span><span class="s1">'d'</span><span class="p">])</span>
+<span class="mi">5</span>
+<span class="c1"># valor não foi alterado</span>
+</pre></div>
+
+
+<h2>Conclusão</h2>
+<p>Este artigo tenta demonstrar de maneira simples a criação de <em>dicts</em>, utilizando os diversos recursos que a linguagem oferece bem como os prós e contras de cada abordagem. </p>
+<h2>Referências</h2>
+<p>Para mais detalhes e outros exemplos, deem uma olhada neste <em>post</em> do forum da Python Brasil <a href="https://groups.google.com/forum/#!topic/python-brasil/OhUqYQ32M7E">aqui</a>.</p>
+<p>É isso pessoal. Obrigado por ler!</p>
+ Tue, 01 Oct 2019 23:20:29 +0000
+
+
+ Humberto Rocha: Desbravando o pygame 5 - Movimento e Colisão
+ https://humberto.io/pt-br/blog/desbravando-o-pygame-5-movimento-e-colisao/
+ https://humberto.io/pt-br/blog/desbravando-o-pygame-5-movimento-e-colisao/
+ O movimento é uma característica que está presente na maioria dos jogos. Ao saltar entre plataformas, atirar contra a horda de inimigos, pilotar uma nave espacial e correr pelas estradas estamos exercendo movimento, interagindo com o ambiente do jogo, aplicando ações e causando reações.
+Neste capítulo iremos conhecer os conceitos básicos de movimentação de objetos na tela e sua interação com outros elementos através da detecção de colisão.
+Movimento Se você vem acompanhando esta série de postagens, teve um breve exemplo de movimentação na postagem sobre game loop, onde uma bola que se movimentava quicando pela tela foi implementada.
+ Tue, 10 Sep 2019 00:00:00 +0000
+
+
+ Humberto Rocha: Publicando meu primeiro Jogo
+ https://humberto.io/pt-br/blog/publicando-meu-primeiro-jogo/
+ https://humberto.io/pt-br/blog/publicando-meu-primeiro-jogo/
+ Jogos sempre me conectam com tecnologia desde o início.
+Eu e meu pai montamos nosso primeiro computador (um Pentium 286) e a primeira coisa que eu me lembro de fazer, foi jogar os jogos de DOS como Prince of Persia e Lunar Lander. Eu aprendi vários comandos de CLI só para poder jogar os meus jogos favoritos.
+A paixão por jogar e fazer jogos sempre me acompanhou como um hobby. I tenho uma série de posts sobre pygame neste blog onde eu passo pelos conceitos básicos de desenvolvimento de jogos tentando explicar para pessoas que estejam iniciando seu aprendizado na área.
+ Wed, 28 Aug 2019 00:00:00 +0000
+
+
+ PythonClub: Tutorial Django 2.2
+ tag:pythonclub.com.br,2019-06-24:/tutorial-django-2.html
+ https://pythonclub.com.br/tutorial-django-2.html
+ <p>Este tutorial é baseado no <strong>Intro to Django</strong> que fica na parte de baixo da página <a href="https://www.djangoproject.com/start/">start</a> do Django project.</p>
+<p>Até a data deste post o Django está na versão 2.2.2, e requer Python 3.</p>
+<h2>O que você precisa?</h2>
+<p>Python 3.6 ou superior, pip e virtualenv.</p>
+<p>Considere que você tenha instalado Python 3.6 ou superior, <a href="https://pip.readthedocs.io/en/latest/">pip</a> e <a href="https://virtualenv.pypa.io/en/latest/">virtualenv</a>.</p>
+<h2>Criando o ambiente</h2>
+<p>Crie uma pasta com o nome <code>django2-pythonclub</code></p>
+<div class="highlight"><pre><span></span>$ mkdir django2-pythonclub
+$ <span class="nb">cd</span> django2-pythonclub
+</pre></div>
+
+
+<p>A partir de agora vamos considerar esta como a nossa pasta principal.</p>
+<p>Considerando que você está usando <strong>Python 3</strong>, digite</p>
+<div class="highlight"><pre><span></span><span class="err">python3 -m venv .venv</span>
+</pre></div>
+
+
+<p>Lembre-se de colocar esta pasta no seu <code>.gitignore</code>, caso esteja usando.</p>
+<div class="highlight"><pre><span></span><span class="err">echo .venv >> .gitignore</span>
+</pre></div>
+
+
+<p>Depois ative o ambiente digitando</p>
+<div class="highlight"><pre><span></span><span class="err">source .venv/bin/activate</span>
+</pre></div>
+
+
+<blockquote>
+<p>Lembre-se, sempre quando você for mexer no projeto, tenha certeza de ter ativado o <code>virtualenv</code>, executando o comando <code>source .venv/bin/activate</code>. Você deve repetir esse comando toda a vez que você abrir um novo terminal.</p>
+</blockquote>
+<h2>Instalando Django 2.2.2</h2>
+<p>Basta digitar</p>
+<div class="highlight"><pre><span></span><span class="err">pip install django==2.2.2</span>
+</pre></div>
+
+
+<p>Dica: se você digitar <code>pip freeze</code> você verá a versão dos programas instalados.</p>
+<p>É recomendável que você atualize a versão do <code>pip</code></p>
+<div class="highlight"><pre><span></span><span class="err">pip install -U pip</span>
+</pre></div>
+
+
+<p>Se der erro então faça:</p>
+<div class="highlight"><pre><span></span><span class="err">python -m pip install --upgrade pip</span>
+</pre></div>
+
+
+<h2>Instalando mais dependências</h2>
+<p>Eu gosto de usar o <a href="https://django-extensions.readthedocs.io/en/latest/">django-extensions</a> e o <a href="https://github.com/jazzband/django-widget-tweaks">django-widget-tweaks</a>, então digite</p>
+<div class="highlight"><pre><span></span><span class="err">pip install django-extensions django-widget-tweaks python-decouple</span>
+</pre></div>
+
+
+<p><strong>Importante:</strong> você precisa criar um arquivo <code>requirements.txt</code> para instalações futuras do projeto em outro lugar.</p>
+<div class="highlight"><pre><span></span><span class="err">pip freeze > requirements.txt</span>
+</pre></div>
+
+
+<p>Este é o resultado do meu até o dia deste post:</p>
+<div class="highlight"><pre><span></span><span class="p">(.</span><span class="n">venv</span><span class="p">):</span><span class="err">$</span> <span class="n">cat</span> <span class="n">requirements</span><span class="p">.</span><span class="n">txt</span>
+
+<span class="n">django</span><span class="o">-</span><span class="n">extensions</span><span class="o">==</span><span class="mi">2</span><span class="p">.</span><span class="mi">1</span><span class="p">.</span><span class="mi">6</span>
+<span class="n">django</span><span class="o">-</span><span class="n">widget</span><span class="o">-</span><span class="n">tweaks</span><span class="o">==</span><span class="mi">1</span><span class="p">.</span><span class="mi">4</span><span class="p">.</span><span class="mi">3</span>
+<span class="n">python</span><span class="o">-</span><span class="n">decouple</span><span class="o">==</span><span class="mi">3</span><span class="p">.</span><span class="mi">1</span>
+<span class="n">pytz</span><span class="o">==</span><span class="mi">2018</span><span class="p">.</span><span class="mi">9</span>
+<span class="n">six</span><span class="o">==</span><span class="mi">1</span><span class="p">.</span><span class="mi">12</span><span class="p">.</span><span class="mi">0</span>
+</pre></div>
+
+
+<h2>Escondendo a SECRET_KEY e trabalhando com variáveis de ambiente</h2>
+<p>É muito importante que você não deixe sua SECRET_KEY exposta. Então remova-o imediatamente do seu settings.py ANTES mesmo do primeiro commit. Espero que você esteja usando Git.</p>
+<p>Vamos usar o <a href="https://github.com/henriquebastos/python-decouple">python-decouple</a> escrito por <a href="https://henriquebastos.net/">Henrique Bastos</a> para gerenciar nossas variáveis de ambiente. Repare que já instalamos ele logo acima.</p>
+<p>Em seguida você vai precisar criar um arquivo <code>.env</code>, para isso rode o comando a seguir, ele vai criar uma pasta contrib e dentro dele colocar um arquivo <code>env_gen.py</code></p>
+<div class="highlight"><pre><span></span><span class="err">if [ ! -d contrib ]; then mkdir contrib; fi; git clone https://gist.github.com/22626de522f5c045bc63acdb8fe67b24.git contrib/</span>
+<span class="err">rm -rf contrib/.git/ # remova a pasta .git que está dentro de contrib.</span>
+</pre></div>
+
+
+<p>Em seguida rode</p>
+<div class="highlight"><pre><span></span><span class="err">python contrib/env_gen.py</span>
+</pre></div>
+
+
+<p>que ele vai criar o arquivo <code>.env</code>.</p>
+<p>Supondo que você está versionando seu código com Git, é importante que você escreva isso dentro do seu arquivo <code>.gitignore</code>, faça direto pelo terminal</p>
+<div class="highlight"><pre><span></span><span class="err">echo .env >> .gitignore</span>
+<span class="err">echo .venv >> .gitignore</span>
+<span class="err">echo '*.sqlite3' >> .gitignore</span>
+</pre></div>
+
+
+<p>Pronto, agora você pode dar o primeiro commit.</p>
+<h2>Criando o projeto e a App</h2>
+<p>Para criar o projeto digite</p>
+<div class="highlight"><pre><span></span>$ django-admin startproject myproject .
+</pre></div>
+
+
+<p>repare no ponto no final do comando, isto permite que o arquivo <code>manage.py</code> fique nesta mesma pasta <em>django2-pythonclub</em> .</p>
+<p>Agora vamos criar a <em>app</em> <strong>bands</strong>, mas vamos deixar esta <em>app</em> dentro da pasta <em>myproject</em>. Então entre na pasta</p>
+<div class="highlight"><pre><span></span>$ <span class="nb">cd</span> myproject
+</pre></div>
+
+
+<p>e digite</p>
+<div class="highlight"><pre><span></span>$ python ../manage.py startapp bands
+</pre></div>
+
+
+<p>A intenção é que os arquivos tenham a seguinte hierarquia nas pastas:</p>
+<div class="highlight"><pre><span></span><span class="err">.</span>
+<span class="err">├── manage.py</span>
+<span class="err">├── myproject</span>
+<span class="err">│ ├── bands</span>
+<span class="err">│ │ ├── admin.py</span>
+<span class="err">│ │ ├── apps.py</span>
+<span class="err">│ │ ├── models.py</span>
+<span class="err">│ │ ├── tests.py</span>
+<span class="err">│ │ └── views.py</span>
+<span class="err">│ ├── settings.py</span>
+<span class="err">│ ├── urls.py</span>
+<span class="err">│ └── wsgi.py</span>
+<span class="err">└── requirements.txt</span>
+</pre></div>
+
+
+<p>Agora permaneça sempre na pasta <code>django2-pythonclub</code></p>
+<div class="highlight"><pre><span></span><span class="err">cd ..</span>
+</pre></div>
+
+
+<p>e digite</p>
+<div class="highlight"><pre><span></span>$ python manage.py migrate
+</pre></div>
+
+
+<p>para criar a primeira <em>migração</em> (isto cria o banco de dados SQLite), e depois rode a aplicação com</p>
+<div class="highlight"><pre><span></span>$ python manage.py runserver
+</pre></div>
+
+
+<p>e veja que a aplicação já está funcionando. Veja o endereço da url aqui</p>
+<div class="highlight"><pre><span></span><span class="err">Django version 2.2.2, using settings 'myproject.settings'</span>
+<span class="err">Starting development server at http://127.0.0.1:8000/</span>
+<span class="err">Quit the server with CONTROL-C.</span>
+</pre></div>
+
+
+<h2>Editando settings.py</h2>
+<p>Em <code>INSTALLED_APPS</code> acrescente as linhas abaixo.</p>
+<div class="highlight"><pre><span></span><span class="n">INSTALLED_APPS</span> <span class="o">=</span> <span class="p">(</span>
+ <span class="o">...</span>
+ <span class="s1">'widget_tweaks'</span><span class="p">,</span>
+ <span class="s1">'django_extensions'</span><span class="p">,</span>
+ <span class="s1">'myproject.bands'</span><span class="p">,</span>
+<span class="p">)</span>
+</pre></div>
+
+
+<p>E mude também o idioma.</p>
+<p><code>LANGUAGE_CODE = 'pt-br'</code></p>
+<p>E caso você queira o mesmo horário de Brasília-BR</p>
+<p><code>TIME_ZONE = 'America/Sao_Paulo'</code></p>
+<p>Já que falamos do python-decouple, precisamos de mais alguns ajustes</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">decouple</span> <span class="kn">import</span> <span class="n">config</span><span class="p">,</span> <span class="n">Csv</span>
+
+<span class="c1"># SECURITY WARNING: keep the secret key used in production secret!</span>
+<span class="n">SECRET_KEY</span> <span class="o">=</span> <span class="n">config</span><span class="p">(</span><span class="s1">'SECRET_KEY'</span><span class="p">)</span>
+
+<span class="c1"># SECURITY WARNING: don't run with debug turned on in production!</span>
+<span class="n">DEBUG</span> <span class="o">=</span> <span class="n">config</span><span class="p">(</span><span class="s1">'DEBUG'</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">cast</span><span class="o">=</span><span class="nb">bool</span><span class="p">)</span>
+
+<span class="n">ALLOWED_HOSTS</span> <span class="o">=</span> <span class="n">config</span><span class="p">(</span><span class="s1">'ALLOWED_HOSTS'</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="p">[],</span> <span class="n">cast</span><span class="o">=</span><span class="n">Csv</span><span class="p">())</span>
+</pre></div>
+
+
+<p>Veja que é importante manter sua SECRET_KEY bem guardada (em outro lugar).</p>
+<p>Então crie um arquivo <code>.env</code> e guarde sua SECRET_KEY dentro dele, exemplo:</p>
+<div class="highlight"><pre><span></span><span class="err">SECRET_KEY=your_secret_key</span>
+<span class="err">DEBUG=True</span>
+<span class="err">ALLOWED_HOSTS=127.0.0.1,.localhost</span>
+</pre></div>
+
+
+<h2>Editando models.py</h2>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>
+<span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">reverse_lazy</span>
+
+
+<span class="k">class</span> <span class="nc">Band</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
+
+ <span class="sd">"""A model of a rock band."""</span>
+ <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">200</span><span class="p">)</span>
+ <span class="n">can_rock</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">BooleanField</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
+
+ <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
+ <span class="n">ordering</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'name'</span><span class="p">,)</span>
+ <span class="n">verbose_name</span> <span class="o">=</span> <span class="s1">'band'</span>
+ <span class="n">verbose_name_plural</span> <span class="o">=</span> <span class="s1">'bands'</span>
+
+ <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span>
+
+ <span class="k">def</span> <span class="nf">get_absolute_url</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="c1"># retorna a url no formato /bands/1/</span>
+ <span class="k">return</span> <span class="n">reverse_lazy</span><span class="p">(</span><span class="s1">'band_detail'</span><span class="p">,</span> <span class="n">kwargs</span><span class="o">=</span><span class="p">{</span><span class="s1">'pk'</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pk</span><span class="p">})</span>
+
+ <span class="k">def</span> <span class="nf">get_members_count</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="c1"># count members by band</span>
+ <span class="c1"># conta os membros por banda</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">band</span><span class="o">.</span><span class="n">count</span><span class="p">()</span>
+
+
+<span class="k">class</span> <span class="nc">Member</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
+
+ <span class="sd">"""A model of a rock band member."""</span>
+ <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="s2">"Member's name"</span><span class="p">,</span> <span class="n">max_length</span><span class="o">=</span><span class="mi">200</span><span class="p">)</span>
+ <span class="n">instrument</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">choices</span><span class="o">=</span><span class="p">(</span>
+ <span class="p">(</span><span class="s1">'g'</span><span class="p">,</span> <span class="s2">"Guitar"</span><span class="p">),</span>
+ <span class="p">(</span><span class="s1">'b'</span><span class="p">,</span> <span class="s2">"Bass"</span><span class="p">),</span>
+ <span class="p">(</span><span class="s1">'d'</span><span class="p">,</span> <span class="s2">"Drums"</span><span class="p">),</span>
+ <span class="p">(</span><span class="s1">'v'</span><span class="p">,</span> <span class="s2">"Vocal"</span><span class="p">),</span>
+ <span class="p">(</span><span class="s1">'p'</span><span class="p">,</span> <span class="s2">"Piano"</span><span class="p">),</span>
+ <span class="p">),</span>
+ <span class="n">max_length</span><span class="o">=</span><span class="mi">1</span>
+ <span class="p">)</span>
+
+ <span class="n">band</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s2">"Band"</span><span class="p">,</span> <span class="n">related_name</span><span class="o">=</span><span class="s1">'band'</span><span class="p">,</span> <span class="n">on_delete</span><span class="o">=</span><span class="n">models</span><span class="o">.</span><span class="n">CASCADE</span><span class="p">)</span>
+
+ <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
+ <span class="n">ordering</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'name'</span><span class="p">,)</span>
+ <span class="n">verbose_name</span> <span class="o">=</span> <span class="s1">'member'</span>
+ <span class="n">verbose_name_plural</span> <span class="o">=</span> <span class="s1">'members'</span>
+
+ <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span>
+</pre></div>
+
+
+<p>Tem algumas coisas que eu não estou explicando aqui para o tutorial ficar curto, mas uma coisa importante é que, como nós editamos o models.py vamos precisar criar um arquivo de migração do novo modelo. Para isso digite</p>
+<div class="highlight"><pre><span></span><span class="err">python manage.py makemigrations</span>
+<span class="err">python manage.py migrate</span>
+</pre></div>
+
+
+<p>O primeiro comando cria o arquivo de migração e o segundo o executa, criando as tabelas no banco de dados.</p>
+<h2>Editando urls.py</h2>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">include</span><span class="p">,</span> <span class="n">path</span>
+<span class="kn">from</span> <span class="nn">myproject.bands</span> <span class="kn">import</span> <span class="n">views</span> <span class="k">as</span> <span class="n">v</span>
+<span class="kn">from</span> <span class="nn">django.contrib</span> <span class="kn">import</span> <span class="n">admin</span>
+
+<span class="n">app_name</span> <span class="o">=</span> <span class="s1">'bands'</span>
+
+<span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
+ <span class="n">path</span><span class="p">(</span><span class="s1">''</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">home</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'home'</span><span class="p">),</span>
+ <span class="c1"># path('bands/', v.band_list, name='bands'),</span>
+ <span class="c1"># path('bands/<int:pk>/', v.band_detail, name='band_detail'),</span>
+ <span class="c1"># path('bandform/', v.BandCreate.as_view(), name='band_form'),</span>
+ <span class="c1"># path('memberform/', v.MemberCreate.as_view(), name='member_form'),</span>
+ <span class="c1"># path('contact/', v.band_contact, name='contact'),</span>
+ <span class="c1"># path('protected/', v.protected_view, name='protected'),</span>
+ <span class="c1"># path('accounts/login/', v.message),</span>
+ <span class="n">path</span><span class="p">(</span><span class="s1">'admin/'</span><span class="p">,</span> <span class="n">admin</span><span class="o">.</span><span class="n">site</span><span class="o">.</span><span class="n">urls</span><span class="p">),</span>
+<span class="p">]</span>
+</pre></div>
+
+
+<p>Obs: deixei as demais urls comentada porque precisa da função em views.py para que cada url funcione. Descomente cada url somente depois que você tiver definido a função em classe em views.py a seguir.</p>
+<h2>Editando views.py</h2>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.shortcuts</span> <span class="kn">import</span> <span class="n">render</span>
+<span class="kn">from</span> <span class="nn">django.http</span> <span class="kn">import</span> <span class="n">HttpResponse</span>
+<span class="kn">from</span> <span class="nn">django.contrib.auth.decorators</span> <span class="kn">import</span> <span class="n">login_required</span>
+<span class="kn">from</span> <span class="nn">django.views.generic</span> <span class="kn">import</span> <span class="n">CreateView</span>
+<span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">reverse_lazy</span>
+<span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Band</span><span class="p">,</span> <span class="n">Member</span>
+<span class="c1"># from .forms import BandContactForm, BandForm, MemberForm</span>
+</pre></div>
+
+
+<p>Obs: Deixei a última linha comentada porque ainda não chegamos em forms.</p>
+<p>A função a seguir retorna um <strong>HttpResponse</strong>, ou seja, uma mensagem simples no navegador.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">home</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">HttpResponse</span><span class="p">(</span><span class="s1">'Welcome to the site!'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>A próxima função (use uma ou outra) renderiza um template, uma página html no navegador.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">home</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">'home.html'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>A função <code>band_list</code> retorna todas as bandas.</p>
+<p>Para fazer a <strong>busca</strong> por nome de banda usamos o comando <code>search = request.GET.get('search_box')</code>, onde <code>search_box</code> é o nome do campo no template <strong>band_list.html</strong>.</p>
+<p>E os nomes são retornados a partir do comando <code>bands = bands.filter(name__icontains=search)</code>. Onde <code>icontains</code> procura um texto que contém a palavra, ou seja, você pode digitar o nome incompleto (ignora maiúsculo ou minúsculo).</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">band_list</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="sd">""" A view of all bands. """</span>
+ <span class="n">bands</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">search</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">GET</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'search_box'</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">search</span><span class="p">:</span>
+ <span class="n">bands</span> <span class="o">=</span> <span class="n">bands</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">name__icontains</span><span class="o">=</span><span class="n">search</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">'bands/band_list.html'</span><span class="p">,</span> <span class="p">{</span><span class="s1">'bands'</span><span class="p">:</span> <span class="n">bands</span><span class="p">})</span>
+</pre></div>
+
+
+<p>Em urls.py pode descomentar a linha a seguir:</p>
+<div class="highlight"><pre><span></span><span class="n">path</span><span class="p">(</span><span class="s1">'bands/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">band_list</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'bands'</span><span class="p">),</span>
+</pre></div>
+
+
+<p>A função <code>band_contact</code> mostra como tratar um formulário na view. Esta função requer <code>BandContactForm</code>, explicado em forms.py.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">band_contact</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="sd">""" A example of form """</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'POST'</span><span class="p">:</span>
+ <span class="n">form</span> <span class="o">=</span> <span class="n">BandContactForm</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">POST</span><span class="p">)</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="n">form</span> <span class="o">=</span> <span class="n">BandContactForm</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">'bands/band_contact.html'</span><span class="p">,</span> <span class="p">{</span><span class="s1">'form'</span><span class="p">:</span> <span class="n">form</span><span class="p">})</span>
+</pre></div>
+
+
+<p>Em urls.py pode descomentar a linha a seguir:</p>
+<div class="highlight"><pre><span></span><span class="n">path</span><span class="p">(</span><span class="s1">'contact/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">band_contact</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'contact'</span><span class="p">),</span>
+</pre></div>
+
+
+<p>A função <code>band_detail</code> retorna todos os membros de cada banda, usando o <code>pk</code> da banda junto com o comando <code>filter</code> em members.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">band_detail</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">):</span>
+ <span class="sd">""" A view of all members by bands. """</span>
+ <span class="n">band</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">pk</span><span class="o">=</span><span class="n">pk</span><span class="p">)</span>
+ <span class="n">members</span> <span class="o">=</span> <span class="n">Member</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">band</span><span class="o">=</span><span class="n">band</span><span class="p">)</span>
+ <span class="n">context</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'members'</span><span class="p">:</span> <span class="n">members</span><span class="p">,</span> <span class="s1">'band'</span><span class="p">:</span> <span class="n">band</span><span class="p">}</span>
+ <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">'bands/band_detail.html'</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Em urls.py pode descomentar a linha a seguir:</p>
+<div class="highlight"><pre><span></span><span class="n">path</span><span class="p">(</span><span class="s1">'bands/<int:pk>/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">band_detail</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'band_detail'</span><span class="p">),</span>
+</pre></div>
+
+
+<p><code>BandCreate</code> e <code>MemberCreate</code> usam o <a href="https://docs.djangoproject.com/en/2.2/ref/class-based-views/">Class Based View</a> para tratar formulário de uma forma mais simplificada usando a classe <code>CreateView</code>. O <code>reverse_lazy</code> serve para tratar a url de retorno de página.</p>
+<p>As classes a seguir requerem <code>BandForm</code> e <code>MemberForm</code>, explicado em forms.py.</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">BandCreate</span><span class="p">(</span><span class="n">CreateView</span><span class="p">):</span>
+ <span class="n">model</span> <span class="o">=</span> <span class="n">Band</span>
+ <span class="n">form_class</span> <span class="o">=</span> <span class="n">BandForm</span>
+ <span class="n">template_name</span> <span class="o">=</span> <span class="s1">'bands/band_form.html'</span>
+ <span class="n">success_url</span> <span class="o">=</span> <span class="n">reverse_lazy</span><span class="p">(</span><span class="s1">'bands'</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">MemberCreate</span><span class="p">(</span><span class="n">CreateView</span><span class="p">):</span>
+ <span class="n">model</span> <span class="o">=</span> <span class="n">Member</span>
+ <span class="n">form_class</span> <span class="o">=</span> <span class="n">MemberForm</span>
+ <span class="n">template_name</span> <span class="o">=</span> <span class="s1">'bands/member_form.html'</span>
+ <span class="n">success_url</span> <span class="o">=</span> <span class="n">reverse_lazy</span><span class="p">(</span><span class="s1">'bands'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Em urls.py pode descomentar a linha a seguir:</p>
+<div class="highlight"><pre><span></span><span class="n">path</span><span class="p">(</span><span class="s1">'bandform/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">BandCreate</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span> <span class="n">name</span><span class="o">=</span><span class="s1">'band_form'</span><span class="p">),</span>
+<span class="n">path</span><span class="p">(</span><span class="s1">'memberform/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">MemberCreate</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span> <span class="n">name</span><span class="o">=</span><span class="s1">'member_form'</span><span class="p">),</span>
+</pre></div>
+
+
+<p>A próxima função requer que você entre numa página somente quando estiver logado.</p>
+<p><code>[@login_required](https://docs.djangoproject.com/en/2.2/topics/auth/default/#the-login-required-decorator)</code> é um <strong>decorator</strong>.</p>
+<p><code>login_url='/accounts/login/'</code> é página de erro, ou seja, quando o usuário não conseguiu logar.</p>
+<p>E <code>render(request, 'bands/protected.html',...</code> é página de sucesso.</p>
+<div class="highlight"><pre><span></span><span class="nd">@login_required</span><span class="p">(</span><span class="n">login_url</span><span class="o">=</span><span class="s1">'/accounts/login/'</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">protected_view</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="sd">""" A view that can only be accessed by logged-in users """</span>
+ <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">'bands/protected.html'</span><span class="p">,</span> <span class="p">{</span><span class="s1">'current_user'</span><span class="p">:</span> <span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="p">})</span>
+</pre></div>
+
+
+<p><code>HttpResponse</code> retorna uma mensagem simples no navegador sem a necessidade de um template.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">message</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="sd">""" Message if is not authenticated. Simple view! """</span>
+ <span class="k">return</span> <span class="n">HttpResponse</span><span class="p">(</span><span class="s1">'Access denied!'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Em urls.py pode descomentar a linha a seguir:</p>
+<div class="highlight"><pre><span></span><span class="n">path</span><span class="p">(</span><span class="s1">'protected/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">protected_view</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'protected'</span><span class="p">),</span>
+<span class="n">path</span><span class="p">(</span><span class="s1">'accounts/login/'</span><span class="p">,</span> <span class="n">v</span><span class="o">.</span><span class="n">message</span><span class="p">),</span>
+</pre></div>
+
+
+<h2>Comandos básicos do manage.py</h2>
+<p>Para criar novas migrações com base nas alterações feitas nos seus modelos</p>
+<p><code>$ python manage.py makemigrations bands</code></p>
+<p>Obs: talvez dê erro porque está faltando coisas de forms.py, explicado mais abaixo.</p>
+<p>Para aplicar as migrações</p>
+<p><code>$ python manage.py migrate</code></p>
+<p>Para criar um usuário e senha para o admin</p>
+<p><code>$ python manage.py createsuperuser</code></p>
+<p>Para rodar a aplicação localmente</p>
+<p><code>$ python manage.py runserver</code></p>
+<p>Após criar um super usuário você pode entrar em localhost:8000/admin</p>
+<p>Obs: Se você entrar agora em localhost:8000 vai faltar o template home.html. Explicado mais abaixo.</p>
+<h2>shell_plus</h2>
+<p>É o <strong>interpretador interativo do python</strong> rodando <strong>via terminal</strong> direto na aplicação do django.</p>
+<p>Com o comando a seguir abrimos o shell do Django.</p>
+<p><code>$ python manage.py shell</code></p>
+<p>Mas se você está usando o django-extensions (mostrei como configurá-lo no settings.py), então basta digitar</p>
+<p><code>$ python manage.py shell_plus</code></p>
+<p>Veja a seguir como inserir dados direto pelo shell.</p>
+<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="kn">from</span> <span class="nn">myproject.bands.models</span> <span class="kn">import</span> <span class="n">Band</span><span class="p">,</span> <span class="n">Member</span>
+<span class="o">>>></span> <span class="c1"># Com django-extensions não precisa fazer o import</span>
+<span class="o">>>></span> <span class="c1"># criando o objeto e salvando</span>
+<span class="o">>>></span> <span class="n">band</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'Metallica'</span><span class="p">)</span>
+<span class="o">>>></span> <span class="n">band</span><span class="o">.</span><span class="n">name</span>
+<span class="o">>>></span> <span class="n">band</span><span class="o">.</span><span class="n">can_rock</span>
+<span class="o">>>></span> <span class="n">band</span><span class="o">.</span><span class="n">id</span>
+<span class="o">>>></span> <span class="c1"># criando uma instancia da banda a partir do id</span>
+<span class="o">>>></span> <span class="n">b</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">band</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
+<span class="o">>>></span> <span class="c1"># criando uma instancia do Membro e associando o id da banda a ela</span>
+<span class="o">>>></span> <span class="n">m</span> <span class="o">=</span> <span class="n">Member</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'James Hetfield'</span><span class="p">,</span> <span class="n">instrument</span><span class="o">=</span><span class="s1">'b'</span><span class="p">,</span> <span class="n">band</span><span class="o">=</span><span class="n">b</span><span class="p">)</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">name</span>
+<span class="o">>>></span> <span class="c1"># retornando o instrumento</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">instrument</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">get_instrument_display</span><span class="p">()</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">band</span>
+<span class="o">>>></span> <span class="c1"># salvando</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+<span class="o">>>></span> <span class="c1"># listando todas as bandas</span>
+<span class="o">>>></span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+<span class="o">>>></span> <span class="c1"># listando todos os membros</span>
+<span class="o">>>></span> <span class="n">Member</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+<span class="o">>>></span> <span class="c1"># criando mais uma banda</span>
+<span class="o">>>></span> <span class="n">band</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'The Beatles'</span><span class="p">)</span>
+<span class="o">>>></span> <span class="n">band</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'The Beatles'</span><span class="p">)</span>
+<span class="o">>>></span> <span class="n">band</span><span class="o">.</span><span class="n">id</span>
+<span class="o">>>></span> <span class="n">b</span> <span class="o">=</span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">band</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
+<span class="o">>>></span> <span class="c1"># criando mais um membro</span>
+<span class="o">>>></span> <span class="n">m</span> <span class="o">=</span> <span class="n">Member</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'John Lennon'</span><span class="p">,</span> <span class="n">instrument</span><span class="o">=</span><span class="s1">'v'</span><span class="p">,</span> <span class="n">band</span><span class="o">=</span><span class="n">b</span><span class="p">)</span>
+<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+<span class="o">>>></span> <span class="c1"># listando tudo novamente</span>
+<span class="o">>>></span> <span class="n">Band</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+<span class="o">>>></span> <span class="n">Member</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+<span class="o">>>></span> <span class="n">exit</span><span class="p">()</span>
+</pre></div>
+
+
+<h2>Criando os templates</h2>
+<p>Você pode criar os templates com os comandos a seguir...</p>
+<div class="highlight"><pre><span></span>$ mkdir -p myproject/bands/templates/bands
+$ touch myproject/bands/templates/<span class="o">{</span>menu,base,home<span class="o">}</span>.html
+$ touch myproject/bands/templates/bands/<span class="o">{</span>band_list,band_detail,band_form,band_contact,member_form,protected<span class="o">}</span>.html
+</pre></div>
+
+
+<p>... ou pegar os templates já prontos direto do Github.</p>
+<div class="highlight"><pre><span></span><span class="err">mkdir -p myproject/bands/templates/bands</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/base.html -P myproject/bands/templates/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/home.html -P myproject/bands/templates/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/menu.html -P myproject/bands/templates/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/band_contact.html -P myproject/bands/templates/bands/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/band_detail.html -P myproject/bands/templates/bands/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/band_form.html -P myproject/bands/templates/bands/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/band_list.html -P myproject/bands/templates/bands/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/member_form.html -P myproject/bands/templates/bands/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/protected.html -P myproject/bands/templates/bands/</span>
+</pre></div>
+
+
+<h2>forms.py</h2>
+<p><code>$ touch myproject/bands/forms.py</code></p>
+<p>Edite o forms.py.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django</span> <span class="kn">import</span> <span class="n">forms</span>
+<span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Band</span><span class="p">,</span> <span class="n">Member</span>
+
+
+<span class="k">class</span> <span class="nc">BandContactForm</span><span class="p">(</span><span class="n">forms</span><span class="o">.</span><span class="n">Form</span><span class="p">):</span>
+ <span class="n">subject</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
+ <span class="n">message</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">widget</span><span class="o">=</span><span class="n">forms</span><span class="o">.</span><span class="n">Textarea</span><span class="p">)</span>
+ <span class="n">sender</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">EmailField</span><span class="p">()</span>
+ <span class="n">cc_myself</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">BooleanField</span><span class="p">(</span><span class="n">required</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">BandForm</span><span class="p">(</span><span class="n">forms</span><span class="o">.</span><span class="n">ModelForm</span><span class="p">):</span>
+
+ <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
+ <span class="n">model</span> <span class="o">=</span> <span class="n">Band</span>
+ <span class="n">fields</span> <span class="o">=</span> <span class="s1">'__all__'</span>
+
+
+<span class="k">class</span> <span class="nc">MemberForm</span><span class="p">(</span><span class="n">forms</span><span class="o">.</span><span class="n">ModelForm</span><span class="p">):</span>
+
+ <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
+ <span class="n">model</span> <span class="o">=</span> <span class="n">Member</span>
+ <span class="n">fields</span> <span class="o">=</span> <span class="s1">'__all__'</span>
+</pre></div>
+
+
+<p>Lembra que eu deixei o código comentado em views.py?</p>
+<p>Descomente ele por favor</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">.forms</span> <span class="kn">import</span> <span class="n">BandContactForm</span><span class="p">,</span> <span class="n">BandForm</span><span class="p">,</span> <span class="n">MemberForm</span>
+</pre></div>
+
+
+<h2>admin.py</h2>
+<p>Criamos uma customização para o admin onde em members aparece um filtro por bandas.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.contrib</span> <span class="kn">import</span> <span class="n">admin</span>
+<span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Band</span><span class="p">,</span> <span class="n">Member</span>
+
+
+<span class="k">class</span> <span class="nc">MemberAdmin</span><span class="p">(</span><span class="n">admin</span><span class="o">.</span><span class="n">ModelAdmin</span><span class="p">):</span>
+ <span class="sd">"""Customize the look of the auto-generated admin for the Member model."""</span>
+ <span class="n">list_display</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'name'</span><span class="p">,</span> <span class="s1">'instrument'</span><span class="p">)</span>
+ <span class="n">list_filter</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'band'</span><span class="p">,)</span>
+
+
+<span class="n">admin</span><span class="o">.</span><span class="n">site</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">Band</span><span class="p">)</span> <span class="c1"># Use the default options</span>
+<span class="n">admin</span><span class="o">.</span><span class="n">site</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">Member</span><span class="p">,</span> <span class="n">MemberAdmin</span><span class="p">)</span> <span class="c1"># Use the customized options</span>
+</pre></div>
+
+
+<h2>Carregando dados de um CSV</h2>
+<p>Vamos baixar alguns arquivos para criar os dados no banco a partir de um CSV.</p>
+<div class="highlight"><pre><span></span><span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/create_data.py</span>
+<span class="err">mkdir fix</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/fix/bands.csv -P fix/</span>
+<span class="err">wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/fix/members.csv -P fix/</span>
+</pre></div>
+
+
+<p>Estando na pasta principal, rode o comando</p>
+<div class="highlight"><pre><span></span><span class="err">python create_data.py</span>
+</pre></div>
+
+
+<p>que ele vai carregar alguns dados pra você.</p>
+<p>Veja o código de <a href="https://github.com/rg3915/django2-pythonclub/blob/master/create_data.py">create_data.py</a>.</p>
+<p>Veja o código completo em https://github.com/rg3915/django2-pythonclub</p>
+<p><code>git clone https://github.com/rg3915/django2-pythonclub.git</code></p>
+ Tue, 25 Jun 2019 01:00:00 +0000
+
+
+ Gabbleblotchits: Scientific Rust #rust2019
+ tag:blog.luizirber.org,2019-01-05:/2019/01/05/rust-2019/
+ https://blog.luizirber.org/2019/01/05/rust-2019/
+ <p>The Rust community requested feedback last year for where the language should go
+in 2018, and now they are running it again for 2019.
+Last year I was too new in Rust to organize a blog post,
+but after an year using it I feel more comfortable writing this!</p>
+<p>(Check my previous post about <a href="https://blog.luizirber.org/2018/08/23/sourmash-rust/">replacing the C++ core in sourmash with Rust</a> for more details on how I spend my year in Rust).</p>
+<h2>What counts as "scientific Rust"?</h2>
+<p>Anything that involves doing science using computers counts as
+scientific programming. It includes from embedded software
+<a href="https://www.youtube.com/watch?v=y5Yd3FC-kh8">running on satellites</a> to climate models
+running in supercomputers, from shell scripts running tools in a pipeline to
+data analysis using notebooks.</p>
+<p>It also makes the discussion harder, because it's too general! But it is very
+important to keep in mind, because scientists are not your regular user: they
+are highly qualified in their field of expertise, and they are also pushing the
+boundaries of what we know (and this might need flexibility in their tools).</p>
+<p>In this post I will be focusing more in two areas: array computing (what most
+people consider 'scientific programming' to be) and "data structures".</p>
+<h3>Array computing</h3>
+<p>This one is booming in the last couple of years due to industry interest in data
+sciences and deep learning (where they will talk about tensors instead of arrays),
+and has its roots in models running in supercomputers (a field where Fortran is
+still king!). Data tends to be quite regular (representable with matrices) and
+amenable to parallel processing.</p>
+<p>A good example is the SciPy stack in Python, built on top of NumPy and SciPy.
+The adoption of the SciPy stack (both in academia and industry) is staggering,
+and many <a href="https://github.com/cupy/cupy">alternative implementations</a> try to provide a NumPy-like API to try to
+capture its mindshare.</p>
+<p>This is the compute-intensive side science (be it CPU or GPU/TPU), and also the kind
+of data that pushed CPU evolution and is still very important in defining policy
+in scientific computing funding (see countries competing for the largest
+supercomputers and measuring performance in floating point operations per second).</p>
+<h3>Data structures for efficient data representation</h3>
+<p>For data that is not so regular the situation is a bit different. I'll use
+bioinformatics as an example: the data we get out of nucleotide sequencers is usually
+represented by long strings (of ACGT), and algorithms will do a lot of string processing
+(be it building string-overlap graphs for assembly, or searching for substrings
+in large collections). This is only one example: there are many analyses that
+will work with other types of data, and most of them don't have a
+universal data representation as in the array computing case.</p>
+<p>This is the memory-intensive science, and it's hard to measure performance in
+floating point operations because... most of the time you're not even using
+floating point numbers. It also suffers from limited data locality (which is
+almost a prerequisite for compute-intensive performance).</p>
+<h2>High performance core, interactive API</h2>
+<p>There is something common in both cases: while performance-intensive
+code is implemented in C/C++/Fortran, users usually interact with the API from
+other languages (especially Python or R) because it's faster to iterate and
+explore the data, and many of the tools already available in these languages are
+very helpful for these tasks (think Jupyter/pandas or RStudio/tidyverse).
+These languages are used to define the computation, but it is a lower-level core
+library that drives it (NumPy or Tensorflow follow this idea, for example).</p>
+<h2>How to make Rust better for science?</h2>
+<p>The biggest barrier to learning Rust is the ownership model, and while we can
+agree it is an important feature it is also quite daunting for newcomers,
+especially if they don't have previous programming experience and exposure to
+what bugs are being prevented. I don't see it being the first language we teach
+to scientists any time soon, because the majority of scientists are not system
+programmers, and have very different expectations for a programming language.
+That doesn't mean that they can't benefit from Rust!</p>
+<p>Rust is already great for building the performance-intensive parts,
+and thanks to Cargo it is also a better alternative for sharing this code around,
+since they tend to get 'stuck' inside Python or R packages.
+And the 'easy' approach of vendoring C/C++ instead of having packages make it
+hard to keep track of changes and doesn't encourage reusable code.</p>
+<p>And, of course, if this code is Rust instead of C/C++ it also means that Rust
+users can use them directly, without depending on the other languages. Seems
+like a good way to bootstrap a scientific community in Rust =]</p>
+<h2>What I would like to see in 2019?</h2>
+<h3>An attribute proc-macro like <code>#[wasm_bindgen]</code> but for FFI</h3>
+<p>While FFI is an integral part of Rust goals (interoperability with C/C++), I
+have serious envy of the structure and tooling developed for WebAssembly! (Even
+more now that it works in stable too)</p>
+<p>We already have <code>#[no_mangle]</code> and <code>pub extern "C"</code>, but they are quite
+low-level. I would love to see something closer to what wasm-bindgen does,
+and define some traits (like <a href="https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/convert/trait.IntoWasmAbi.html"><code>IntoWasmAbi</code></a>) to make it easier to
+pass more complex data types through the FFI.</p>
+<p>I know it's not that simple, and there are different design restrictions than
+WebAssembly to take into account... The point here is not having the perfect
+solution for all use cases, but something that serves as an entry point and helps
+to deal with the complexity while you're still figuring out all the quirks and
+traps of FFI. You can still fallback and have more control using the lower-level
+options when the need rises.</p>
+<h3>More -sys and Rust-like crates for interoperability with the larger ecosystems</h3>
+<p>There are new projects bringing more interoperability to <a href="https://arrow.apache.org/">dataframes</a> and <a href="https://xnd.io/">tensors</a>.
+While this ship has already sailed and they are implemented in C/C++,
+it would be great to be a first-class citizen,
+and not reinvent the wheel.
+(Note: the arrow project already have pretty good Rust support!)</p>
+<p>In my own corner (bioinformatics), the <a href="https://github.com/rust-bio">Rust-bio community</a> is doing a
+great job of wrapping <a href="https://github.com/rust-bio/rust-htslib">useful libraries in C/C++</a> and exposing them to
+Rust (and also a shout-out to 10X Genomics for doing this work for
+<a href="https://github.com/10XGenomics/rust-bwa">other tools</a> while also contributing to Rust-bio!).</p>
+<h3>More (bioinformatics) tools using Rust!</h3>
+<p>We already have great examples like <a href="https://github.com/onecodex/finch-rs">finch</a> and <a href="https://github.com/natir/yacrd">yacrd</a>,
+since Rust is great for single binary distribution of programs.
+And with bioinformatics focusing so much in independent tools chained together in workflows,
+I think we can start convincing people to try it out =]</p>
+<h3>A place to find other scientists?</h3>
+<p>Another idea is to draw inspiration from <a href="https://ropensci.org/about/">rOpenSci</a> and have a Rust equivalent,
+where people can get feedback about their projects and how to better integrate it with other crates.
+This is quite close to the working group idea,
+but I think it would serve more as a gateway to other groups,
+more focused on developing entry-level docs and bringing more scientists to the
+community.</p>
+<h2>Final words</h2>
+<p>In the end, I feel like this post ended up turning into my 'wishful TODO list'
+for 2019, but I would love to find more people sharing these goals (or willing
+to take any of this and just run with it, I do have a PhD to finish! =P)</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/101367104235280253">Thread on Mastodon</a></li>
+<li><a href="https://twitter.com/luizirber/status/1081729107170193408">Thread on Twitter</a></li>
+</ul>
+ Sat, 05 Jan 2019 19:00:00 +0000
+
+
+ PythonClub: Algoritmos de Ordenação
+ tag:pythonclub.com.br,2018-11-29:/algoritmos-ordenacao.html
+ https://pythonclub.com.br/algoritmos-ordenacao.html
+ <p>Fala pessoal, tudo bom?</p>
+<p>Nos vídeos abaixo, vamos aprender como implementar alguns dos algoritmos de ordenação usando Python.</p>
+<div class="section" id="bubble-sort">
+<h2>Bubble Sort</h2>
+<p>Como o algoritmo funciona: Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=Doy64STkwlI&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r&t=0s&index=3">https://www.youtube.com/watch?v=Doy64STkwlI</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=B0DFF0fE4rk&index=3&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r">https://www.youtube.com/watch?v=B0DFF0fE4rk</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Código do algoritmo</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">sort</span><span class="p">(</span><span class="n">array</span><span class="p">):</span>
+
+ <span class="k">for</span> <span class="n">final</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">),</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span>
+ <span class="n">exchanging</span> <span class="o">=</span> <span class="kc">False</span>
+
+ <span class="k">for</span> <span class="n">current</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">final</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">array</span><span class="p">[</span><span class="n">current</span><span class="p">]</span> <span class="o">></span> <span class="n">array</span><span class="p">[</span><span class="n">current</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]:</span>
+ <span class="n">array</span><span class="p">[</span><span class="n">current</span> <span class="o">+</span> <span class="mi">1</span><span class="p">],</span> <span class="n">array</span><span class="p">[</span><span class="n">current</span><span class="p">]</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="n">current</span><span class="p">],</span> <span class="n">array</span><span class="p">[</span><span class="n">current</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
+ <span class="n">exchanging</span> <span class="o">=</span> <span class="kc">True</span>
+
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">exchanging</span><span class="p">:</span>
+ <span class="k">break</span>
+</pre></div>
+</div>
+<div class="section" id="selection-sort">
+<h2>Selection Sort</h2>
+<p>Como o algoritmo funciona: Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=vHxtP9BC-AA&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r&index=4">https://www.youtube.com/watch?v=vHxtP9BC-AA</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=0ORfCwwhF_I&index=5&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r&index=5">https://www.youtube.com/watch?v=0ORfCwwhF_I</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Código do algoritmo</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">sort</span><span class="p">(</span><span class="n">array</span><span class="p">):</span>
+ <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">)):</span>
+ <span class="n">min_index</span> <span class="o">=</span> <span class="n">index</span>
+
+ <span class="k">for</span> <span class="n">right</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">index</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">)):</span>
+ <span class="k">if</span> <span class="n">array</span><span class="p">[</span><span class="n">right</span><span class="p">]</span> <span class="o"><</span> <span class="n">array</span><span class="p">[</span><span class="n">min_index</span><span class="p">]:</span>
+ <span class="n">min_index</span> <span class="o">=</span> <span class="n">right</span>
+
+ <span class="n">array</span><span class="p">[</span><span class="n">index</span><span class="p">],</span> <span class="n">array</span><span class="p">[</span><span class="n">min_index</span><span class="p">]</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="n">min_index</span><span class="p">],</span> <span class="n">array</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
+</pre></div>
+</div>
+<div class="section" id="insertion-sort">
+<h2>Insertion Sort</h2>
+<p>Como o algoritmo funciona: Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=O_E-Lj5HuRU&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r&t=0s&index=6">https://www.youtube.com/watch?v=O_E-Lj5HuRU</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=Sy_Z1pqMgko&index=7&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r">https://www.youtube.com/watch?v=Sy_Z1pqMgko</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Código do algoritmo</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">sort</span><span class="p">(</span><span class="n">array</span><span class="p">):</span>
+ <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">)):</span>
+ <span class="n">current_element</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="n">p</span><span class="p">]</span>
+
+ <span class="k">while</span> <span class="n">p</span> <span class="o">></span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">array</span><span class="p">[</span><span class="n">p</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">></span> <span class="n">current_element</span><span class="p">:</span>
+ <span class="n">array</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="n">p</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span>
+ <span class="n">p</span> <span class="o">-=</span> <span class="mi">1</span>
+
+ <span class="n">array</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="n">current_element</span>
+</pre></div>
+</div>
+<div class="section" id="merge-sort">
+<h2>Merge Sort</h2>
+<p>Como o algoritmo funciona: Como implementar o algoritmo usando Python: <a class="reference external" href="https://www.youtube.com/watch?v=Lnww0ibU0XM&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r&t=0s&index=8">https://www.youtube.com/watch?v=Lnww0ibU0XM</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Como implementar o algoritmo usando Python - Parte I: <a class="reference external" href="https://www.youtube.com/watch?v=cXJHETlYyVk&index=9&list=PLvo_Yb_myrNBhIdq8qqtNSDFtnBfsKL2r">https://www.youtube.com/watch?v=cXJHETlYyVk</a>.</p>
+<div class="youtube youtube-16x9"></div><p>Código do algoritmo</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">sort</span><span class="p">(</span><span class="n">array</span><span class="p">):</span>
+ <span class="n">sort_half</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
+
+
+<span class="k">def</span> <span class="nf">sort_half</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">start</span> <span class="o">>=</span> <span class="n">end</span><span class="p">:</span>
+ <span class="k">return</span>
+
+ <span class="n">middle</span> <span class="o">=</span> <span class="p">(</span><span class="n">start</span> <span class="o">+</span> <span class="n">end</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span>
+
+ <span class="n">sort_half</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">middle</span><span class="p">)</span>
+ <span class="n">sort_half</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">middle</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">end</span><span class="p">)</span>
+
+ <span class="n">merge</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">)</span>
+
+
+<span class="k">def</span> <span class="nf">merge</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">):</span>
+ <span class="n">array</span><span class="p">[</span><span class="n">start</span><span class="p">:</span> <span class="n">end</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">array</span><span class="p">[</span><span class="n">start</span><span class="p">:</span> <span class="n">end</span> <span class="o">+</span> <span class="mi">1</span><span class="p">])</span>
+</pre></div>
+</div>
+ Thu, 29 Nov 2018 15:10:00 +0000
+
+
+ PythonClub: Trabalhando com operadores ternários
+ tag:pythonclub.com.br,2018-10-06:/trabalhando-com-operadores-ternarios.html
+ https://pythonclub.com.br/trabalhando-com-operadores-ternarios.html
+ <p>Quando estamos escrevendo um código qualquer, possivelmente
+a expressão que mais utilizamos é o <code>if</code>. Para qualquer
+tarefas que buscamos automatizar ou problemas que buscamos
+resolver, sempre acabamos caindo em lógicas como "Se isso
+acontecer, então faça aquilo, senão faça aquele outro...".</p>
+<p>Quando estamos falando de ações a serem executadas, pessoalmente
+gosto da forma com que o código fica organizado em python quando
+usamos este tipo de condições, por exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">vencer_o_thanos</span><span class="p">:</span>
+ <span class="n">restaurar_a_paz</span><span class="p">()</span>
+
+<span class="k">else</span><span class="p">:</span>
+ <span class="n">foo</span><span class="p">()</span>
+</pre></div>
+
+
+<p>Graças a indentação e ao espaçamento, vemos onde onde começa e/ou
+termina o bloco executado caso a varável <code>vencer_o_thanos</code> seja
+<code>True</code>. Quanto mais <code>if</code>'s você aninhar, mais bonito seu
+código fica e em momento algum o mesmo se torna mais confuso
+(ao menos, não deveria se tornar). Entretanto, sempre fico
+extremamente incomodado quando tenho de escrever um bloco apenas
+marcar uma variável, como por exemplo:</p>
+<div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">vencer_o_thanos</span><span class="p">:</span>
+ <span class="n">paz</span> <span class="o">=</span> <span class="kc">True</span>
+
+<span class="k">else</span><span class="p">:</span>
+ <span class="n">paz</span> <span class="o">=</span> <span class="kc">False</span>
+</pre></div>
+
+
+<p>Por isso, para trabalhar com variáveis que possuem um valor condicional,
+gosto sempre de trabalhar com expressões condicionais, ou como costumam
+ser chamadas, <strong>operadores ternários</strong>.</p>
+<p>Operadores ternários são todos os operadores que podem receber três
+operandos. Como as expressões condicionais costumam ser os operadores
+ternários mais populares nas linguagens em que aparecem, acabamos por
+associar estes nomes e considerar que são a mesma coisa. Cuidado ao tirar
+este tipo de conclusão, mesmo que toda vogal esteja no alfabeto, o
+alfabeto não é composto apenas por vogais.</p>
+<p>A estrutura de uma expressão condicional é algo bem simples, veja só:</p>
+<div class="highlight"><pre><span></span><span class="n">paz</span> <span class="o">=</span> <span class="kc">True</span> <span class="k">if</span> <span class="n">vencer_o_thanos</span> <span class="k">else</span> <span class="kc">False</span>
+<span class="n">tipo_de_x</span> <span class="o">=</span> <span class="s2">"Par"</span> <span class="k">if</span> <span class="n">x</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="s2">"impar"</span>
+</pre></div>
+
+
+<p>Resumidamente, teremos <strong>um valor seguido de uma condição e por fim seu
+valor caso a condição seja falsa</strong>. Pessoalmente acredito que apesar de um
+pouco diferente, essa forma de escrita para casos como o exemplificado acima
+é muito mais clara, mais explicita.</p>
+<p>Se você fizer uma tradução literal das booleanas utilizadas no primeiro exemplo,
+lerá algo como <code>paz é verdadeira caso vencer_o_thanos, caso contrário é Falsa.</code>
+já o segundo exemplo fica mais claro ainda, pois lemos algo como
+<code>tipo_de_x é par caso o resto da divisão de x por 2 seja 0, se não, tipo_de_x é impar.</code>.</p>
+<p>Interpretar código dessa forma pode ser estranho para um programador. Interpretar
+uma abertura de chave ou uma indentação já é algo mas natural. Todavia, para aqueles
+que estão começando, o raciocínio ocorre de forma muito mais parecida com a descrita
+acima. Espero que tenham gostado do texto e que esse conhecimento lhes seja útil.</p>
+ Sat, 06 Oct 2018 12:21:00 +0000
+
+
+ Gabbleblotchits: What open science is about
+ tag:blog.luizirber.org,2018-09-24:/2018/09/24/open-science/
+ https://blog.luizirber.org/2018/09/24/open-science/
+ <p>Today I got a pleasant surprise: <a href="http://www.olgabotvinnik.com/">Olga Botvinnik</a> posted on <a href="https://twitter.com/olgabot/status/1044292704513839104">Twitter</a>
+about a <a href="https://github.com/czbiohub/kmer-hashing/blob/olgabot/search-compare-ignore-abundance/figures/presentations/2018-09-24_beyond_the_cell_atlas_poster/2018-09-24_Beyond_the_Cell_Atlas.pdf">poster</a> she is presenting at the Beyond the Cell Atlas conference
+and she name-dropped a bunch of people that helped her. The cool thing? They
+are all open source developers, and Olga interacted thru GitHub to <a href="https://github.com/dib-lab/sourmash/pull/543">ask</a> <a href="https://github.com/dib-lab/sourmash/issues/545">for</a> <a href="https://github.com/betteridiot/bamnostic/issues/15">features</a>,
+<a href="https://github.com/betteridiot/bamnostic/issues/20">report bugs</a> and even <a href="https://github.com/dib-lab/sourmash/pull/543">submit</a> <a href="https://github.com/dib-lab/sourmash/pull/539">pull</a> <a href="https://github.com/dib-lab/sourmash/pull/529">requests</a>.</p>
+<p>That's what open science is about: collaboration, good practices, and in the end coming up
+with something that is larger than each individual piece. Now sourmash is better,
+bamnostic is better, reflow is better. I would like to see this becoming more and
+more common =]</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/100783375923088450">Thread on Mastodon</a></li>
+<li><a href="https://twitter.com/luizirber/status/1044369382233665536">Thread on Twitter</a></li>
+</ul>
+ Mon, 24 Sep 2018 20:00:00 +0000
+
+
+ Gabbleblotchits: New crate: nthash
+ tag:blog.luizirber.org,2018-09-13:/2018/09/13/nthash/
+ https://blog.luizirber.org/2018/09/13/nthash/
+ <p>A quick announcement: I wrote a <a href="https://github.com/luizirber/nthash">Rust implementation</a> of <a href="https://github.com/bcgsc/ntHash">ntHash</a> and published
+it in <a href="https://crates.io/crates/nthash">crates.io</a>. It implements an <code>Iterator</code> to take advantage of the
+rolling properties of <code>ntHash</code> which make it so useful in bioinformatics (where
+we work a lot with sliding windows over sequences).</p>
+<p>It's a pretty small crate, and probably was a better project to learn Rust than
+doing a <a href="https://blog.luizirber.org/2018/08/23/sourmash-rust/">sourmash implementation</a> because it doesn't involve gnarly FFI
+issues. I also put <a href="https://github.com/luizirber/nthash/blob/d0c16d7deb0a78b8aeb29090db91bba954c14fe8/src/lib.rs#L91">some docs</a>, <a href="https://github.com/luizirber/nthash/blob/d0c16d7deb0a78b8aeb29090db91bba954c14fe8/benches/nthash.rs#L11">benchmarks</a> using <a href="https://japaric.github.io/criterion.rs/">criterion</a>,
+and even an <a href="https://github.com/luizirber/nthash/blob/d0c16d7deb0a78b8aeb29090db91bba954c14fe8/tests/nthash.rs#L80">oracle property-based test</a> with <a href="https://github.com/BurntSushi/quickcheck">quickcheck</a>.</p>
+<p>More info <a href="https://docs.rs/nthash/">in the docs</a>, and if you want an <s>optimization</s> versioning bug
+discussion be sure to check the <a href="https://github.com/luizirber/nthash_bug"><code>ntHash bug?</code></a> repo,
+which has a (slow) Python implementation and a pretty nice <a href="https://nbviewer.jupyter.org/github/luizirber/nthash_bug/blob/master/analysis.ipynb">analysis</a> notebook.</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/100721133117928424">Thread on Mastodon</a></li>
+<li><a href="https://twitter.com/luizirber/status/1040386666089705472">Thread on Twitter</a></li>
+</ul>
+ Thu, 13 Sep 2018 20:00:00 +0000
+
+
+ Gabbleblotchits: Oxidizing sourmash: WebAssembly
+ tag:blog.luizirber.org,2018-08-27:/2018/08/27/sourmash-wasm/
+ https://blog.luizirber.org/2018/08/27/sourmash-wasm/
+ <p>sourmash calculates MinHash signatures for genomic datasets,
+meaning we are reducing the data (via subsampling) to a small
+representative subset (a signature) capable of answering one question:
+how similar is this dataset to another one? The key here is that a dataset with
+10-100 GB will be reduced to something in the megabytes range, and two approaches
+for doing that are:</p>
+<ul>
+<li>The user install our software in their computer.
+ This is not so bad anymore (yay bioconda!), but still requires knowledge
+ about command line interfaces and how to install all this stuff. The user
+ data never leaves their computer, and they can share the signatures later
+ if they want to.</li>
+<li>Provide a web service to calculate signatures. In this case no software
+ need to be installed, but it's up to someone (me?) to maintain a server running with
+ an API and frontend to interact with the users. On top of requiring more
+ maintenance, another drawback is that
+ the user need to send me the data, which is very inefficient network-wise
+ and lead to questions about what I can do with their raw data (and I'm not
+ into surveillance capitalism, TYVM).</li>
+</ul>
+<h2>But... what if there is a third way?</h2>
+<p>What if we could keep the frontend code from the web service (very
+user-friendly) but do all the calculations client-side (and avoid the network
+bottleneck)? The main hurdle
+here is that our software is implemented in Python (and C++), which are not
+supported in browsers. My first solution was to write the core features of
+<a href="https://github.com/luizirber/sourmash-node">sourmash in JavaScript</a>, but that quickly started hitting annoying things
+like JavaScript not supporting 64-bit integers. There is also the issue of
+having another codebase to maintain and keep in sync with the original sourmash,
+which would be a relevant burden for us. I gave a <a href="https://drive.google.com/open?id=1JvXiDaEA4J3hmEKw6sV-VHMpuHG_sxls3fLxJOht28E">lab meeting</a> about this
+approach, using a <a href="https://soursigs-dnd-luizirber.hashbase.io/">drag-and-drop UI as proof of concept</a>. It did work but it
+was finicky (dealing with the 64-bit integer hashes is not fun). The good thing
+is that at least I had a working UI for further testing<sup id="sf-sourmash-wasm-1-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-1" class="simple-footnote" title="even if horrible, I need to get some design classes =P">1</a></sup></p>
+<p>In "<a href="https://blog.luizirber.org/2018/08/23/sourmash-rust/">Oxidizing sourmash: Python and FFI</a>" I described my road to learn Rust,
+but something that I omitted was that around the same time the <code>WebAssembly</code>
+support in Rust started to look better and better and was a huge influence in
+my decision to learn Rust. Reimplementing the sourmash C++ extension in Rust and
+use the same codebase in the browser sounded very attractive,
+and now that it was working I started looking into how to use the WebAssembly
+target in Rust.</p>
+<h2>WebAssembly?</h2>
+<p>From the <a href="https://webassembly.org/">official site</a>,</p>
+<blockquote>
+<p>WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine.
+Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust,
+enabling deployment on the web for client and server applications.</p>
+</blockquote>
+<p>You can write WebAssembly by hand, but the goal is to have it as lower level
+target for other languages. For me the obvious benefit is being able to use
+something that is not JavaScript in the browser, even though the goal is not to replace
+JS completely but complement it in a big pain point: performance. This also
+frees JavaScript from being the target language for other toolchains,
+allowing it to grow into other important areas (like language ergonomics).</p>
+<p>Rust is not the only language targeting WebAssembly: Go 1.11 includes
+<a href="https://golang.org/doc/go1.11#wasm">experimental support for WebAssembly</a>, and there are even projects bringing
+the <a href="https://github.com/iodide-project/pyodide">scientific Python to the web</a> using WebAssembly. </p>
+<h2>But does it work?</h2>
+<p>With the <a href="https://github.com/luizirber/sourmash-rust">Rust implementation in place</a> and with all tests working on sourmash, I
+added the finishing touches using <a href="https://github.com/rustwasm/wasm-bindgen"><code>wasm-bindgen</code></a> and built an NPM package using
+<a href="https://github.com/rustwasm/wasm-pack"><code>wasm-pack</code></a>: <a href="https://www.npmjs.com/package/sourmash">sourmash</a> is a Rust codebase compiled to WebAssembly and ready
+to use in JavaScript projects.</p>
+<p>(Many thanks to Madicken Munk, who also presented during SciPy about how they used
+<a href="https://munkm.github.io/2018-07-13-scipy/">Rust and WebAssembly to do interactive visualization in Jupyter</a>
+and helped with a good example on how to do this properly =] )</p>
+<p>Since I already had the working UI from the previous PoC, I <a href="https://github.com/luizirber/wort-dnd">refactored the code</a>
+to use the new WebAssembly module and voilà! <a href="https://wort-dnd.hashbase.io/">It works!</a><sup id="sf-sourmash-wasm-2-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-2" class="simple-footnote" title="the first version of this demo only worked in Chrome because they implemented the BigInt proposal, which is not in the official language yet. The funny thing is that BigInt would have made the JS implementation of sourmash viable, and I probably wouldn't have written the Rust implementation =P. Turns out that I didn't need the BigInt support if I didn't expose any 64-bit integers to JS, and that is what I'm doing now.">2</a></sup>.
+<sup id="sf-sourmash-wasm-3-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-3" class="simple-footnote" title="Along the way I ended up writing a new FASTQ parser... because it wouldn't be bioinformatics if it didn't otherwise, right? =P">3</a></sup>
+But that was the demo from a year ago with updated code and I got a bit
+better with frontend development since then, so here is the new demo:</p>
+<div id="files" class="box">
+ <h2>sourmash + Wasm</h2>
+ <div id="drag-container">
+ <p><b>Drag & drop</b> a FASTA or FASTQ file here to calculate the sourmash signature.</p>
+ </div>
+
+ <div id="progress-container">
+ <div id="progress-bar"></div>
+ </div>
+ <div class="columns">
+ <fieldset class="box input-button" id="params">
+ <label for="ksize-input">k-mer size:</label>
+ <input id="ksize-input" type="number" value="21" />
+ <label for="scaled-input">scaled:</label>
+ <input id="scaled-input" type="number" value="0" />
+ <label for="num-input">number of hashes:</label>
+ <input id="num-input" type="number" value="500" />
+ <label for="dna-protein-group">Input type:</label>
+ <div id="dna-protein-group">
+ <input id="dna-input" name="dna-protein-input" type="radio" value="DNA/RNA" checked="checked" />
+ <label for="dna-input">DNA/RNA</label>
+ <input id="protein-input" name="dna-protein-input" type="radio" value="Protein" />
+ <label for="protein-input">Protein</label>
+ </div>
+ <label for="track-abundance-input">Track abundance?</label>
+ <input id="track-abundance-input" type="checkbox" checked="checked" />
+ </fieldset>
+ <div class="box" id="download">
+ <button id="download_btn" type="button" disabled="disabled">Download</button>
+ </div>
+ </div>
+</div>
+
+<p>
+</p>
+<p>For the source code for this demo, check the <a href="https://blog.luizirber.org/static/sourmash-wasm/index.html">sourmash-wasm</a> directory.</p>
+<h2>Next steps</h2>
+<p>The proof of concept works, but it is pretty useless right now.
+I'm thinking about building it as a <a href="https://www.webcomponents.org/">Web Component</a> and making it really easy
+to add to any webpage<sup id="sf-sourmash-wasm-4-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-4" class="simple-footnote" title="or maybe a React component? I really would like to have something that works independent of framework, but not sure what is the best option in this case...">4</a></sup>.</p>
+<p>Another interesting feature would be supporting more input formats (the GMOD
+project implemented a lot of those!), but more features are probably better
+after something simple but functional is released =P</p>
+<h2>Next time!</h2>
+<p>Where we will go next? Maybe explore some decentralized web technologies like
+IPFS and dat, hmm? =]</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/100624574917435477">Thread on Mastodon</a></li>
+<li><a href="https://www.reddit.com/r/rust/comments/9atie8/blog_post_clientside_bioinformatics_in_the/">Thread on reddit</a></li>
+<li><a href="https://twitter.com/luizirber/status/1034206952773935104">Thread on Twitter</a></li>
+</ul>
+<h2>Updates</h2>
+<ul>
+<li>2018-08-30: Added a demo in the blog post.</li>
+</ul><hr /><h2>Footnotes</h2><ol><li id="sf-sourmash-wasm-1"><p>even if horrible, I
+need to get some design classes =P <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-1-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-wasm-2"><p>the first version
+of this demo only worked in Chrome because they implemented the <a href="https://github.com/tc39/proposal-bigint">BigInt proposal</a>,
+which is not in the official language yet. The funny thing is that BigInt would
+have made the JS implementation of sourmash viable, and I probably wouldn't have
+written the Rust implementation =P.
+Turns out that I didn't need the BigInt support if I didn't expose any 64-bit
+integers to JS, and that is what I'm doing now. <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-2-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-wasm-3"><p>Along the way I ended up writing a new FASTQ parser... because it wouldn't
+be bioinformatics if it didn't otherwise, right? =P <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-3-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-wasm-4"><p>or maybe a React component? I really would like to
+have something that works independent of framework, but not sure what is the
+best option in this case... <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-wasm-4-back" class="simple-footnote-back">↩</a></p></li></ol>
+ Mon, 27 Aug 2018 18:30:00 +0000
+
+
+ Gabbleblotchits: Oxidizing sourmash: Python and FFI
+ tag:blog.luizirber.org,2018-08-23:/2018/08/23/sourmash-rust/
+ https://blog.luizirber.org/2018/08/23/sourmash-rust/
+ <p>I think the first time I heard about Rust was because Frank Mcsherry chose it to
+write a <a href="https://github.com/frankmcsherry/timely-dataflow">timely dataflow</a> implementation.
+Since then it started showing more and more in my news sources,
+leading to Armin Ronacher publishing a post in the Sentry blog last November about
+writing <a href="https://blog.sentry.io/2017/11/14/evolving-our-rust-with-milksnake">Python extensions in Rust</a>.</p>
+<p>Last December I decided to give it a run:
+I spent some time porting the C++ bits of <a href="https://github.com/dib-lab/sourmash">sourmash</a>
+to Rust.
+The main advantage here is that it's a problem I know well,
+so I know what the code is supposed to do and can focus on figuring out
+syntax and the mental model for the language.
+I started digging into the <code>symbolic</code> codebase and understanding what they did,
+and tried to mirror or improve it for my use cases.</p>
+<p>(About the post title: The process of converting a codebase to Rust is referred as <a href="https://wiki.mozilla.org/Oxidation">"Oxidation"</a> in
+the Rust community, following the codename Mozilla chose for the process of
+integrating Rust components into the Firefox codebase.
+<sup id="sf-sourmash-rust-1-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-1" class="simple-footnote" title="The creator of the language is known to keep making up different explanations for the name of the language, but in this case ">1</a></sup>
+Many of these components were tested and derived in Servo, an experimental
+browser engine written in Rust, and are being integrated into Gecko,
+the current browser engine (mostly written in C++).)</p>
+<h2>Why Rust?</h2>
+<p>There are other programming languages more focused on scientific software
+that could be used instead, like Julia<sup id="sf-sourmash-rust-2-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-2" class="simple-footnote" title="Even more now that it hit 1.0, it is a really nice language">2</a></sup>. Many programming languages start from a
+specific niche (like R and statistics,
+or Maple and mathematics) and grow into larger languages over time.
+While Rust goal is not to be a scientific language,
+its focus on being a general purpose language allows a phenomenon similar
+to what happened with Python, where people from many areas pushed the
+language in different directions (system scripting, web development,
+numerical programming...) allowing developers to combine all these things
+in their systems.</p>
+<p>But by far my interest in Rust is for the many best practices it brings to the default experience:
+integrated package management (with Cargo),
+documentation (with rustdoc), testing and benchmarking.
+It's understandable that older languages like C/C++ need
+more effort to support some of these features (like modules and an unified
+build system), since they are designed by standard and need to keep backward
+compatibility with codebases that already exist.
+Nonetheless, the lack of features increase the effort needed to have good
+software engineering practices, since you need to choose a solution that might
+not be compatible with other similar but slightly different options,
+leading to fragmentation and increasing the impedance to use these features.</p>
+<p>Another big reason is that Rust doesn't aim to completely replace what already
+exists, but complement and extend it. Two very good talks about how to do this,
+one by <a href="https://ashleygwilliams.github.io/rustfest-2017/#1">Ashley Williams</a>, another by <a href="http://talks.edunham.net/lca2018/should-you-rewrite-in-rust/beamer.pdf">E. Dunham</a>.</p>
+<h2>Converting from a C++ extension to Rust</h2>
+<p>The current implementation of the core data structures in sourmash is in a
+C++ extension wrapped with Cython. My main goals for converting the code are:</p>
+<ul>
+<li>
+<p>support additional languages and platforms. sourmash is available as a Python
+ package and CLI, but we have R users in the lab that would benefit from having
+ an R package, and ideally we wouldn't need to rewrite the software every time
+ we want to support a new language.</p>
+</li>
+<li>
+<p>reducing the number of wheel packages necessary (one for each OS/platform).</p>
+</li>
+<li>
+<p>in the long run, use the Rust memory management concepts (lifetimes, borrowing)
+to increase parallelism in the code.</p>
+</li>
+</ul>
+<p>Many of these goals are attainable with our current C++ codebase, and
+"rewrite in a new language" is rarely the best way to solve a problem.
+But the reduced burden in maintenance due to better tooling,
+on top of features that would require careful planning to execute
+(increasing the parallelism without data races) while maintaining compatibility
+with the current codebase are promising enough to justify this experiment.
+<a href="https://github.com/luizirber/2018-python-rust/blob/master/03.current_impl.md"><img src="https://blog.luizirber.org/images/arch_cpp.png" title="" /></a></p>
+<p>Cython provides a nice gradual path to migrate code from Python to C++,
+since it is a superset of the Python syntax. It also provides low overhead
+for many C++ features, especially the STL containers, with makes it easier
+to map C++ features to the Python equivalent.
+For research software this also lead to faster exploration of solutions before
+having to commit to lower level code, but without a good process it might also
+lead to code never crossing into the C++ layer and being stuck in the Cython
+layer. This doesn't make any difference for a Python user, but it becomes
+harder from users from other languages to benefit from this code (since your
+language would need some kind of support to calling Python code, which is not
+as readily available as calling C code).</p>
+<p>Depending on the requirements, a downside is that Cython is tied to the CPython API,
+so generating the extension requires a development environment set up with
+the appropriate headers and compiler. This also makes the extension specific
+to a Python version: while this is not a problem for source distributions,
+generating wheels lead to one wheel for each OS and Python version supported.</p>
+<h2>The new implementation</h2>
+<p>This is the overall architecture of the Rust implementation:
+<a href="https://github.com/luizirber/2018-python-rust/blob/master/04.rust_impl.md"><img src="https://blog.luizirber.org/images/arch_rust.png" title="" /></a>
+It is pretty close to what <code>symbolic</code> does,
+so let's walk through it.</p>
+<h3>The Rust code</h3>
+<p>If you take a look at my Rust code, you will see it is very... C++. A lot of the
+code is very similar to the original implementation, which is both a curse and a
+blessing: I'm pretty sure that are more idiomatic and performant ways of doing
+things, but most of the time I could lean on my current mental model for C++ to
+translate code. The biggest exception was the <code>merge</code> function, were I was doing
+something on the C++ implementation that the borrow checker didn't like.
+Eventually I found it was because it couldn't keep track of the lifetime
+correctly and putting braces around it fixed the problem,
+which was both an epiphany and a WTF moment. <a href="https://play.rust-lang.org/?gist=eae9de12950d1b2a7699cd49a3571c37&version=stable">Here</a> is an example that triggers
+the problem, and the <a href="https://play.rust-lang.org/?gist=c8733c5125766930a589c8d0412af99c&version=stable">solution</a>.</p>
+<p>"Fighting the borrow checker" seems to be a common theme while learning Rust,
+but the compiler really tries to help you to understand what is happening and
+(most times) how to fix it. A lot of people grow to hate the borrow checker,
+but I see it more as a 'eat your vegetables' situation: you might not like it
+at first, but it's better in the long run. Even though I don't have a big
+codebase in Rust yet, it keeps you from doing things that will come back to bite
+you hard later.</p>
+<h3>Generating C headers for Rust code: cbindgen</h3>
+<p>With the Rust library working, the next step was taking the Rust code and generate C headers describing the
+functions and structs we expose with the <code>#[no_mangle]</code> attribute in Rust
+(these are defined in the <a href="https://github.com/luizirber/sourmash-rust/blob/ead9ae0ed3b2d16c9e3b8379919f3bfd2efd21ae/src/ffi.rs"><code>ffi.rs</code></a> module in <code>sourmash-rust</code>).
+This attribute tells the Rust compiler to generate names that are compatible
+with the C ABI, and so can be called from other languages that implement FFI
+mechanisms. FFI (the foreign function interface) is quite low-level,
+and pretty much defines things that C can represent: integers, floats, pointers
+and structs. It doesn't support higher level concepts like objects or generics,
+so in a sense it looks like a feature funnel.
+This might sound bad, but ends up being something that other languages can
+understand without needing too much extra functionality in their runtimes,
+which means that most languages have support to calling code through an FFI.</p>
+<p>Writing the C header by hand is possible, but is very error prone.
+A better solution is to use <a href="https://github.com/eqrion/cbindgen"><code>cbindgen</code></a>,
+a program that takes Rust code and generate a C header file automatically.
+<code>cbindgen</code> is developed primarily to generate the C headers for <a href="https://github.com/servo/webrender/">webrender</a>,
+the GPU-based renderer for servo,
+so it's pretty likely that if it can handle a complex codebase it will work
+just fine for the majority of projects.</p>
+<h3>Interfacing with Python: CFFI and Milksnake</h3>
+<p>Once we have the C headers, we can use the FFI to
+call Rust code in Python. Python has a FFI module in the standard library: <code>ctypes</code>,
+but the Pypy developers also created CFFI, which has more features.</p>
+<p>The C headers generated by cbindgen can be interpreted by CFFI to generate
+a low-level Python interface for the code. This is the equivalent of declaring
+the functions/methods and structs/classes in a <code>pxd</code> file (in the Cython
+world): while the code is now usable in Python, it is not well adapted to
+the features and idioms available in the language.</p>
+<p>Milksnake is the package developed by Sentry that takes care of running cargo
+for the Rust compilation and generating the CFFI boilerplate,
+making it easy to load the low-level CFFI bindings in Python.
+With this low-level binding available we can now write something more Pythonic
+(the <code>pyx</code> file in Cython), and I ended up just renaming the <code>_minhash.pyx</code> file
+back to <code>minhash.py</code> and doing one-line fixes to replace the Cython-style code
+with the equivalent CFFI calls.</p>
+<p>All of these changes should be transparent to the Python code, and to guarantee
+that I made sure that all the current tests that we have (both for the Python
+module and the command line interface) are still working after the changes.
+It also led to finding some quirks in the implementation,
+and even improvements in the current C++ code (because we were moving a lot of
+data from C++ to Python).</p>
+<h2>Where I see this going</h2>
+<p>It seems it worked as an experiment,
+and I presented a <a href="https://github.com/luizirber/2018-python-rust">poster</a> at <a href="https://gccbosc2018.sched.com/event/FEWp/b23-oxidizing-python-writing-extensions-in-rust">GCCBOSC 2018</a> and <a href="https://scipy2018.scipy.org/ehome/index.php?eventid=299527&tabid=712461&cid=2233543&sessionid=21618890&sessionchoice=1&">SciPy 2018</a> that
+was met with excitement by many people.
+Knowing that it is possible,
+I want to reiterate some points why Rust is pretty exciting for bioinformatics
+and science in general.</p>
+<h3>Bioinformatics as libraries (and command line tools too!)</h3>
+<p>Bioinformatics is an umbrella term for many different methods, depending on
+what analysis you want to do with your data (or model).
+In this sense, it's distinct from other scientific areas where it is possible
+to rely on a common set of libraries (numpy in linear algebra, for example), since a
+library supporting many disjoint methods tend to grow too big and hard to
+maintain.</p>
+<p>The environment also tends to be very diverse, with different languages being
+used to implement the software. Because it is hard to interoperate,
+the methods tend to be implemented in command line programs that are stitched
+together in pipelines, a workflow describing how to connect the input and output of many different tools to
+generate results.
+Because the basic unit is a command-line tool,
+pipelines tend to rely on standard operating system abstractions like
+files and pipes to make the tools communicate with each other. But since tools
+might have input requirements distinct from what the previous tool provides,
+many times it is necessary to do format conversion or other adaptations to make the
+pipeline work.</p>
+<p>Using tools as blackboxes, controllable through specific parameters at the
+command-line level, make exploratory analysis and algorithm reuse harder:
+if something needs to be investigated the user needs to resort to perturbations
+of the parameters or the input data, without access to the more feature-rich and
+meaningful abstraction happening inside the tool.</p>
+<p>Even if many languages are used for writing the software, most of the time there
+is some part written in C or C++ for performance reasons, and these tend to be
+the core data structures of the computational method. Because it is not easy to
+package your C/C++ code in a way that other people can readily use it,
+most of this code is reinvented over and over again, or is copy and pasted into
+codebases and start diverging over time. Rust helps solve this problem with the
+integrated package management, and due to the FFI it can also be reused inside
+other programs written in other languages.</p>
+<p>sourmash is not going to be Rust-only and abandon Python,
+and it would be crazy to do so when it has so many great exploratory tools
+for scientific discovery. But now we can also use our method in other languages
+and environment, instead of having our code stuck in one language.</p>
+<h3>Don't rewrite it all!</h3>
+<p>I could have gone all the way and rewrite sourmash in Rust<sup id="sf-sourmash-rust-3-back"><a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-3" class="simple-footnote" title="and risk being kicked out of the lab =P">3</a></sup>, but it would be incredibly disruptive for
+the current sourmash users and it would take way longer to pull off. Because
+Rust is so focused in supporting existing code, you can do a slow transition and
+reuse what you already have while moving into more and more Rust code.
+A great example is this one-day effort by Rob Patro to bring <a href="https://github.com/COMBINE-lab/cqf-rust">CQF</a> (a C
+codebase) into Rust, using <code>bindgen</code> (a generator of C bindings for Rust).
+Check <a href="https://blog.luizirber.org/2018/08/27/sourmash-wasm/">the Twitter thread</a> for more =]</p>
+<h3>Good scientific citizens</h3>
+<p>There is another MinHash implementation already written in Rust, <a href="https://github.com/onecodex/finch-rs">finch</a>.
+Early in my experiment I got an email from them asking if I wanted to work
+together, but since I wanted to learn the language I kept doing my thing. (They
+were totally cool with this, by the way). But the fun thing is that Rust has a
+pair of traits called <a href="https://doc.rust-lang.org/rust-by-example/conversion/from_into.html"><code>From</code> and <code>Into</code></a> that you can implement for your
+type, and so I <a href="https://github.com/luizirber/sourmash-rust/pull/1">did that</a> and now we can have interoperable
+implementations. This synergy allows <code>finch</code> to use <code>sourmash</code> methods,
+and vice versa.</p>
+<p>Maybe this sounds like a small thing, but I think it is really exciting. We can
+stop having incompatible but very similar methods, and instead all benefit from
+each other advances in a way that is supported by the language.</p>
+<h2>Next time!</h2>
+<p>Turns out Rust supports WebAssembly as a target,
+so... what if we run sourmash in the browser?
+That's what I'm covering in the <a href="https://blog.luizirber.org/2018/08/27/sourmash-wasm/">next blog post</a>,
+so stay tuned =]</p>
+<h2>Comments?</h2>
+<ul>
+<li><a href="https://social.lasanha.org/@luizirber/100602525575698239">Thread on Mastodon</a></li>
+<li><a href="https://www.reddit.com/r/rust/comments/99vakd/blog_post_converting_c_to_rust_and_interoperate/">Thread on reddit</a></li>
+<li><a href="https://twitter.com/luizirber/status/1032779995129597952">Thread on Twitter</a></li>
+</ul>
+<!-- rust libs -->
+
+<!-- why rust -->
+
+<!-- WASM and demos -->
+
+<!-- python and FFI -->
+
+<!-- rust examples -->
+
+<!-- cbindgen -->
+
+<!-- rust for pythonistas -->
+
+<!-- understanding rust -->
+
+<!-- rust and bio -->
+
+<!-- rust error handling -->
+
+<!-- other notes
+
+- Implement Default trait for defaults (less verbose than ::new...)
+ https://doc.rust-lang.org/std/default/trait.Default.html
+
+--><hr /><h2>Footnotes</h2><ol><li id="sf-sourmash-rust-1"><p>The creator of the language is known to keep making up different
+explanations for the <a href="https://www.reddit.com/r/rust/comments/27jvdt/internet_archaeology_the_definitive_endall_source/">name of the language</a>,
+but in this case "oxidation" refers to the chemical process that creates
+rust, and rust is the closest thing to metal (metal being the hardware).
+There are many terrible puns in the Rust community. <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-1-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-rust-2"><p>Even more now that it hit 1.0,
+it is a really nice language <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-2-back" class="simple-footnote-back">↩</a></p></li><li id="sf-sourmash-rust-3"><p>and risk
+being kicked out of the lab =P <a href="https://blog.luizirber.org/atom.xml?tag=python#sf-sourmash-rust-3-back" class="simple-footnote-back">↩</a></p></li></ol>
+ Thu, 23 Aug 2018 20:00:00 +0000
+
+
+ PythonClub: Upload de Arquivos com Socket e Struct
+ tag:pythonclub.com.br,2018-05-17:/upload-de-arquivos-com-socket-e-struct.html
+ https://pythonclub.com.br/upload-de-arquivos-com-socket-e-struct.html
+ <p>Apesar de termos muitas formas de enviarmos arquivos para servidores hoje em dia, como por exemplo o <em>scp</em> e <em>rsync</em>, podemos usar o python com seus módulos <em>built-in</em> para enviar arquivos a servidores usando struct para serializar os dados e socket para criar uma conexão cliente/servidor.</p>
+<h3><em>Struct</em></h3>
+<p>O módulo <a href="https://docs.python.org/3/library/struct.html">struct</a> é usado para converter bytes no python em formatos do struct em C.
+Com ele podemos enviar num único conjunto de dados o nome de um arquivo e os bytes referentes ao seus dados.</p>
+<p>Struct também é utilizado para serializar diversos tipos de dados diferentes, como bytes, inteiros, floats além de outros, no nosso caso usaremos apenas bytes.</p>
+<p>Vamos criar um arquivo para serializar.</p>
+<div class="highlight"><pre><span></span><span class="err">!</span><span class="n">echo</span> <span class="s2">"Upload de arquivos com sockets e struct</span><span class="se">\n</span><span class="s2">Criando um arquivo para serializar."</span> <span class="o">></span> <span class="n">arquivo_para_upload</span>
+</pre></div>
+
+
+<p>Agora em posse de um arquivo vamos criar nossa estrutura de bytes para enviar.</p>
+<div class="highlight"><pre><span></span><span class="n">arquivo</span> <span class="o">=</span> <span class="s2">"arquivo_para_upload"</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">arquivo</span><span class="p">,</span> <span class="s1">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">arq</span><span class="p">:</span>
+ <span class="n">dados_arquivo</span> <span class="o">=</span> <span class="n">arq</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
+ <span class="n">serializar</span> <span class="o">=</span> <span class="n">struct</span><span class="o">.</span><span class="n">Struct</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">s </span><span class="si">{}</span><span class="s2">s"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">arquivo</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">dados_arquivo</span><span class="p">)))</span>
+ <span class="n">dados_upload</span> <span class="o">=</span> <span class="n">serializar</span><span class="o">.</span><span class="n">pack</span><span class="p">(</span><span class="o">*</span><span class="p">[</span><span class="n">arquivo</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="n">dados_arquivo</span><span class="p">])</span>
+</pre></div>
+
+
+<p>Por padrão, struct usa caracteres no início da sequência dos dados para definir a ordem dos bytes, tamanho e alinhamento dos bytes nos dados empacotados.
+Esses caracteres podem ser vistos na <a href="https://docs.python.org/3/library/struct.html#byte-order-size-and-alignment">seção 7.1.2.1</a> da documentação.
+Como não definimos, será usado o <strong>@</strong> que é o padrão.</p>
+<p>Nessa linha:</p>
+<div class="highlight"><pre><span></span><span class="n">serializar</span> <span class="o">=</span> <span class="n">struct</span><span class="o">.</span><span class="n">Struct</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">s </span><span class="si">{}</span><span class="s2">s"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">arquivo</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">dados_arquivo</span><span class="p">)))</span>
+</pre></div>
+
+
+<p>definimos que nossa estrutura serializada seria de dois conjuntos de caracteres, a primeira com o tamanho do nome do arquivo, e a segunda com o tamanho total dos dados lidos em</p>
+<div class="highlight"><pre><span></span><span class="n">dados_arquivo</span> <span class="o">=</span> <span class="n">arq</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
+</pre></div>
+
+
+<p>Se desempacotarmos os dados, teremos uma lista com o nome do arquivo e os dados lidos anteriormente.</p>
+<div class="highlight"><pre><span></span><span class="n">serializar</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">dados_upload</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="err">b'arquivo_para_upload'</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="n">serializar</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">dados_upload</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="err">b'Upload de arquivos com sockets e struct\\nCriando um arquivo para serializar.\n'</span>
+</pre></div>
+
+
+<p>Agora de posse dos nossos dados já serializados, vamos criar um cliente e um servidor com socket para transferirmos nosso arquivo.</p>
+<h3><em>Socket</em></h3>
+<p>O modulo <a href="https://docs.python.org/3/library/socket.html">socket</a> prove interfaces de socket BSD, disponiveis em praticamente todos os sistemas operacionais.</p>
+<h4>Familias de sockets</h4>
+<p>Diversas famílias de sockets podem ser usadas para termos acessos a objetos que nos permitam fazer chamadas de sistema.
+Mais informações sobre as famílias podem ser encontradas na <a href="https://docs.python.org/3/library/socket.html#socket-families">seção 18.1.1</a> da documentação. No nosso exemplo usaremos a AF_INET.</p>
+<h4>AF_INET</h4>
+<p><strong>AF_INET</strong> precisa basicamente de um par de dados, contendo um endereço IPv4 e uma porta para ser instanciada.
+Para endereços IPv6 o modulo disponibiliza o <strong>AF_INET6</strong></p>
+<h4>Constante [SOCK_STREAM]</h4>
+<p>As constantes representam as famílias de sockets, como a constante AF_INET e os protocolos usados como parâmetros para o modulo socket.
+Um dos protocolos mais usados encontrados na maioria dos sistemas é o SOCK_STREAM.</p>
+<p>Ele é um protocolo baseado em comunicação que permite que duas partes estabeleçam uma conexão e conversem entre si.</p>
+<h3><em>Servidor e cliente socket</em></h3>
+<p>Como vamos usar um protocolo baseado em comunicação, iremos construir o servidor e cliente paralelamente para um melhor entendimento.</p>
+<blockquote>
+<p>Servidor</p>
+</blockquote>
+<p>Para esse exemplo eu vou usar a porta 6124 para o servidor, ela esta fora da range reservada pela IANA para sistemas conhecidos, que vai de 0-1023.</p>
+<p>Vamos importar a bilioteca socket e definir um host e porta para passarmos como parametro para a constante AF_INET.</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">socket</span>
+<span class="n">host</span> <span class="o">=</span> <span class="s2">"127.0.0.1"</span>
+<span class="n">porta</span> <span class="o">=</span> <span class="mi">6124</span>
+<span class="n">sock</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Agora usaremos o metodo bind para criarmos um ponto de conexão para nosso cliente. Esse método espera por uma tupla contento o host e porta como parâmetros.</p>
+<div class="highlight"><pre><span></span><span class="n">sock</span><span class="o">.</span><span class="n">bind</span><span class="p">((</span><span class="n">host</span><span class="p">,</span> <span class="n">porta</span><span class="p">))</span>
+</pre></div>
+
+
+<p>Agora vamos colocar nosso servidor socket em modo escuta com o metodo listen. Esse método recebe como parâmetro um número inteiro (<strong>backlog</strong>) definindo qual o tamanho da fila que será usada para receber pacotes SYN até dropar a conexão. Usaremos um valor baixo o que evita SYN flood na rede. Mais informações sobre <em>backlog</em> podem ser encontradas na <a href="https://tools.ietf.org/html/rfc7413">RFC 7413</a>.</p>
+<div class="highlight"><pre><span></span><span class="n">sock</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Agora vamos colocar o nosso socket em um loop esperando por uma conexão e um início de conversa. Pra isso vamos usar o metodo <em>accept</em> que nos devolve uma tupla, onde o primeiro elemento é um novo objeto socket para enviarmos e recebermos informações, e o segundo contendo informações sobre o endereço de origem e porta usada pelo cliente.</p>
+<p><strong>Vamos criar um diretório para salvar nosso novo arquivo.</strong></p>
+<div class="highlight"><pre><span></span><span class="err">!</span><span class="n">mkdir</span> <span class="n">arquivos_recebidos</span>
+</pre></div>
+
+
+<p><em>Os dados são enviados sempre em bytes</em>. <strong>Leia os comentários</strong></p>
+<div class="highlight"><pre><span></span><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
+ <span class="n">novo_sock</span><span class="p">,</span> <span class="n">cliente</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span>
+ <span class="k">with</span> <span class="n">novo_sock</span><span class="p">:</span> <span class="c1"># Caso haja uma nova conexão</span>
+ <span class="n">ouvir</span> <span class="o">=</span> <span class="n">novo_sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> <span class="c1"># Colocamos nosso novo objeto socket para ouvir</span>
+ <span class="k">if</span> <span class="n">ouvir</span> <span class="o">!=</span> <span class="sa">b</span><span class="s2">""</span><span class="p">:</span> <span class="c1"># Se houver uma mensagem...</span>
+ <span class="sd">"""</span>
+<span class="sd"> Aqui usaremos os dados enviados na mensagem para criar nosso serielizador.</span>
+
+<span class="sd"> Com ele criado poderemos desempacotar os dados assim que recebermos.</span>
+<span class="sd"> Veja no cliente mais abaixo qual a primeira mensagem enviada.</span>
+<span class="sd"> """</span>
+ <span class="n">mensagem</span><span class="p">,</span> <span class="n">nome</span><span class="p">,</span> <span class="n">dados</span> <span class="o">=</span> <span class="n">ouvir</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">":"</span><span class="p">)</span>
+ <span class="n">serializar</span> <span class="o">=</span> <span class="n">struct</span><span class="o">.</span><span class="n">Struct</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">s </span><span class="si">{}</span><span class="s2">s"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">nome</span><span class="o">.</span><span class="n">split</span><span class="p">()[</span><span class="mi">0</span><span class="p">]),</span> <span class="nb">int</span><span class="p">(</span><span class="n">dados</span><span class="o">.</span><span class="n">split</span><span class="p">()[</span><span class="mi">0</span><span class="p">])))</span>
+ <span class="n">novo_sock</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"Pode enviar!"</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span> <span class="c1"># Enviaremos uma mensagem para o cliente enviar os dados.</span>
+ <span class="n">dados</span> <span class="o">=</span> <span class="n">novo_sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> <span class="c1"># Agora iremos esperar por eles.</span>
+ <span class="n">nome</span><span class="p">,</span> <span class="n">arquivo</span> <span class="o">=</span> <span class="n">serializar</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">dados</span><span class="p">)</span> <span class="c1"># Vamos desempacotar os dados</span>
+ <span class="sd">"""</span>
+<span class="sd"> Agora enviamos uma mensagem dizendo que o arquivo foi recebido.</span>
+
+<span class="sd"> E iremos salva-lo no novo diretório criado.</span>
+<span class="sd"> """</span>
+ <span class="n">novo_sock</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"Os dados do arquivo </span><span class="si">{}</span><span class="s2"> foram enviados."</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">nome</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span>
+ <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s2">"arquivos_recebidos/</span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">nome</span><span class="o">.</span><span class="n">decode</span><span class="p">()),</span> <span class="s1">'wb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">novo_arquivo</span><span class="p">:</span>
+ <span class="n">novo_arquivo</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">arquivo</span><span class="p">)</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s2">"Arquivo </span><span class="si">{}</span><span class="s2"> salvo em arquivos_recebidos."</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">nome</span><span class="o">.</span><span class="n">decode</span><span class="p">()))</span>
+
+ <span class="n">Arquivo</span> <span class="n">arquivo_para_upload</span> <span class="n">salvo</span> <span class="n">em</span> <span class="n">arquivos_recebidos</span><span class="o">.</span>
+</pre></div>
+
+
+<blockquote>
+<p>Cliente</p>
+</blockquote>
+<p>Nosso cliente irá usar o metodo <em>connect</em> para se conectar no servidor e a partir dai começar enviar e receber mensagens. Ele também recebe como parâmetros uma tupla com o host e porta de conexão do servidor.</p>
+<div class="highlight"><pre><span></span><span class="n">host</span> <span class="o">=</span> <span class="s1">'127.0.0.1'</span>
+<span class="n">porta</span> <span class="o">=</span> <span class="mi">6124</span>
+<span class="n">sock</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span> <span class="c1"># Cria nosso objeto socket</span>
+<span class="n">sock</span><span class="o">.</span><span class="n">connect</span><span class="p">((</span><span class="n">host</span><span class="p">,</span> <span class="n">porta</span><span class="p">))</span>
+<span class="n">sock</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"Enviarei um arquivo chamado: </span><span class="si">{}</span><span class="s2"> contendo: </span><span class="si">{}</span><span class="s2"> bytes"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
+ <span class="n">arquivo</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">dados_arquivo</span><span class="p">))</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span> <span class="c1"># Enviamos a mensagem com o nome e tamanho do arquivo.</span>
+<span class="n">ouvir</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> <span class="c1"># Aguardamos uma mensagem de confirmação do servidor.</span>
+<span class="k">if</span> <span class="n">ouvir</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> <span class="o">==</span> <span class="s2">"Pode enviar!"</span><span class="p">:</span>
+ <span class="n">sock</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">dados_upload</span><span class="p">)</span> <span class="c1"># Enviamos os dados empacotados.</span>
+ <span class="n">resposta</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> <span class="c1"># Aguardamos a confirmação de que os dados foram enviados.</span>
+ <span class="nb">print</span><span class="p">(</span><span class="n">resposta</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span>
+
+ <span class="n">Os</span> <span class="n">dados</span> <span class="n">do</span> <span class="n">arquivo</span> <span class="n">arquivo_para_upload</span> <span class="n">foram</span> <span class="n">enviados</span><span class="o">.</span>
+</pre></div>
+
+
+<p>Agora podemos checar nossos arquivos e ver se eles foram salvos corretamente.</p>
+<div class="highlight"><pre><span></span><span class="err">!</span><span class="n">md5sum</span> <span class="n">arquivo_para_upload</span><span class="p">;</span> <span class="n">md5sum</span> <span class="n">arquivos_recebidos</span><span class="o">/</span><span class="n">arquivos_para_upload</span>
+
+ <span class="mf">605e99</span><span class="n">b3d873df0b91d8834ff292d320</span> <span class="n">arquivo_para_upload</span>
+ <span class="mf">605e99</span><span class="n">b3d873df0b91d8834ff292d320</span> <span class="n">arquivos_recebidos</span><span class="o">/</span><span class="n">arquivo_para_upload</span>
+</pre></div>
+
+
+<p>Com base nesse exemplo, podem ser enviados diversos arquivos, sendo eles texto, arquivos compactados ou binários.</p>
+<p>Sem mais delongas, fiquem com Cher e até a próxima!</p>
+<p>Abraços.</p>
+ Thu, 17 May 2018 22:24:00 +0000
+
+
+ PythonClub: Monitorando Ips Duplicados na Rede
+ tag:pythonclub.com.br,2018-05-15:/monitorando-ips-duplicados-na-rede.html
+ https://pythonclub.com.br/monitorando-ips-duplicados-na-rede.html
+ <p>Muitos administradores de redes e sysadmins encontram problemas de conectividade nos ambientes que administram e por muitas vezes o problema é um simples IP duplicado causando todo mal estar. Agora veremos como usar o scapy e defaultdict da lib collections para monitorar esses IPs.</p>
+<h3>Scapy</h3>
+<p>O Scapy é uma poderosa biblioteca de manipulação de pacotes interativa, com abrangencia a uma enorme quantidade de protocolos provenientes da suite TCP/IP.
+Mais informações sobre o scpay pode ser encontrada na <a href="http://scapy.readthedocs.io/en/latest/index.html"><strong>documentação oficial</strong></a>.
+Nesse caso em especifico iremos utilizar do scapy a metaclasse <em>ARP</em> e a função <em>sniff</em>.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">scapy.all</span> <span class="kn">import</span> <span class="n">ARP</span><span class="p">,</span> <span class="n">sniff</span>
+</pre></div>
+
+
+<h4>sniff</h4>
+<p>Vamos usar a função sniff para monitorar os pacotes que trafegam na rede usando o protocolo ARP.
+Pra isso vamos utilizar dela quatro parametros basicos:</p>
+<div class="highlight"><pre><span></span><span class="n">sniff</span><span class="p">(</span><span class="n">prn</span><span class="o">=</span><span class="n">pacotes</span><span class="p">,</span> <span class="nb">filter</span><span class="o">=</span><span class="s2">"arp"</span><span class="p">,</span> <span class="n">iface</span><span class="o">=</span><span class="n">interface</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
+</pre></div>
+
+
+<ul>
+<li>
+<p>prn, chama uma função para ser aplicada a cada pacote capturado pelo sniff.</p>
+</li>
+<li>
+<p>filter, irá filtrar todos os pacotes que contiverem o protocolo ARP.</p>
+</li>
+<li>
+<p>iface, determina a interface de rede que será monitorada.</p>
+</li>
+<li>
+<p>timeout, irá determinar que nosso monitoramento da rede se dara por 60 segundos.</p>
+</li>
+</ul>
+<h4>ARP</h4>
+<p>ARP é uma metaclasse de pacotes com dados sobre o protocolo arp pertencente a camada de enlace de dados.
+Iremos utilizar essa metaclasse para filtrar os pacotes com informações de pacotes com respostas a requisições arp. (opcode == 2 [is at])
+As informações sobre o protocolo ARP podem serm encontradas na <a href="https://tools.ietf.org/html/rfc826">rfc826</a> no site do IETF.</p>
+<h3>collections.defaultdict</h3>
+<p>defaultdict é uma subclasse de dict que prove uma instancia de variavel para a chamada de uma chave inexistente.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">defaultdict</span>
+<span class="n">list_ips</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">set</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Basicamente nossa função irá monitorar por um certo tempo o trafego de pacotes pela rede adicionar a nossa variavel <em>list_ips</em> o endereço ou endereços MAC encontrados.</p>
+<h3>Definindo a função que será passada como parametro para o sniff.</h3>
+<p>Para cada pacote capturado pela função sniff, será checado se o opcode corresponde a um response do protocolo arp.
+Caso seja, sera adicionado a nossa defaultdict.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">pacotes</span><span class="p">(</span><span class="n">pacote</span><span class="p">):</span>
+ <span class="sd">"""Checa se o valor do opcode dentro do protocolo arp é igual a 2."""</span>
+ <span class="k">if</span> <span class="n">pacote</span><span class="p">[</span><span class="n">ARP</span><span class="p">]</span><span class="o">.</span><span class="n">op</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
+ <span class="c1"># Se for adiciona o ip de origem e seu mac à dict list_ips</span>
+ <span class="n">list_ips</span><span class="p">[</span><span class="n">pacote</span><span class="p">[</span><span class="n">ARP</span><span class="p">]</span><span class="o">.</span><span class="n">psrc</span><span class="p">]</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">pacote</span><span class="p">[</span><span class="n">ARP</span><span class="p">]</span><span class="o">.</span><span class="n">hwsrc</span><span class="p">)</span>
+</pre></div>
+
+
+<h3>Limpando a tabela arp</h3>
+<p>Para que seja feita novas requisições arp, iremos limpar nossa tabela arp e iniciar o monitoramento da rede.
+Pra isso iremos usar o comando arp, dentro do shell do sistema. <em>(Como uso <a href="https://www.freebsd.org">FreeBSD</a> vou definir uma função chamando um comando pelo csh)</em></p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">os</span>
+<span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s1">'which arp'</span><span class="p">)</span>
+<span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">sbin</span><span class="o">/</span><span class="n">arp</span>
+</pre></div>
+
+
+<p>Com posse do caminho do comando arp, irei definir uma função que limpe a tabela e inicie o monitore a rede por 60 segundos.</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">monitorar</span><span class="p">(</span><span class="n">interface</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> O comando arp no FreeBSD usa os parametros:</span>
+
+<span class="sd"> -d para deletar as entradas</span>
+<span class="sd"> -i para declarar a interface</span>
+<span class="sd"> -a para representar todas entradas a serem deletas.</span>
+<span class="sd"> """</span>
+ <span class="n">cmd</span> <span class="o">=</span> <span class="s2">"/usr/sbin/arp -d -i </span><span class="si">{}</span><span class="s2"> -a"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">interface</span><span class="p">)</span>
+ <span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="n">cmd</span><span class="p">)</span>
+ <span class="n">sniff</span><span class="p">(</span><span class="n">prn</span><span class="o">=</span><span class="n">pacotes</span><span class="p">,</span> <span class="nb">filter</span><span class="o">=</span><span class="s2">"arp"</span><span class="p">,</span> <span class="n">iface</span><span class="o">=</span><span class="n">interface</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
+</pre></div>
+
+
+<p>E por ultimo chamar a função de monitoramento.
+No meu caso eu vou monitorar a interface <strong>em0</strong>.</p>
+<div class="highlight"><pre><span></span><span class="n">monitorar</span><span class="p">(</span><span class="s2">"em0"</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Agora só conferir as entradas em nossa dict.</p>
+<div class="highlight"><pre><span></span><span class="k">for</span> <span class="n">ip</span> <span class="ow">in</span> <span class="n">list_ips</span><span class="p">:</span>
+ <span class="nb">print</span> <span class="s2">"IP: </span><span class="si">{}</span><span class="s2"> -> MACs: </span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ip</span><span class="p">,</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">list_ips</span><span class="p">[</span><span class="n">ip</span><span class="p">])))</span>
+
+<span class="n">IP</span><span class="p">:</span> <span class="mf">192.168</span><span class="o">.</span><span class="mf">213.1</span> <span class="o">-></span> <span class="n">MACs</span><span class="p">:</span> <span class="mi">00</span><span class="p">:</span><span class="mi">90</span><span class="p">:</span><span class="mi">0</span><span class="n">b</span><span class="p">:</span><span class="mi">49</span><span class="p">:</span><span class="mi">3</span><span class="n">d</span><span class="p">:</span><span class="mi">0</span><span class="n">a</span>
+<span class="n">IP</span><span class="p">:</span> <span class="mf">192.168</span><span class="o">.</span><span class="mf">213.10</span> <span class="o">-></span> <span class="n">MACs</span><span class="p">:</span> <span class="mi">08</span><span class="p">:</span><span class="mi">00</span><span class="p">:</span><span class="mi">27</span><span class="p">:</span><span class="n">bf</span><span class="p">:</span><span class="mi">52</span><span class="p">:</span><span class="mi">6</span><span class="n">d</span><span class="p">,</span> <span class="n">a0</span><span class="p">:</span><span class="n">f3</span><span class="p">:</span><span class="n">c1</span><span class="p">:</span><span class="mi">03</span><span class="p">:</span><span class="mi">74</span><span class="p">:</span><span class="mi">6</span><span class="n">a</span>
+</pre></div>
+
+
+<p>Eu uso um script rodando nos switchs e gateway da rede que me enviam mensagens assim que ips duplicados são encontrados na rede.
+Também da pra usar o <strong>arping</strong> do scpay para fazer as requisições arp e coletar os responses.</p>
+<p>Abraços.</p>
+ Tue, 15 May 2018 13:24:00 +0000
+
+
+ Lauro Moura: C++ para Pythonistas – Introdução e iteradores
+ http://lauro.wordpress.com/?p=591
+
+ C++ pode ter a fama de ser uma linguagem com complexidade comparável à legislação tributária brasileira, mas ao mesmo tempo é uma linguagem extremamente poderosa, podendo ser usada tanto desde microcontroladores até super computadores, sem contar satélites espaciais. Tamanha complexidade pode assustar quem vem de linguagens mais simples como Python (malloc? rvalue references?). Uma ajuda … <a class="more-link" href="https://lauro.wordpress.com/2018/04/30/c-para-pythonistas-introducao-e-iteradores/">Continuar lendo <span class="meta-nav">→</span></a>
+ Tue, 01 May 2018 02:10:28 +0000
+
+
+ Magnun Leno: Algumas Razões Para Amar o PostgresSQL
+ http://mindbending.org/pt/algumas-razoes-para-amar-o-postgressql
+ http://mindbending.org/pt/algumas-razoes-para-amar-o-postgressql
+ <p>Geralmente, quando se fala em SGBDs OpenSource, a primeira resposta que se ouve é MySQL/MariaDB. Eu sempre torço meu nariz para respostas como essa… Implicancia pessoal? Talvez um pouco, mas existem <a class="reference external" href="https://www.cybertec-postgresql.com/en/why-favor-postgresql-over-mariadb-mysql/" target="_blank">muitos fundamentos</a>…</p>
+<p><a href="http://mindbending.org/pt/algumas-razoes-para-amar-o-postgressql">Algumas Razões Para Amar o PostgresSQL</a> é um artigo original de <a href="http://mindbending.org/pt">Mind Bending</a></p>
+ Thu, 19 Apr 2018 18:02:00 +0000
+
+
+ Python Help: Preservando a ordem de sequências ao remover duplicatas
+ http://pythonhelp.wordpress.com/?p=1984
+
+ Imagine que você tenha uma lista com as URLs extraídas de uma página web e queira eliminar as duplicatas da mesma. Transformar a lista em um conjunto (set) talvez seja a forma mais comum de se fazer isso. Tipo assim: Porém, observe que perdemos a ordem original dos elementos. Esse é um efeito colateral indesejado […]
+ Mon, 26 Feb 2018 01:31:38 +0000
+
+
+ PythonClub: Django Rest Framework - #3 Class Based Views
+ tag:pythonclub.com.br,2018-02-15:/django-rest-framework-class-based-views.html
+ https://pythonclub.com.br/django-rest-framework-class-based-views.html
+ <ul>
+<li>0 - <a href="http://pythonclub.com.br/django-rest-framework-quickstart.html">Quickstart</a></li>
+<li>1 - <a href="http://pythonclub.com.br/django-rest-framework-serialization.html">Serialization</a></li>
+<li>2 - <a href="http://pythonclub.com.br/django-rest-framework-requests-responses.html">Requests & Responses</a></li>
+<li>3 - <strong>Class based views</strong></li>
+</ul>
+<p>Este post é continuação do post <a href="http://pythonclub.com.br/django-rest-framework-requests-responses.html">Django Rest Framework Requests & Responses</a>.</p>
+<p>Finalmente chegamos as views baseadas em classes. A grande vantagem é que com poucas linhas de código já temos nossa API pronta.</p>
+<p>Veja como fica a <a href="https://github.com/rg3915/drf/blob/b0aa989ffc756e6dc5f65e172dfb43d47127d743/core/views.py">views.py</a>:</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.http</span> <span class="kn">import</span> <span class="n">Http404</span>
+<span class="kn">from</span> <span class="nn">rest_framework.views</span> <span class="kn">import</span> <span class="n">APIView</span>
+<span class="kn">from</span> <span class="nn">rest_framework.response</span> <span class="kn">import</span> <span class="n">Response</span>
+<span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">status</span>
+<span class="kn">from</span> <span class="nn">core.models</span> <span class="kn">import</span> <span class="n">Person</span>
+<span class="kn">from</span> <span class="nn">core.serializers</span> <span class="kn">import</span> <span class="n">PersonSerializer</span>
+
+
+<span class="k">class</span> <span class="nc">PersonList</span><span class="p">(</span><span class="n">APIView</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> List all persons, or create a new person.</span>
+<span class="sd"> """</span>
+
+ <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+ <span class="n">persons</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">persons</span><span class="p">,</span> <span class="n">many</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">serializer</span><span class="o">.</span><span class="n">is_valid</span><span class="p">():</span>
+ <span class="n">serializer</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_201_CREATED</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">errors</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_400_BAD_REQUEST</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">PersonDetail</span><span class="p">(</span><span class="n">APIView</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Retrieve, update or delete a person instance.</span>
+<span class="sd"> """</span>
+
+ <span class="k">def</span> <span class="nf">get_object</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">pk</span><span class="p">):</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">pk</span><span class="o">=</span><span class="n">pk</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">Person</span><span class="o">.</span><span class="n">DoesNotExist</span><span class="p">:</span>
+ <span class="k">raise</span> <span class="n">Http404</span>
+
+ <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+ <span class="n">person</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_object</span><span class="p">(</span><span class="n">pk</span><span class="p">)</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">person</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">put</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+ <span class="n">person</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_object</span><span class="p">(</span><span class="n">pk</span><span class="p">)</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">person</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">serializer</span><span class="o">.</span><span class="n">is_valid</span><span class="p">():</span>
+ <span class="n">serializer</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">errors</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_400_BAD_REQUEST</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">delete</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+ <span class="n">person</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_object</span><span class="p">(</span><span class="n">pk</span><span class="p">)</span>
+ <span class="n">person</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_204_NO_CONTENT</span><span class="p">)</span>
+</pre></div>
+
+
+<p>E <code>urls.py</code>:</p>
+<div class="highlight"><pre><span></span><span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
+ <span class="n">path</span><span class="p">(</span><span class="s1">'persons/'</span><span class="p">,</span> <span class="n">views</span><span class="o">.</span><span class="n">PersonList</span><span class="o">.</span><span class="n">as_view</span><span class="p">()),</span>
+ <span class="n">path</span><span class="p">(</span><span class="s1">'persons/<int:pk>/'</span><span class="p">,</span> <span class="n">views</span><span class="o">.</span><span class="n">PersonDetail</span><span class="o">.</span><span class="n">as_view</span><span class="p">()),</span>
+<span class="p">]</span>
+</pre></div>
+
+
+<h2>Usando Mixins</h2>
+<p>Repare que no exemplo anterior tivemos que definir os métodos <code>get()</code>, <code>post()</code>, <code>put()</code> e <code>delete()</code>. Podemos reduzir ainda mais esse código com o uso de mixins.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">mixins</span>
+<span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">generics</span>
+<span class="kn">from</span> <span class="nn">core.models</span> <span class="kn">import</span> <span class="n">Person</span>
+<span class="kn">from</span> <span class="nn">core.serializers</span> <span class="kn">import</span> <span class="n">PersonSerializer</span>
+
+
+<span class="k">class</span> <span class="nc">PersonList</span><span class="p">(</span><span class="n">mixins</span><span class="o">.</span><span class="n">ListModelMixin</span><span class="p">,</span>
+ <span class="n">mixins</span><span class="o">.</span><span class="n">CreateModelMixin</span><span class="p">,</span>
+ <span class="n">generics</span><span class="o">.</span><span class="n">GenericAPIView</span><span class="p">):</span>
+ <span class="n">queryset</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">PersonSerializer</span>
+
+ <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">PersonDetail</span><span class="p">(</span><span class="n">mixins</span><span class="o">.</span><span class="n">RetrieveModelMixin</span><span class="p">,</span>
+ <span class="n">mixins</span><span class="o">.</span><span class="n">UpdateModelMixin</span><span class="p">,</span>
+ <span class="n">mixins</span><span class="o">.</span><span class="n">DestroyModelMixin</span><span class="p">,</span>
+ <span class="n">generics</span><span class="o">.</span><span class="n">GenericAPIView</span><span class="p">):</span>
+ <span class="n">queryset</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">PersonSerializer</span>
+
+ <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">retrieve</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">put</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">delete</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">destroy</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+</pre></div>
+
+
+<h2>Usando generic class-based views</h2>
+<p>E para finalizar usamos <code>ListCreateAPIView</code> e <code>RetrieveUpdateDestroyAPIView</code> que já tem todos os métodos embutidos.</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">generics</span>
+<span class="kn">from</span> <span class="nn">core.models</span> <span class="kn">import</span> <span class="n">Person</span>
+<span class="kn">from</span> <span class="nn">core.serializers</span> <span class="kn">import</span> <span class="n">PersonSerializer</span>
+
+
+<span class="k">class</span> <span class="nc">PersonList</span><span class="p">(</span><span class="n">generics</span><span class="o">.</span><span class="n">ListCreateAPIView</span><span class="p">):</span>
+ <span class="n">queryset</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">PersonSerializer</span>
+
+
+<span class="k">class</span> <span class="nc">PersonDetail</span><span class="p">(</span><span class="n">generics</span><span class="o">.</span><span class="n">RetrieveUpdateDestroyAPIView</span><span class="p">):</span>
+ <span class="n">queryset</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">PersonSerializer</span>
+</pre></div>
+
+
+<p>Versão final de <a href="https://github.com/rg3915/drf/blob/263ca63e5c8e2dd2e0f5d6c88c5733fcfdab4f74/core/views.py">views.py</a>.</p>
+<p>Abraços.</p>
+ Fri, 16 Feb 2018 01:00:00 +0000
+
+
+ PythonClub: Django Rest Framework - #2 Requests and Responses
+ tag:pythonclub.com.br,2018-02-14:/django-rest-framework-requests-responses.html
+ https://pythonclub.com.br/django-rest-framework-requests-responses.html
+ <ul>
+<li>0 - <a href="http://pythonclub.com.br/django-rest-framework-quickstart.html">Quickstart</a></li>
+<li>1 - <a href="http://pythonclub.com.br/django-rest-framework-serialization.html">Serialization</a></li>
+<li>2 - <strong>Requests & Responses</strong></li>
+<li>3 - <a href="http://pythonclub.com.br/django-rest-framework-class-based-views.html">Class based views</a></li>
+</ul>
+<p>Este post é continuação do post <a href="http://pythonclub.com.br/django-rest-framework-serialization.html">Django Rest Framework Serialization</a>.</p>
+<p>O uso de <em>requests</em> e <em>responses</em> torna nossa api mais flexível. A funcionalidade principal do objeto <strong>Request</strong> é o atributo <code>request.data</code>, que é semelhante ao <code>request.POST</code>, mas é mais útil para trabalhar com APIs.</p>
+<h2>Objeto Response</h2>
+<p>Introduzimos aqui um objeto <code>Response</code>, que é um tipo de <code>TemplateResponse</code> que leva conteúdo não renderizado e usa a negociação de conteúdo para determinar o tipo de conteúdo correto para retornar ao cliente.</p>
+<div class="highlight"><pre><span></span><span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="c1"># Renderiza para o tipo de conteúdo conforme solicitado pelo cliente.</span>
+</pre></div>
+
+
+<p>Repare também no uso de <em>status code</em> pré definidos, exemplo: <code>status.HTTP_400_BAD_REQUEST</code>.</p>
+<p>E usamos o decorador <code>@api_view</code> para trabalhar com funções. Ou <code>APIView</code> para classes.</p>
+<p>Nosso código ficou assim:</p>
+<div class="highlight"><pre><span></span><span class="c1"># views.py</span>
+<span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">status</span>
+<span class="kn">from</span> <span class="nn">rest_framework.decorators</span> <span class="kn">import</span> <span class="n">api_view</span>
+<span class="kn">from</span> <span class="nn">rest_framework.response</span> <span class="kn">import</span> <span class="n">Response</span>
+<span class="kn">from</span> <span class="nn">core.models</span> <span class="kn">import</span> <span class="n">Person</span>
+<span class="kn">from</span> <span class="nn">core.serializers</span> <span class="kn">import</span> <span class="n">PersonSerializer</span>
+
+
+<span class="nd">@api_view</span><span class="p">([</span><span class="s1">'GET'</span><span class="p">,</span> <span class="s1">'POST'</span><span class="p">])</span>
+<span class="k">def</span> <span class="nf">person_list</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> List all persons, or create a new person.</span>
+<span class="sd"> """</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'GET'</span><span class="p">:</span>
+ <span class="n">persons</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">persons</span><span class="p">,</span> <span class="n">many</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+
+ <span class="k">elif</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'POST'</span><span class="p">:</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">serializer</span><span class="o">.</span><span class="n">is_valid</span><span class="p">():</span>
+ <span class="n">serializer</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_201_CREATED</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">errors</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_400_BAD_REQUEST</span><span class="p">)</span>
+
+
+<span class="nd">@api_view</span><span class="p">([</span><span class="s1">'GET'</span><span class="p">,</span> <span class="s1">'PUT'</span><span class="p">,</span> <span class="s1">'DELETE'</span><span class="p">])</span>
+<span class="k">def</span> <span class="nf">person_detail</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Retrieve, update or delete a person instance.</span>
+<span class="sd"> """</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">person</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">pk</span><span class="o">=</span><span class="n">pk</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">Person</span><span class="o">.</span><span class="n">DoesNotExist</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_404_NOT_FOUND</span><span class="p">)</span>
+
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'GET'</span><span class="p">:</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">person</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+
+ <span class="k">elif</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'PUT'</span><span class="p">:</span>
+ <span class="n">serializer</span> <span class="o">=</span> <span class="n">PersonSerializer</span><span class="p">(</span><span class="n">person</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">serializer</span><span class="o">.</span><span class="n">is_valid</span><span class="p">():</span>
+ <span class="n">serializer</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">serializer</span><span class="o">.</span><span class="n">errors</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_400_BAD_REQUEST</span><span class="p">)</span>
+
+ <span class="k">elif</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s1">'DELETE'</span><span class="p">:</span>
+ <span class="n">person</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_204_NO_CONTENT</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Veja no <a href="https://github.com/rg3915/drf/commit/69205da9262415eaf83ff04f22a635e912880a60">GitHub</a>.</p>
+<h2>Usando sufixo opcional</h2>
+<p>Em <code>core/urls.py</code> acrescente</p>
+<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">rest_framework.urlpatterns</span> <span class="kn">import</span> <span class="n">format_suffix_patterns</span>
+
+<span class="o">...</span>
+
+<span class="n">urlpatterns</span> <span class="o">=</span> <span class="n">format_suffix_patterns</span><span class="p">(</span><span class="n">urlpatterns</span><span class="p">)</span>
+</pre></div>
+
+
+<p>E em <code>views.py</code> acrescente <code>format=None</code> como parâmetro das funções a seguir:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">person_list</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+
+<span class="k">def</span> <span class="nf">person_detail</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">pk</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
+</pre></div>
+
+
+<p>Com isso você pode chamar a api da seguinte forma:</p>
+<div class="highlight"><pre><span></span>http http://127.0.0.1:8000/persons.json <span class="c1"># ou</span>
+http http://127.0.0.1:8000/persons.api
+</pre></div>
+
+
+<p>Até a próxima.</p>
+ Thu, 15 Feb 2018 01:00:00 +0000
+
+
+ PythonClub: Programação funcional com Python #2 - Iteraveis e iteradores
+ tag:pythonclub.com.br,2017-12-23:/progrmacao-funcional-com-python-2.html
+ https://pythonclub.com.br/progrmacao-funcional-com-python-2.html
+ <h1>2. Iteráveis e iteradores</h1>
+<p>O que são iteráveis? Basicamente e a grosso modo, iteráveis em python são todos os objetos que implementam o método <code>__getitem__</code> ou <code>__iter__</code>. Beleza, vamos partir do simples.</p>
+<p>Quase todos os tipos de dados em python são iteráveis, por exemplo: listas, strings, tuplas, dicionários, conjuntos, etc...</p>
+<p>Vamos aos exemplos, eles são sempre mágicos:</p>
+<div class="highlight"><pre><span></span><span class="n">lista</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">]</span>
+
+<span class="c1"># iteração</span>
+<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">lista</span><span class="p">:</span>
+ <span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
+
+<span class="c1"># 1</span>
+<span class="c1"># 2</span>
+<span class="c1"># 3</span>
+<span class="c1"># 4</span>
+<span class="c1"># 5</span>
+</pre></div>
+
+
+<p>Era só isso? Sim, nem doeu, fala a verdade.</p>
+<p>Em python, o comando <code>for</code> nos fornece um iterador implícito. O que? Não entendi.</p>
+<p>O laço <code>for</code> em python itera em cada elemento da sequência. Como no exemplo, o <code>for</code>, ou <code>foreach</code>, no caso vai passando por cada elemento da sequência. Não é necessária a implementação de um index como na linguagem C, onde a iteração é explícita:</p>
+<div class="highlight"><pre><span></span><span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">></span> <span class="mi">10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
+ <span class="n">sequencia</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
+<span class="p">}</span>
+</pre></div>
+
+
+<h2>2.1 <code>__getitem__</code></h2>
+<p>O padrão de projeto iterator em python já vem implementado por padrão, como já foi dito antes. Basta que um objeto tenha os métodos <code>__iter__</code> ou <code>__getitem__</code> para que um laço possa ser utilizado.</p>
+<p>Vamos exemplificar:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">iteravel</span><span class="p">:</span>
+ <span class="sd">"""</span>
+<span class="sd"> Um objeto que implementa o `__getitem__` pode ser acessado por posição</span>
+<span class="sd"> """</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sequencia</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">seq</span> <span class="o">=</span> <span class="n">sequencia</span>
+
+ <span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">posicao</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Por exemplo, quando tentamos acessar um elemento da sequência usando</span>
+<span class="sd"> slice:</span>
+<span class="sd"> >>> iteravel[2]</span>
+
+<span class="sd"> O interpretador python chama o __getitem__ do objeto e nos retorna</span>
+<span class="sd"> a posição solicitada</span>
+
+<span class="sd"> um exemplo:</span>
+<span class="sd"> IN: lista = [1, 2, 3, 4]</span>
+<span class="sd"> IN: lista[0]</span>
+<span class="sd"> OUT: 1</span>
+<span class="sd"> """</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">seq</span><span class="p">[</span><span class="n">posicao</span><span class="p">]</span>
+</pre></div>
+
+
+<p>Então, pode-se compreender, sendo bem rústico, que todos os objetos que implementam <code>__getitem__</code> são iteráveis em python.</p>
+<h2>2.2 <code>__iter__</code></h2>
+<p>Agora os objetos que implementam <code>__iter__</code> tem algumas peculiaridades. Por exemplo, quando o iterável (vamos pensar no <code>for</code>) chamar a sequência, ela vai pedir o <code>__iter__</code> que vai retornar uma instância de si mesmo para o <code>for</code> e ele vai chamar o <code>__next__</code> até que a exceção <code>StopIteration</code> aconteça.</p>
+<p>Uma classe que implementa <code>__iter__</code>:</p>
+<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">iteravel</span><span class="p">:</span>
+ <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sequencia</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">sequencia</span>
+
+ <span class="k">def</span> <span class="fm">__next__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Neste caso, como o método pop do objeto data é chamado</span>
+<span class="sd"> (vamos pensar em uma lista) ele vai retornar o primeiro elemento</span>
+<span class="sd"> (o de index 0) e vai remove-lo da lista</span>
+
+<span class="sd"> E quando a sequência estiver vazia ele vai nos retornar um StopIteration</span>
+<span class="sd"> O que vai fazer com que a iteração acabe.</span>
+
+<span class="sd"> Porém, estamos iterando com pop, o que faz a nossa iteração ser a única,</span>
+<span class="sd"> pois ela não pode ser repetida, dado que os elementos foram</span>
+<span class="sd"> removidos da lista</span>
+<span class="sd"> """</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">sequencia</span><span class="p">:</span>
+ <span class="k">raise</span> <span class="ne">StopIteration</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">sequencia</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span>
+</pre></div>
+
+
+<p>Como é possível notar, o objeto com <code>__iter__</code> não necessita de um <code>__getitem__</code> e vise-versa. As diferenças partem do ponto em que um pode ser acessado por index/slice e outro não. Também um bom ponto é que nesse nosso caso, removemos os elementos da sequência, ou seja, ela se torna descartável.</p>
+<p>Esse conceito, de ser descartado, pode parecer um pouco estranho no início, mas economiza muita memória. E, como estamos falando de programação funcional, pode-se dizer que nossa sequência se torna imutável, pois não existe uma maneira de mudar os valores contidos na lista do objeto.</p>
+<p>Seguem dois links maravilhosos explicando sobre iteração em python:</p>
+<ul>
+<li>
+<p><a href="https://www.python.org/dev/peps/pep-0234/">PEP0243</a></p>
+</li>
+<li>
+<p><a href="https://www.youtube.com/watch?v=ULj7ejvuzI8">Iteração em Python: do básico ao genial</a></p>
+</li>
+</ul>
+<p>O primeiro é a PEP sobre as estruturas dos iteráveis e o segundo um video do Guru Luciano Ramalho explicando tudo sobre iteradores.</p>
+<p>Ah... Ia quase me esquecendo, se você não entendeu muita coisa sobre os dunders, você pode ler o <a href="https://docs.python.org/3/reference/datamodel.html#special-method-names">Python data model</a>. Obs: não me responsabilizo pelo programador melhor que você sairá desta página.</p>
+<p>Embora esse tópico seja talvez o mais curto, ele vai ser de fundamental importância para o entendimento de um pouco de tudo nesse 'Curso'. É sério. Vamos entender como trabalhar com iteráveis de uma maneira bonita no próximo tópico.</p>
+ Sun, 24 Dec 2017 00:50:00 +0000
+
+
+ PythonClub: Programação funcional com Python #1 - Funções
+ tag:pythonclub.com.br,2017-12-08:/progrmacao-funcional-com-python-1.html
+ https://pythonclub.com.br/progrmacao-funcional-com-python-1.html
+ <h1>1. Funções</h1>
+<p>Como nem tudo são flores, vamos começar do começo e entender algumas características das funções do python (o objeto função) e dar uma revisada básica em alguns conceitos de função só pra gente não se perder no básico depois. Então o primeiro tópico vai se limitar a falar da estrutura básica das funções em python, sem entrar profundamente em cada um dos tópicos. Será uma explanação de código e abrir a cabeça para novas oportunidades de código mais pythonicos e que preferencialmente gere menos efeito colateral. Mas calma, não vamos ensinar a fazer funções, você já está cheio disso.</p>
+<h2>1.1 Funções como objeto de primeira classe</h2>
+<p>Funções como objeto de primeira classe, são funções que se comportam como qualquer tipo nativo de uma determinada linguagem. Por exemplo:</p>
+<div class="highlight"><pre><span></span><span class="c1"># uma lista</span>
+
+<span class="n">lista</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="s1">'str'</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">],</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">),</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">},</span> <span class="p">{</span><span class="mi">1</span><span class="p">:</span> <span class="s1">'um'</span><span class="p">}]</span>
+</pre></div>
+
+
+<p>Todos esses exemplos são tipos de objetos de primeira classe em Python, mas no caso as funções também são. Como assim? Pode-se passar funções como parâmetro de uma outra função, podemos armazenar funções em variáveis, pode-se definir funções em estruturas de dados:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Funções como objeto de primeira classe</span>
+
+<span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="c1"># a função anônima, lambda, foi armazenada em uma variável</span>
+
+<span class="k">def</span> <span class="nf">func_2</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">2</span>
+
+<span class="n">lista</span> <span class="o">=</span> <span class="p">[</span><span class="n">func</span><span class="p">,</span> <span class="n">func_2</span><span class="p">]</span> <span class="c1"># a variável que armazena a função foi inserida em uma estrutura, assim como uma função gerada com def</span>
+
+<span class="n">lista_2</span> <span class="o">=</span> <span class="p">[</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="c1"># aqui as funções foram definidas dentro de uma estrutura</span>
+</pre></div>
+
+
+<p>Como é possível notar, em python, as funções podem ser inseridas em qualquer contexto e também geradas em tempo de execução. Com isso nós podemos, além de inserir funções em estruturas, retornar funções, passar funções como parâmetro (HOFs), definir funções dentro de funções(closures) e assim por diante. Caso você tenha aprendido a programar usando uma linguagem em que as funções não são objetos de primeira classe, não se assuste. Isso faz parte da rotina comum do python. Preferencialmente, e quase obrigatoriamente, é melhor fazer funções simples, pequenas e de pouca complexidade para que elas não sofram interferência do meio externo, gerem menos manutenção e o melhor de tudo, possam ser combinadas em outras funções. Então vamos lá!</p>
+<h2>1.2 Funções puras</h2>
+<p>Funções puras são funções que não sofrem interferência do meio externo. Vamos começar pelo exemplo ruim:</p>
+<div class="highlight"><pre><span></span><span class="n">valor</span> <span class="o">=</span> <span class="mi">5</span>
+
+<span class="k">def</span> <span class="nf">mais_cinco</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">valor</span>
+
+<span class="k">assert</span> <span class="n">mais_cinco</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="mi">10</span> <span class="c1"># True</span>
+
+<span class="n">valor</span> <span class="o">=</span> <span class="mi">7</span>
+
+<span class="k">assert</span> <span class="n">mais_cinco</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="mi">10</span> <span class="c1"># AssertionError</span>
+</pre></div>
+
+
+<p><code>mais_cinco()</code> é o exemplo claro de uma função que gera efeito colateral. Uma função pura deve funcionar como uma caixa preta, todas as vezes em que o mesmo input for dado nela, ela terá que retornar o mesmo valor. Agora vamos usar o mesmo exemplo, só alterando a linha do return:</p>
+<div class="highlight"><pre><span></span><span class="n">valor</span> <span class="o">=</span> <span class="mi">5</span>
+
+<span class="k">def</span> <span class="nf">mais_cinco</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">5</span>
+
+<span class="k">assert</span> <span class="n">mais_cinco</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="mi">10</span> <span class="c1"># True</span>
+
+<span class="n">valor</span> <span class="o">=</span> <span class="mi">7</span>
+
+<span class="k">assert</span> <span class="n">mais_cinco</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="mi">10</span> <span class="c1"># True</span>
+</pre></div>
+
+
+<p>Pode parecer trivial, mas muitas vezes por comodidade deixamos o meio influenciar no comportamento de uma função. Por definição o Python só faz possível, e vamos falar disso em outro tópico, a leitura de variáveis externas. Ou seja, dentro do contexto da função as variáveis externas não podem ser modificadas, mas isso não impede que o contexto externo a modifique. Se você for uma pessoa inteligente como o Jaber deve saber que nunca é uma boa ideia usar valores externos. Mas, caso seja necessário, você pode sobrescrever o valor de uma variável no contexto global usando a palavra reservada <code>global</code>. O que deve ficar com uma cara assim:</p>
+<div class="highlight"><pre><span></span><span class="n">valor</span> <span class="o">=</span> <span class="mi">5</span>
+
+<span class="k">def</span> <span class="nf">teste</span><span class="p">():</span>
+ <span class="k">global</span> <span class="n">valor</span> <span class="c1"># aqui é feita a definição</span>
+ <span class="n">valor</span> <span class="o">=</span> <span class="mi">7</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">valor</span><span class="p">)</span> <span class="c1"># 7</span>
+</pre></div>
+
+
+<p>Só lembre-se de ser sempre coerente quando fizer isso, as consequências podem ser imprevisíveis. Nessa linha de funções puras e pequeninas, podemos caracterizar, embora isso não as defina, funções de ordem superior, que são funções que recebem uma função como argumento, ou as devolvem, e fazem a chamada das mesmas dentro do contexto da função que a recebeu como parâmetro. Isso resulta em uma composição de funções, o que agrega muito mais valor caso as funções não gerem efeitos colaterais.</p>
+<h2>1.3 Funções de ordem superior (HOFs)</h2>
+<p>Funções de ordem superior são funções que recebem funções como argumento(s) e/ou retornam funções como resposta. Existem muitas funções embutidas em python de ordem superior, como: <code>map, filter, zip</code> e praticamente todo o módulo functools <code>import functools</code>. Porém, nada impede de criarmos novas funções de ordem superior. Um ponto a ser lembrado é que map e filter não tem mais a devida importância em python com a entrada das comprehensions (embora eu as adore), o que nos faz escolher única e exclusivamente por gosto, apesar de comprehensions serem mais legíveis (vamos falar disso em outro contexto), existem muitos casos onde elas ainda fazem sentido. Mas sem me estender muito, vamos ao código:</p>
+<div class="highlight"><pre><span></span><span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">+</span><span class="mi">2</span> <span class="c1"># uma função simples, soma mais 2 a qualquer inteiro</span>
+
+<span class="k">def</span> <span class="nf">func_mais_2</span><span class="p">(</span><span class="n">funcao</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Executa a função passada por parâmetro e retorna esse valor somado com dois</span>
+
+<span class="sd"> Ou seja, é uma composição de funções:</span>
+
+<span class="sd"> Dado que func(valor) é processado por func_func:</span>
+<span class="sd"> func_mais_2(func(valor)) == f(g(x))</span>
+<span class="sd"> """</span>
+ <span class="k">return</span> <span class="n">funcao</span><span class="p">(</span><span class="n">valor</span><span class="p">)</span> <span class="o">+</span> <span class="mi">2</span>
+</pre></div>
+
+
+<p>Um ponto a tocar, e o que eu acho mais bonito, é que a função vai retornar diferentes respostas para o mesmo valor, variando a entrada da função. Nesse caso, dada a entrada de um inteiro ele será somado com 2 e depois com mais dois. Mas, vamos estender este exemplo:</p>
+<div class="highlight"><pre><span></span><span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">2</span> <span class="c1"># uma função simples, soma mais 2 a qualquer inteiro</span>
+
+<span class="k">def</span> <span class="nf">func_mais_2</span><span class="p">(</span><span class="n">funcao</span><span class="p">,</span> <span class="n">valor</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Função que usamos antes.</span>
+<span class="sd"> """</span>
+ <span class="k">return</span> <span class="n">funcao</span><span class="p">(</span><span class="n">valor</span><span class="p">)</span> <span class="o">+</span> <span class="mi">2</span>
+
+<span class="k">assert</span> <span class="n">func_mais_2</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">6</span> <span class="c1"># true</span>
+
+<span class="k">def</span> <span class="nf">func_quadrada</span><span class="p">(</span><span class="n">val</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Eleva o valor de entrada ao quadrado.</span>
+<span class="sd"> """</span>
+ <span class="k">return</span> <span class="n">val</span> <span class="o">*</span> <span class="n">val</span>
+
+<span class="k">assert</span> <span class="n">func_mais_2</span><span class="p">(</span><span class="n">func_quadrada</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">6</span> <span class="c1"># true</span>
+</pre></div>
+
+
+<h3>1.3.1 Um exemplo usando funções embutidas:</h3>
+<p>Muitas das funções embutidas em python são funções de ordem superior (HOFs) como a função map, que é uma das minhas preferidas. Uma função de map recebe uma função, que recebe um único argumento e devolve para nós uma nova lista com a função aplicada a cada elemento da lista:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="n">arg</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">arg</span> <span class="o">+</span> <span class="mi">2</span>
+
+<span class="n">lista</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">]</span>
+
+<span class="nb">list</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="n">lista</span><span class="p">))</span> <span class="o">==</span> <span class="p">[</span><span class="mi">4</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span> <span class="c1"># true</span>
+</pre></div>
+
+
+<p>Mas fique tranquilo, falaremos muito mais sobre isso.</p>
+<h2>1.4 <code>__call__</code></h2>
+<p>Por que falar de classes? Lembre-se, Python é uma linguagem construída em classes, e todos os objetos que podem ser chamados/invocados implementam o método <code>__call__</code>:</p>
+<p>Em uma função anônima:</p>
+<div class="highlight"><pre><span></span><span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span>
+
+<span class="s1">'__call__'</span> <span class="ow">in</span> <span class="nb">dir</span><span class="p">(</span><span class="n">func</span><span class="p">)</span> <span class="c1"># True</span>
+</pre></div>
+
+
+<p>Em funções tradicionais:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">x</span>
+
+<span class="s1">'__call__'</span> <span class="ow">in</span> <span class="nb">dir</span><span class="p">(</span><span class="n">func</span><span class="p">)</span> <span class="c1"># True</span>
+</pre></div>
+
+
+<p>Isso quer dizer que podemos gerar classes que se comportam como funções?</p>
+<p>SIIIIIM. Chupa Haskell</p>
+<p>Essa é uma parte interessante da estrutura de criação do Python a qual veremos mais em outro momento sobre introspecção de funções, mas vale a pena dizer que classes, funções nomeadas, funções anônimas e funções geradoras usam uma base comum para funcionarem, essa é uma das coisas mais bonitas em python e que em certo ponto fere a ortogonalidade da linguagem, pois coisas iguais tem funcionamentos diferentes, mas facilita o aprendizado da linguagem, mas não é nosso foco agora.</p>
+<h2>1.5 Funções geradoras</h2>
+<p>Embora faremos um tópico extremamente focado em funções geradoras, não custa nada dar uma palinha, não?</p>
+<p>Funções geradoras são funções que nos retornam um iterável. Mas ele é lazy (só é computado quando invocado). Para exemplo de uso, muitos conceitos precisam ser esclarecidos antes de entendermos profundamente o que acontece com elas, mas digo logo: são funções lindas <3</p>
+<p>Para que uma função seja geradora, em tese, só precisamos trocar o return por yield:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">gen</span><span class="p">(</span><span class="n">lista</span><span class="p">):</span>
+ <span class="k">for</span> <span class="n">elemento</span> <span class="ow">in</span> <span class="n">lista</span><span class="p">:</span>
+ <span class="k">yield</span> <span class="n">elemento</span>
+
+<span class="n">gerador</span> <span class="o">=</span> <span class="n">gen</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">])</span>
+
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># 1</span>
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># 2</span>
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># 3</span>
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># 4</span>
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># 5</span>
+<span class="nb">next</span><span class="p">(</span><span class="n">gerador</span><span class="p">)</span> <span class="c1"># StopIteration</span>
+</pre></div>
+
+
+<p>Passando bem por cima, uma função geradora nos retorna um iterável que é preguiçoso. Ou seja, ele só vai efetuar a computação quando for chamado.</p>
+<h2>1.6 Funções anônimas (lambda)</h2>
+<p>Funções anônimas, ou funções lambda, são funções que podem ser declaradas em qualquer contexto. Tá... Todo tipo de função em python pode ser declarada em tempo de execução. Porém funções anônimas podem ser atribuídas a variáveis, podem ser definidas dentro de sequências e declaradas em um argumento de função. Vamos olhar sua sintaxe:</p>
+<div class="highlight"><pre><span></span><span class="k">lambda</span> <span class="n">argumento</span><span class="p">:</span> <span class="n">argumento</span>
+</pre></div>
+
+
+<p>A palavra reservada <code>lambda</code> define a função, assim como uma <code>def</code>. Porém em uma <code>def</code> quase que instintivamente sempre quebramos linha:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func</span><span class="p">():</span>
+ <span class="k">pass</span>
+</pre></div>
+
+
+<p>Uma das diferenças triviais em python é que as funções anônimas não tem nome. Tá, isso era meio óbvio, mas vamos averiguar:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func</span><span class="p">():</span>
+ <span class="k">pass</span>
+
+<span class="n">func</span><span class="o">.</span><span class="vm">__name__</span> <span class="c1"># func</span>
+
+<span class="n">lambda_func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">arg</span><span class="p">:</span> <span class="n">arg</span>
+
+<span class="n">lambda_func</span><span class="o">.</span><span class="vm">__name__</span> <span class="c1"># '<lambda>'</span>
+</pre></div>
+
+
+<p>O resultado <code>'<lambda>'</code> será o mesmo para qualquer função. Isso torna sua depuração praticamente impossível em python. Por isso os usuários de python (e nisso incluo todos os usuários, até aqueles que gostam de funcional) não encorajam o uso de funções lambda a todos os contextos da linguagem. Mas, em funções que aceitam outra funções isso é meio que uma tradição, caso a função (no caso a que executa o código a ser usado pelo lambda) não esteja definida e nem seja reaproveitada em outro contexto. Eu gosto de dizer que lambdas são muito funcionais em aplicações parciais de função. Porém, os lambdas não passam de açúcar sintático em Python, pois não há nada que uma função padrão (definida com <code>def</code>), não possa fazer de diferente. Até a introspecção retorna o mesmo resultado:</p>
+<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func</span><span class="p">():</span>
+ <span class="k">pass</span>
+
+<span class="nb">type</span><span class="p">(</span><span class="n">func</span><span class="p">)</span> <span class="c1"># function</span>
+
+<span class="n">lambda_func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">arg</span><span class="p">:</span> <span class="n">arg</span>
+
+<span class="nb">type</span><span class="p">(</span><span class="n">lambda_func</span><span class="p">)</span> <span class="c1"># function</span>
+</pre></div>
+
+
+<p>Uma coisa que vale ser lembrada é que funções anônimas em python só executam uma expressão. Ou seja, não podemos usar laços de repetição (<code>while</code>, <code>for</code>), tratamento de exceções (<code>try</code>, <code>except</code>, <code>finally</code>). Um simples <code>if</code> com uso de <code>elif</code> também não pode ser definido. Como sintaticamente só são aceitas expressões, o único uso de um <code>if</code> é o ternário:</p>
+<div class="highlight"><pre><span></span><span class="n">valor_1</span> <span class="k">if</span> <span class="n">condicao</span> <span class="k">else</span> <span class="n">valor_2</span>
+</pre></div>
+
+
+<p>O que dentro de um lambda teria essa aparência:</p>
+<div class="highlight"><pre><span></span><span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">argumento</span><span class="p">:</span> <span class="n">argumento</span> <span class="o">+</span> <span class="mi">2</span> <span class="k">if</span> <span class="n">argumento</span> <span class="o">></span> <span class="mi">0</span> <span class="k">else</span> <span class="n">argumento</span> <span class="o">-</span> <span class="mi">2</span>
+</pre></div>
+
+
+<p>Funções lambda também podem ter múltiplos argumentos, embora seu processamento só possa ocorrer em uma expressão:</p>
+<div class="highlight"><pre><span></span><span class="n">func</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span><span class="p">,</span> <span class="n">arg_3</span><span class="p">:</span> <span class="kc">True</span> <span class="k">if</span> <span class="nb">sum</span><span class="p">([</span><span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span><span class="p">,</span> <span class="n">arg_3</span><span class="p">])</span> <span class="o">></span> <span class="mi">7</span> <span class="k">else</span> <span class="nb">min</span><span class="p">([</span><span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span><span class="p">,</span> <span class="n">arg_3</span><span class="p">])</span>
+</pre></div>
+
+
+<p>Embora essa seja uma explanação inicial sobre as funções anônimas, grande parte dos tópicos fazem uso delas e vamos poder explorar melhor sua infinitude.</p>
+<p>Mas por hoje é só e no tópico seguinte vamos discutir, mesmo que superficialmente, iteradores e iteráveis e suas relações com a programação funcional.</p>
+ Fri, 08 Dec 2017 15:30:00 +0000
+
+
+ PythonClub: Programação funcional com Python #0 - Saindo da zona de conforto
+ tag:pythonclub.com.br,2017-11-20:/progrmacao-funcional-com-python-0.html
+ https://pythonclub.com.br/progrmacao-funcional-com-python-0.html
+ <h1>0. Saindo da zona de conforto</h1>
+<p>Sinta-se um vencedor, se você chegou até aqui, isso significa que quer aprender mais sobre o mundo da programação.</p>
+<p>Aprender novos paradígmas podem te trazer muitas coisas positivas, assim como aprender linguagens diferentes, pois paradígmas e linguagens transpõem maneiras, estruturas e métodos de implementação completamente diferentes. Com isso você pode ter mais ferramentas para usar no dia a dia. Você pode aumentar sua capacidade de expressar ideias de diferentes maneiras. Eu penso que o maior limitador de um programador é a linguagem de programação em que ele tem domínio. Quando você aprende linguagens imperativas, como C, Python, Java e etc..., você se vê limitado ao escopo de criar e manipular variáveis. Não que isso seja uma coisa ruim, porém existem outras maneiras de resolver problemas e quando você tem conhecimento disso consegue avaliar melhor quando implementar cada tipo de coisa.</p>
+<p>Você pode me dizer que aprender diferentes tipos de estruturas e maneiras de computar é uma coisa negativa pois tudo é variável nesse contexto. Mas eu penso exatamente o contrário, quanto mais você aprender da sua língua nativa, no caso estamos falando em português, maior o campo de domínio que você tem sobre como se comunicar e expressar ideias. Assim como aprender outras línguas te darão mais fundamentos para expressar ideias em outros idiomas, que não são melhores que os seu, mas diferentes e compõem diferentes estruturas, e isso pode ser libertador. Não quero me prolongar nesse assunto, mas dizer que isso pode acrescentar muito na suas habilidades cognitivas, até mesmo para usar ferramentas que você já usa no seu dia a dia.</p>
+<p>Vamos começar fazendo uma tentativa de entender os paradígmas de programação, sem muito falatório e complicações. Um exemplo muito legal é do David Mertz em "Functional Programming in Python":</p>
+<ul>
+<li>
+<p>Usa-se programação funcional quando se programa em Lisp, Haskell, Scala, Erlang, F# etc..</p>
+</li>
+<li>
+<p>Do mesmo modo que se usa programação imperativa quando se programada C/C++, Pascal, Java, Python etc...</p>
+</li>
+<li>
+<p>Também quando se programa Prolog estamos programando usando o paradígma lógico.</p>
+</li>
+</ul>
+<p>Apesar de não ser uma definição muito elegante, talvez seja a melhor a ser dada em muitas ocasiões. Vamos tentar ser um pouco mais objetivos em relação ao estilo de computação, embora essa discussão não tenha fim:</p>
+<ul>
+<li>
+<p>O foco de usar programação imperativa está no ato de mudar variáveis. A computação se dá pela modificação dos estados das variáveis iniciais. Sendo assim, vamos pensar que tudo é definido no início e vai se modificando até que o resultado esperado seja obtido.</p>
+</li>
+<li>
+<p>Na programação funcional, se tem a noção de que o estado deve ser substituído, no caso da avaliação, para criação de um novo 'objeto' que no caso são funções.</p>
+</li>
+</ul>
+<h2>0.1 Mas de onde vem a programação funcional?</h2>
+<p>O florescer da programação funcional nasce no Lisp (acrônomo para List Processing) para tentar resolver alguns problemas de inteligência artificial que eram provenientes da linguística, que tinha foco em processamento de linguagem natural que por sua vez eram focados em processamento de listas em geral. Isso justifica uma grande parte do conteúdo que vamos ver aqui e seus tipos de dados variam somente entre listas e átomos. E assim foi mantido o foco de processamento de listas em todas as linguagens funcionais e suas funções e abstrações para resolver problemas relativos a listas e estruturas iteráveis. Uma curiosidade é que para quem não sabe porque em lisp existem tantos parênteses é que ele é baseado em s-expression, uma coisa que temos um "equivalente" evoluído em python, que parte dos teoremas de gramáticas livres de contexto:</p>
+<div class="highlight"><pre><span></span><span class="p">(</span><span class="nb">+ </span><span class="mi">4</span> <span class="mi">5</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Sim, isso é uma soma em lisp. Diferente das linguagens imperativas como costumamos ver:</p>
+<div class="highlight"><pre><span></span><span class="mi">4</span> <span class="o">+</span> <span class="mi">5</span>
+</pre></div>
+
+
+<p>Uma assertiva pode ser feita dessa maneira:</p>
+<ul>
+<li>Funcional (ou declarativa)</li>
+</ul>
+<div class="highlight"><pre><span></span><span class="p">(</span><span class="nb">= </span><span class="mi">4</span> <span class="p">(</span><span class="nb">+ </span><span class="mi">2</span> <span class="mi">2</span><span class="p">))</span>
+</pre></div>
+
+
+<ul>
+<li>Imperativa</li>
+</ul>
+<div class="highlight"><pre><span></span><span class="p">(</span><span class="mi">2</span> <span class="o">+</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">4</span>
+</pre></div>
+
+
+<p>Chega de enrolação e vamos correr com essa introdução, não viemos aqui para aprender Lisp ou C. Mas acho que parte desse contexto pode nos ajudar e muito quando formos nos aprofundar em alguns tópicos. Pretendo sempre que iniciar uma nova ferramenta da programação funcional ao menos explicar em que contexto ela foi desenvolvida e para resolver cada tipo de problema.</p>
+<h2>0.2 Técnicas usadas por linguagens funcionais</h2>
+<p>Vamos tentar mapear o que as linguagens funcionais fazem de diferente das linguagens imperativas, mas não vamos nos aprofundar nesse tópicos agora, pois são coisas às vezes complexas sem o entendimento prévio de outros contextos, mas vamos tentar só explanar pra que você se sinta empolgado por estar aqui:</p>
+<ul>
+<li>
+<p>Funções como objetos de primeira classe:</p>
+<ul>
+<li>São funções que podem estar em qualquer lugar (em estruturas, declaradas em tempo de execução).</li>
+</ul>
+</li>
+<li>
+<p>Funções de ordem superior:</p>
+<ul>
+<li>São funções que podem receber funções como argumentos e retornar funções.</li>
+</ul>
+</li>
+<li>
+<p>Funções puras:</p>
+<ul>
+<li>São funções que não sofrem interferências de meios externos (variáveis de fora). Evita efeitos colaterais.</li>
+</ul>
+</li>
+<li>
+<p>Recursão, como oposição aos loops:</p>
+<ul>
+<li>Frequentemente a recursão na matemática é uma coisa mais intuitiva e é só chamar tudo outra vez, no lugar de ficar voltando ao ponto inicial da iteração.</li>
+</ul>
+</li>
+<li>
+<p>Foco em processamento de iteráveis:</p>
+<ul>
+<li>Como dito anteriormente, pensar em como as sequências podem nos ajudar a resolver problemas.</li>
+</ul>
+</li>
+<li>
+<p>O que deve ser computado, não como computar:</p>
+<ul>
+<li>Não ser tão expressivo e aceitar que as intruções não tem necessidade de estar explicitas todas as vezes, isso ajuda em legibilidade.</li>
+</ul>
+</li>
+<li>
+<p>Lazy evaluation:</p>
+<ul>
+<li>Criar sequências infinitas sem estourar nossa memória.</li>
+</ul>
+</li>
+</ul>
+<h2>0.3 Python é uma linguagem funcional?</h2>
+<h4>Não. Mas é uma linguagem que implementa muitos paradígmas e porque não usar todos de uma vez?</h4>
+<p>O objetivo desse 'conjunto de vídeos' é escrever código que gere menos efeito colateral e código com menos estados. Só que isso tudo feito na medida do possível, pois Python não é uma linguagem funcional. Porém, podemos contar o máximo possível com as features presentes do paradígma em python.</p>
+<p>Exemplos de funcional (básicos) em python:</p>
+<div class="highlight"><pre><span></span><span class="c1"># Gerar uma lista da string # Imperativo</span>
+<span class="n">string</span> <span class="o">=</span> <span class="s1">'Python'</span>
+<span class="n">lista</span> <span class="o">=</span> <span class="p">[]</span> <span class="c1"># estado inicial</span>
+
+<span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="n">string</span><span class="p">:</span>
+ <span class="n">lista</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">l</span><span class="p">)</span> <span class="c1"># cada iteração gera um novo estado</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">lista</span><span class="p">)</span> <span class="c1"># ['P', 'y', 't', 'h', 'o', 'n']</span>
+</pre></div>
+
+
+<div class="highlight"><pre><span></span><span class="c1"># Gerar uma lista da string # Funcional</span>
+<span class="n">string</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span>
+
+<span class="n">lista</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="n">string</span><span class="p">(</span><span class="s1">'Python'</span><span class="p">)))</span> <span class="c1"># atribuição a um novo objeto</span>
+
+<span class="nb">print</span><span class="p">(</span><span class="n">lista</span><span class="p">)</span> <span class="c1"># ['P', 'y', 't', 'h', 'o', 'n']</span>
+</pre></div>
+
+
+<p>Como você pode ver, depois de uma explanação básica das técnicas, a segunda implementação não sofre interferência do meio externo (Funções puras), evita loops e sua saída sem o construtor de list é lazy. Mas não se assuste, vamos abordar tudo isso com calma.</p>
+<h2>0.4 A quem esse 'curso' é destinado?</h2>
+<p>Primeiramente gostaria de dizer que roubei essa ideia dos infinitos livros da O’Reilly, que sempre exibem esse tópico. Mas vamos ao assunto. Este curso é para você que sabe o básico de Python, e quando digo básico quero dizer que consegue fazer qualquer coisa com um pouco de pesquisa na internet. O básico de programação se reduz a isso. Vamos falar sobre coisas simples e coisas mais complexas, mas pretendo manter o bom senso para que todos possam absorver o máximo de conteúdo possível.</p>
+<p>Então, caso você venha do Python (OO ou procedural) você vai encontrar aqui uma introdução a programação funcional descontraída e sem uma tonelada de material difícil de entender. Caso você venha de linguagens funcionais como Haskell e Lisp, você pode se sentir um pouco estranho com tantas declarações, mas aprenderá a se expressar em Python. Caso você venha de linguagens funcionais modernas como Clojure e Scala, as coisas são bem parecidas por aqui.</p>
+<p>Então tente tirar o máximo de proveito. Vamos nos divertir.</p>
+<h2>0.5 Apresentando o Jaber</h2>
+<p>Jaber é nosso aluno de mentira, mas vamos pensar que ele é um aluno que senta na primeira fileira e pergunta de tudo, sempre que acha necessário. Roubei essa ideia do livro de expressões regulares do Aurélio. Ele tem um personagem, Piazinho, e acho que toda interação com ele é sempre graciosa e tira dúvidas quando tudo parece impossível.</p>
+<h2>0.6 Sobre as referências</h2>
+<p>Não gosto muito de citar referências pois procurei não copiar texto dos livros, mas muita coisa contida neles serve de base para o entendimento de certos tópicos. Outro motivo é o nível de complexidade dos exemplos ou explicações que tentei reduzir ao máximo enquanto escrevia esses roteiros. Para um exemplo, você pode olhar o livro do Steven Lott, cheio de fórmulas e abstrações matemáticas que em certo ponto acabam comprometendo o entendimento de quem não tem uma sólida base em computação teórica ou matemática.</p>
+<p>Como um todo, as referências serviram como guia, foi o que lí quando dúvidas para explicações surgiram. Não tiro nenhum crédito delas e as exponho para que todos saibam que existem muitos livros bons e que boa parte do que é passado aqui, foi aprendido neles.</p>
+<h2>0.7 Mais sobre o histórico das linguagens funcionais</h2>
+<p>Se você pretende realmente se aprofundar no assunto enquanto acompanha esse curso, fazer uma imersão ou coisa parecida. Tudo começa com o cálculo lambda mentalizado pelo incrível <a href="https://en.wikipedia.org/wiki/Alonzo_Church">Alonzo Church</a>. Caso você não o conheça, ele foi um matemático fantástico e teve uma carreira acadêmica brilhante. Foi o orientador de pessoas incríveis como Alan Turing, Raymond Smullyan etc...</p>
+<p>Outro grande homem e que vale a pena mencionar e ser buscado é o <a href="https://pt.wikipedia.org/wiki/Haskell_Curry">Haskell Curry</a>, um lógico que trouxe excelentes contribuições para o que chamamos hoje de programação funcional.</p>
+<p>A primeira linguagem funcional 'oficial' (não gosto muito de dizer isso) é o Lisp (List Processing) criada pelo fenomenal <a href="https://pt.wikipedia.org/wiki/John_McCarthy">John McCarthy</a> que também vale a pena ser pesquisado e estudado.</p>
+<p>Veremos o básico sobre os tipos de função no próximo tópico.</p>
+<p>OBS: <a href="https://github.com/z4r4tu5tr4/python-funcional/blob/master/referencias.md">Referências</a> usadas durante todos os tópicos.</p>
+ Mon, 20 Nov 2017 21:43:00 +0000
+
+
+ PythonClub: Peewee - Um ORM Python minimalista
+ tag:pythonclub.com.br,2017-07-20:/peewee-um-orm-python-minimalista.html
+ https://pythonclub.com.br/peewee-um-orm-python-minimalista.html
+ <p><a href="http://peewee.readthedocs.io/en/latest/index.html">Peewee</a> é um ORM destinado a criar e gerenciar tabelas de banco de dados relacionais através de objetos Python. Segundo a <a href="https://pt.wikipedia.org/wiki/Mapeamento_objeto-relacional">wikipedia</a>, um ORM é:</p>
+<blockquote>
+<p>Mapeamento objeto-relacional (ou ORM, do inglês: Object-relational mapping) é uma técnica de desenvolvimento > utilizada para reduzir a impedância da programação orientada aos objetos utilizando bancos de dados relacionais. As tabelas do banco de dados são representadas através de classes e os registros de cada tabela são representados como instâncias das classes correspondentes.</p>
+</blockquote>
+<p>O que um ORM faz é, basicamente, transformar classes Python em tabelas no banco de dados, além de permitir construir <em>querys</em> usando diretamente objetos Python ao invés de SQL.</p>
+<p>O Peewee é destinado a projetos de pequeno/médio porte e se destaca pela simplicidade quando comparado a outros ORM mais conhecidos, como o SQLAlchemy. Uma analogia utilizada pelo autor da API e que acho muito interessante é que Peewee está para o SQLAlchemy assim como SQLite está para o PostgreSQL.</p>
+<p>Em relação aos recursos por ele oferecidos, podemos citar que ele possui suporte nativo a SQLite, PostgreSQL e MySQL, embora seja necessário a instalação de <em>drivers</em> para utilizá-lo com PostgreSQL e MySQL e suporta tanto Python 2.6+ quanto Python 3.4+.</p>
+<p>Neste tutorial, utilizaremos o SQLite, por sua simplicidade de uso e pelo Python possuir suporte nativo ao mesmo (usaremos o Python 3.5).</p>
+<h2>Instalação</h2>
+<p>O Peewee pode ser facilmente instalado com o gerenciador de pacotes <em>pip</em> (recomendo a instalação em um virtualenv):</p>
+<div class="highlight"><pre><span></span><span class="err">pip install peewee</span>
+</pre></div>
+
+
+<h2>Criando o banco de dados</h2>
+<p>Para criar o banco de dados é bem simples. Inicialmente passamos o nome do nosso banco de dados (a extensão <code>*.db</code> indica um arquivo do SQLite).</p>
+<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">peewee</span>
+
+<span class="c1"># Aqui criamos o banco de dados</span>
+<span class="n">db</span> <span class="o">=</span> <span class="n">peewee</span><span class="o">.</span><span class="n">SqliteDatabase</span><span class="p">(</span><span class="s1">'codigo_avulso.db'</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Diferente de outros bancos de dados que funcionam através um servidor, o SQLite cria um arquivo de extensão <code>*.db</code>, onde todos os nossos dados são armazenados.</p>
+<blockquote>
+<p>Caso deseje ver as tabelas existentes no arquivo <code>codigo_avulso.db</code>, instale o aplicativo <code>SQLiteBrowser</code>. Com ele fica fácil monitorar as tabelas criadas e acompanhar o tutorial.</p>
+</blockquote>
+<div class="highlight"><pre><span></span> sudo apt-get install sqlitebrowser
+</pre></div>
+
+
+<p>A título de exemplo, vamos criar um banco destinado a armazenar nomes de livros e de seus respectivos autores. Iremos chamá-lo de <code>models.py</code>.</p>
+<p>Inicialmente, vamos criar a classe base para todos os nossos <code>models</code>. Esta é uma abordagem recomendada pela documentação e é considerada uma boa prática. Também adicionaremos um log para acompanharmos as mudanças que são feitas no banco:</p>
+<div class="highlight"><pre><span></span><span class="c1"># models.py</span>
+
+<span class="kn">import</span> <span class="nn">peewee</span>
+
+<span class="c1"># Criamos o banco de dados</span>
+<span class="n">db</span> <span class="o">=</span> <span class="n">peewee</span><span class="o">.</span><span class="n">SqliteDatabase</span><span class="p">(</span><span class="s1">'codigo_avulso.db'</span><span class="p">)</span>
+
+
+<span class="k">class</span> <span class="nc">BaseModel</span><span class="p">(</span><span class="n">peewee</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
+ <span class="sd">"""Classe model base"""</span>
+
+ <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
+ <span class="c1"># Indica em qual banco de dados a tabela</span>
+ <span class="c1"># 'author' sera criada (obrigatorio). Neste caso,</span>
+ <span class="c1"># utilizamos o banco 'codigo_avulso.db' criado anteriormente</span>
+ <span class="n">database</span> <span class="o">=</span> <span class="n">db</span>
+</pre></div>
+
+
+<p>A class <code>BaseModel</code> é responsável por criar a conexão com nosso banco de dados.</p>
+<p>Agora, vamos criar a model que representa os autores:</p>
+<div class="highlight"><pre><span></span><span class="c1"># models.py</span>
+
+<span class="k">class</span> <span class="nc">Author</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
+
+ <span class="sd">"""</span>
+<span class="sd"> Classe que representa a tabela Author</span>
+<span class="sd"> """</span>
+ <span class="c1"># A tabela possui apenas o campo 'name', que receberá o nome do autor sera unico</span>
+ <span class="n">name</span> <span class="o">=</span> <span class="n">peewee</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Se observamos a model <code>Author</code>, veremos que não foi especificado nenhuma coluna como <em>primary key</em> (chave primaria), sendo assim o Peewee irá criar um campo chamado <code>id</code> do tipo inteiro com auto incremento para funcionar como chave primária.
+Em seguida, no mesmo arquivo <code>models.py</code> criamos a classe que representa os livros. Ela possui uma relação de "muitos para um" com a tabela de autores, ou seja, cada livro possui apenas um autor, mas um autor pode possuir vários livros.</p>
+<div class="highlight"><pre><span></span><span class="c1"># models.py</span>
+
+<span class="k">class</span> <span class="nc">Book</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
+ <span class="sd">"""</span>
+<span class="sd"> Classe que representa a tabela Book</span>
+<span class="sd"> """</span>
+
+ <span class="c1"># A tabela possui apenas o campo 'title', que receberá o nome do livro</span>
+ <span class="n">title</span> <span class="o">=</span> <span class="n">peewee</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
+
+ <span class="c1"># Chave estrangeira para a tabela Author</span>
+ <span class="n">author</span> <span class="o">=</span> <span class="n">peewee</span><span class="o">.</span><span class="n">ForeignKeyField</span><span class="p">(</span><span class="n">Author</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Agora, adicionamos o código que cria as tabelas <code>Author</code> e <code>Book</code>.</p>
+<div class="highlight"><pre><span></span><span class="c1"># models.py</span>
+
+<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">Author</span><span class="o">.</span><span class="n">create_table</span><span class="p">()</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s2">"Tabela 'Author' criada com sucesso!"</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">peewee</span><span class="o">.</span><span class="n">OperationalError</span><span class="p">:</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s2">"Tabela 'Author' ja existe!"</span><span class="p">)</span>
+
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">Book</span><span class="o">.</span><span class="n">create_table</span><span class="p">()</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s2">"Tabela 'Book' criada com sucesso!"</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">peewee</span><span class="o">.</span><span class="n">OperationalError</span><span class="p">:</span>
+ <span class="nb">print</span><span class="p">(</span><span class="s2">"Tabela 'Book' ja existe!"</span><span class="p">)</span>
+</pre></div>
+
+
+<p>excerpt
+Agora executamos o <code>models.py</code>:</p>
+<div class="highlight"><pre><span></span><span class="err">python models.py</span>
+</pre></div>
+
+
+<p>A estrutura do diretório ficou assim:</p>
+<div class="highlight"><pre><span></span>.
+├── codigo_avulso.db
+├── models.py
+</pre></div>
+
+
+<p>Após executarmos o código, será criado um arquivo de nome <code>codigo_avulso.db</code> no mesmo diretório do nosso arquivo <code>models.py</code>, contendo as tabelas <code>Author</code> e <code>Book</code>.</p>
+<h2>Realizando o CRUD</h2>
+<p>Agora vamos seguir com as 4 principais operações que podemos realizar em um banco de dados, também conhecida como CRUD.</p>
+<p>A sigla <code>CRUD</code> é comumente utilizada para designar as quatro operações básicas que pode-se executar em um banco de dados, sendo elas: </p>
+<div class="highlight"><pre><span></span><span class="err">- Create (criar um novo registro no banco)</span>
+<span class="err">- Read (ler/consultar um registro)</span>
+<span class="err">- Update (atualizar um registro)</span>
+<span class="err">- Delete (excluir um registro do banco)</span>
+</pre></div>
+
+
+<p>Iremos abordar cada uma dessas operações.</p>
+<h3>Create: Inserindo dados no banco</h3>
+<p>Agora, vamos popular nosso banco com alguns autores e seus respectivos livros. Para isso criamos um arquivo <code>create.py</code>. A estrutura do diretório ficou assim:</p>
+<div class="highlight"><pre><span></span>.
+├── codigo_avulso.db
+├── models.py
+├── create.py
+</pre></div>
+
+
+<p>A criação dos registros no banco pode ser feito através do método <code>create</code>, quando desejamos inserir um registro apenas; ou pelo método <code>insert_many</code>, quando desejamos inserir vários registros de uma vez em uma mesma tabela.</p>
+<div class="highlight"><pre><span></span><span class="c1"># create.py</span>
+
+<span class="kn">from</span> <span class="nn">models</span> <span class="kn">import</span> <span class="n">Author</span><span class="p">,</span> <span class="n">Book</span>
+
+<span class="c1"># Inserimos um autor de nome "H. G. Wells" na tabela 'Author'</span>
+<span class="n">author_1</span> <span class="o">=</span> <span class="n">Author</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'H. G. Wells'</span><span class="p">)</span>
+
+<span class="c1"># Inserimos um autor de nome "Julio Verne" na tabela 'Author'</span>
+<span class="n">author_2</span> <span class="o">=</span> <span class="n">Author</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'Julio Verne'</span><span class="p">)</span>
+
+<span class="n">book_1</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'title'</span><span class="p">:</span> <span class="s1">'A Máquina do Tempo'</span><span class="p">,</span>
+ <span class="s1">'author_id'</span><span class="p">:</span> <span class="n">author_1</span><span class="p">,</span>
+<span class="p">}</span>
+
+<span class="n">book_2</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'title'</span><span class="p">:</span> <span class="s1">'Guerra dos Mundos'</span><span class="p">,</span>
+ <span class="s1">'author_id'</span><span class="p">:</span> <span class="n">author_1</span><span class="p">,</span>
+<span class="p">}</span>
+
+<span class="n">book_3</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'title'</span><span class="p">:</span> <span class="s1">'Volta ao Mundo em 80 Dias'</span><span class="p">,</span>
+ <span class="s1">'author_id'</span><span class="p">:</span> <span class="n">author_2</span><span class="p">,</span>
+<span class="p">}</span>
+
+<span class="n">book_4</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s1">'title'</span><span class="p">:</span> <span class="s1">'Vinte Mil Leguas Submarinas'</span><span class="p">,</span>
+ <span class="s1">'author_id'</span><span class="p">:</span> <span class="n">author_1</span><span class="p">,</span>
+<span class="p">}</span>
+
+<span class="n">books</span> <span class="o">=</span> <span class="p">[</span><span class="n">book_1</span><span class="p">,</span> <span class="n">book_2</span><span class="p">,</span> <span class="n">book_3</span><span class="p">,</span> <span class="n">book_4</span><span class="p">]</span>
+
+<span class="c1"># Inserimos os quatro livros na tabela 'Book'</span>
+<span class="n">Book</span><span class="o">.</span><span class="n">insert_many</span><span class="p">(</span><span class="n">books</span><span class="p">)</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span>
+</pre></div>
+
+
+<h3>Read: Consultando dados no banco</h3>
+<p>O Peewee possui comandos destinados a realizar consultas no banco. De maneira semelhante ao conhecido <code>SELECT</code>. Podemos fazer essa consulta de duas maneiras. Se desejamos o primeiro registro que corresponda a nossa pesquisa, podemos utilizar o método <code>get()</code>.</p>
+<div class="highlight"><pre><span></span><span class="c1"># read.py</span>
+
+<span class="kn">from</span> <span class="nn">models</span> <span class="kn">import</span> <span class="n">Author</span><span class="p">,</span> <span class="n">Book</span>
+
+<span class="n">book</span> <span class="o">=</span> <span class="n">Book</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">Book</span><span class="o">.</span><span class="n">title</span> <span class="o">==</span> <span class="s2">"Volta ao Mundo em 80 Dias"</span><span class="p">)</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">book</span><span class="o">.</span><span class="n">title</span><span class="p">)</span>
+
+<span class="c1"># Resultado</span>
+<span class="c1"># * Volta ao Munto em 80 Dias</span>
+</pre></div>
+
+
+<p>Porém, se desejamos mais de um registro, utilizamos o método <code>select</code>. Por exemplo, para consultar todos os livros escritos pelo autor "H. G. Wells".</p>
+<div class="highlight"><pre><span></span><span class="c1"># read.py</span>
+
+<span class="n">books</span> <span class="o">=</span> <span class="n">Book</span><span class="o">.</span><span class="n">select</span><span class="p">()</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">Author</span><span class="p">)</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="n">Author</span><span class="o">.</span><span class="n">name</span><span class="o">==</span><span class="s1">'H. G. Wells'</span><span class="p">)</span>
+
+<span class="c1"># Exibe a quantidade de registros que corresponde a nossa pesquisa</span>
+<span class="nb">print</span><span class="p">(</span><span class="n">books</span><span class="o">.</span><span class="n">count</span><span class="p">())</span>
+
+<span class="k">for</span> <span class="n">book</span> <span class="ow">in</span> <span class="n">books</span><span class="p">:</span>
+ <span class="nb">print</span><span class="p">(</span><span class="n">book</span><span class="o">.</span><span class="n">title</span><span class="p">)</span>
+
+<span class="c1"># Resultado:</span>
+<span class="c1"># * A Máquina do Tempo</span>
+<span class="c1"># * Guerra dos Mundos</span>
+<span class="c1"># * Vinte Mil Leguas Submarinas</span>
+</pre></div>
+
+
+<p>Também podemos utilizar outras comandos do SQL como <code>limit</code> e <code>group</code> (para mais detalhes, ver a documentação <a href="http://peewee.readthedocs.io/en/latest/index.html">aqui</a>).</p>
+<p>A estrutura do diretório ficou assim:</p>
+<div class="highlight"><pre><span></span>.
+├── codigo_avulso.db
+├── models.py
+├── create.py
+├── read.py
+</pre></div>
+
+
+<h3>Update: Alterando dados no banco</h3>
+<p>Alterar dados também é bem simples. No exemplo anterior, se observarmos o resultado da consulta dos livros do autor "H. G. Wells", iremos nos deparar com o livro de título "Vinte Mil Léguas Submarinas". Se você, caro leitor, gosta de contos de ficção-científica, sabe que esta obra foi escrito por "Julio Verne", coincidentemente um dos autores que também estão cadastrados em nosso banco. Sendo assim, vamos corrigir o autor do respectivo livro.</p>
+<p>Primeiro vamos buscar o registro do autor e do livro:</p>
+<div class="highlight"><pre><span></span><span class="c1"># update.py</span>
+
+<span class="kn">from</span> <span class="nn">models</span> <span class="kn">import</span> <span class="n">Author</span><span class="p">,</span> <span class="n">Book</span>
+
+<span class="n">new_author</span> <span class="o">=</span> <span class="n">Author</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">Author</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s1">'Julio Verne'</span><span class="p">)</span>
+<span class="n">book</span> <span class="o">=</span> <span class="n">Book</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">Book</span><span class="o">.</span><span class="n">title</span><span class="o">==</span><span class="s2">"Vinte Mil Leguas Submarinas"</span><span class="p">)</span>
+</pre></div>
+
+
+<p>Agora vamos alterar o autor e gravar essa alteração no banco.</p>
+<div class="highlight"><pre><span></span><span class="c1"># update.py</span>
+
+<span class="c1"># Alteramos o autor do livro</span>
+<span class="n">book</span><span class="o">.</span><span class="n">author</span> <span class="o">=</span> <span class="n">new_author</span>
+
+<span class="c1"># Salvamos a alteração no banco</span>
+<span class="n">book</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+</pre></div>
+
+
+<p>A estrutura do diretório ficou assim:</p>
+<div class="highlight"><pre><span></span>.
+├── codigo_avulso.db
+├── models.py
+├── create.py
+├── read.py
+├── update.py
+</pre></div>
+
+
+<h3>Delete: Deletando dados do banco</h3>
+<p>Assim como as operações anteriores, também podemos deletar registros do banco de maneira bem prática. Como exemplo, vamos deletar o livro "Guerra dos Mundos" do nosso banco de dados.</p>
+<div class="highlight"><pre><span></span><span class="c1"># delete.py</span>
+
+<span class="kn">from</span> <span class="nn">models</span> <span class="kn">import</span> <span class="n">Author</span><span class="p">,</span> <span class="n">Book</span>
+
+<span class="c1"># Buscamos o livro que desejamos excluir do banco</span>
+<span class="n">book</span> <span class="o">=</span> <span class="n">Book</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">Book</span><span class="o">.</span><span class="n">title</span><span class="o">==</span><span class="s2">"Guerra dos Mundos"</span><span class="p">)</span>
+
+<span class="c1"># Excluimos o livro do banco</span>
+<span class="n">book</span><span class="o">.</span><span class="n">delete_instance</span><span class="p">()</span>
+</pre></div>
+
+
+<p>Simples não?</p>
+<p>A estrutura do diretório ficou assim:</p>
+<div class="highlight"><pre><span></span>.
+├── codigo_avulso.db
+├── models.py
+├── create.py
+├── read.py
+├── update.py
+├── delete.py
+</pre></div>
+
+
+<h2>Conclusão</h2>
+<p>É isso pessoal. Este tutorial foi uma introdução bem enxuta sobre o Peewee. Ainda existem muitos tópicos que não abordei aqui, como a criação de <em>primary_key</em>, de campos <em>many2many</em> entre outros recursos, pois foge do escopo deste tutorial. Se você gostou do ORM, aconselho a dar uma olhada também na sua documentação, para conseguir extrair todo o potencial da ferramenta. A utilização de um ORM evita que o desenvolvedor perca tempo escrevendo <em>query</em> SQL e foque totalmente no desenvolvimento de código.</p>
+<h2>Referências</h2>
+<ul>
+<li><a href="http://peewee.readthedocs.io/en/latest/index.html">Documentação do Peewee (em inglês)</a></li>
+<li><a href="https://www.blog.pythonlibrary.org/2014/07/17/an-intro-to-peewee-another-python-orm/">An Intro to peewee – Another Python ORM</a></li>
+<li><a href="http://jonathansoma.com/tutorials/webapps/intro-to-peewee/">Introduction to peewee</a></li>
+<li><a href="https://www.novatec.com.br/livros/introducao-sql/">Introdução à Linguagem SQL</a></li>
+</ul>
+ Fri, 21 Jul 2017 02:45:24 +0000
+
+
+ Flávio Coelho: Curso de introdução a criptomoedas - Aula 01
+ tag:blogger.com,1999:blog-840127493134277990.post-6812804658380854111
+ http://pyinsci.blogspot.com/2017/06/curso-de-introducao-criptomoedas-aula-01.html
+ For the Portuguese speaking readers of this blog, I am starting an Introductory course on Cryptocurrencies and applications on the blockchain which is an online version of a standard classroom course I am starting now at FGV.
+
+This is the first lecture which is basically an intro to the topic and the structure of the course.
+
+The online version should have the main content of the lectures on
+ Sat, 17 Jun 2017 13:50:44 +0000
+ noreply@blogger.com (Anonymous)
+
+
+
+