<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Dev Notes</title><link href="/gist-blog/" rel="alternate"/><link href="/gist-blog/feeds/all.atom.xml" rel="self"/><id>/gist-blog/</id><updated>2026-04-09T04:06:35.351017+00:00</updated><entry><title>Understanding uv sync and dependency conflicts</title><link href="/gist-blog/posts/2026/04/understanding-uv-sync-and-dependency-conflicts/" rel="alternate"/><published>2026-04-07T23:00:00+00:00</published><updated>2026-04-09T04:06:34.860952+00:00</updated><author><name>Artur Barseghyan</name></author><id>tag:None,2026-04-07:/gist-blog/posts/2026/04/understanding-uv-sync-and-dependency-conflicts/</id><summary type="html">&lt;img src="https://github.com/barseghyanartur/gist-blog-images/blob/main/images/axolotl.webp?raw=true" alt="Preview" class="preview-image" style="max-width:100%; height:auto; border-radius:8px; margin-bottom:1.5rem; display:block;"&gt;&lt;p class="first last"&gt;While &lt;cite&gt;uv pip compile&lt;/cite&gt; only resolves what you specifically request, &lt;cite&gt;uv sync&lt;/cite&gt; attempts to resolve every optional dependency in your project simultaneously. This can lead to unexpected failures if different &amp;quot;extras,&amp;quot; such as ai and deploy, have conflicting requirements. You can fix this by explicitly declaring these incompatible extras in your &lt;cite&gt;pyproject.toml&lt;/cite&gt; file to guide the resolver correctly.&lt;/p&gt;</summary><content type="html">&lt;p&gt;If you're switching to &lt;tt class="docutils literal"&gt;uv sync&lt;/tt&gt;, keep in mind that it behaves differently than the standard &lt;tt class="docutils literal"&gt;uv pip&lt;/tt&gt; workflow. Here is a quick breakdown of what to expect and how to fix common issues.&lt;/p&gt;
&lt;div class="section" id="the-trap-uv-sync-resolves-everything"&gt;
&lt;h2&gt;The Trap: uv sync Resolves Everything&lt;/h2&gt;
&lt;p&gt;Unlike &lt;tt class="docutils literal"&gt;uv pip compile&lt;/tt&gt;, which only looks at what you ask for, &lt;strong&gt;uv sync attempts to resolve every dependency in your project&lt;/strong&gt;, including all optional &amp;quot;extras.&amp;quot;&lt;/p&gt;
&lt;p&gt;Even if you only request specific extras (e.g., &lt;tt class="docutils literal"&gt;uv sync &lt;span class="pre"&gt;--extra&lt;/span&gt; api&lt;/tt&gt;), &lt;tt class="docutils literal"&gt;uv&lt;/tt&gt; still checks if the rest of your extras (like &lt;tt class="docutils literal"&gt;deploy&lt;/tt&gt;) are compatible with your environment. If they conflict, the command will fail.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-solution-declaring-conflicts"&gt;
&lt;h2&gt;The Solution: Declaring Conflicts&lt;/h2&gt;
&lt;p&gt;If you have optional dependencies that are fundamentally incompatible—meaning they aren't meant to be installed together—you can tell &lt;tt class="docutils literal"&gt;uv&lt;/tt&gt; to ignore those conflicts during the sync process.&lt;/p&gt;
&lt;p&gt;Add a &lt;tt class="docutils literal"&gt;conflicts&lt;/tt&gt; section to your &lt;tt class="docutils literal"&gt;pyproject.toml&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[tool.uv]&lt;/span&gt;
&lt;span class="n"&gt;conflicts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ai&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;deploy&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="the-result"&gt;
&lt;h2&gt;The Result&lt;/h2&gt;
&lt;p&gt;By explicitly declaring that &lt;tt class="docutils literal"&gt;ai&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;deploy&lt;/tt&gt; are mutually exclusive, &lt;tt class="docutils literal"&gt;uv sync &lt;span class="pre"&gt;--extra&lt;/span&gt; api &lt;span class="pre"&gt;--extra&lt;/span&gt; ai&lt;/tt&gt; will now work perfectly. It effectively tells the resolver, &amp;quot;I know these two don't get along; just focus on the ones I actually requested.&amp;quot;&lt;/p&gt;
&lt;/div&gt;
&lt;p style="margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #ddd; font-size: 0.9rem; color: #555; text-align: right;"&gt;Originally published as &lt;a href="https://gist.github.com/barseghyanartur/51837dab7bb02c9b5b1c9509693c7d92" target="_blank" rel="noopener"&gt;GitHub Gist #51837dab7bb02c9b5b1c9509693c7d92&lt;/a&gt;&lt;/p&gt;</content><category term="Tech"/><category term="python"/><category term="uv"/></entry><entry><title>licence-normaliser: Taming licence chaos in Python</title><link href="/gist-blog/posts/2026/04/licence-normaliser-taming-licence-chaos-in-python/" rel="alternate"/><published>2026-04-04T14:00:00+00:00</published><updated>2026-04-09T04:06:33.416848+00:00</updated><author><name>Artur Barseghyan</name></author><id>tag:None,2026-04-04:/gist-blog/posts/2026/04/licence-normaliser-taming-licence-chaos-in-python/</id><summary type="html">&lt;img src="https://raw.githubusercontent.com/barseghyanartur/licence-normaliser/main/docs/_static/licence_normaliser_logo.webp" alt="Preview" class="preview-image" style="max-width:100%; height:auto; border-radius:8px; margin-bottom:1.5rem; display:block;"&gt;&lt;p class="first last"&gt;Tired of wrestling with messy license strings like &amp;quot;MIT License&amp;quot; vs. &amp;quot;The MIT License&amp;quot; or cryptic URLs? licence-normaliser is a lightweight Python tool that tames the chaos. It maps inconsistent metadata into a clean, machine-readable hierarchy (Family → License → Version), turning strings like CC BY-NC-ND 4.0 into a tidy cc-by-nc-nd-4.0 automatically. Whether you're scraping repos or managing compliance, it handles SPDX codes, prose, and even complex Creative Commons variants with ease—giving you a single source of truth for your legal metadata.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Hey, ever tried cleaning up messy license strings — like &lt;tt class="docutils literal"&gt;CC &lt;span class="pre"&gt;BY-NC-ND&lt;/span&gt; 4.0&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;MIT License&lt;/tt&gt; — and getting them into something tidy and machine-readable?
That's exactly what &lt;strong&gt;licence-normaliser&lt;/strong&gt; does, and honestly, it's a lifesaver for anyone dealing with open-source compliance or metadata.&lt;/p&gt;
&lt;img alt="licence-normaliser" class="align-center" src="https://raw.githubusercontent.com/barseghyanartur/licence-normaliser/main/docs/_static/licence_normaliser_logo.webp" style="width: 100%;" /&gt;
&lt;p&gt;Check out the repo here: &lt;a class="reference external" href="https://github.com/barseghyanartur/licence-normaliser"&gt;licence-normaliser&lt;/a&gt;.
It's a lightweight Python library that turns chaos into order using a neat three-level system:
&lt;strong&gt;family&lt;/strong&gt; → &lt;strong&gt;licence&lt;/strong&gt; → &lt;strong&gt;version&lt;/strong&gt;. Think &lt;tt class="docutils literal"&gt;cc&lt;/tt&gt; → &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cc-by-nc-nd&lt;/span&gt;&lt;/tt&gt; → &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cc-by-nc-nd-4.0&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Here's a quick demo — imagine you're scraping papers or repos and licenses come in every flavor:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;licence_normaliser&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;normalise_licence&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;normalise_licence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;CC BY-NC-ND 4.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           &lt;span class="c1"&gt;# → cc-by-nc-nd-4.0&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;licence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# → cc-by-nc-nd&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;licence&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;family&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# → cc&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Super clean, right? It handles SPDX codes (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Apache-2.0&lt;/span&gt;&lt;/tt&gt;), full URLs, even sloppy prose like
&amp;quot;creative commons attribution non-commercial no derivatives&amp;quot;. And for Creative Commons fans — yes,
it knows all the variants, including the weird IGO ones.&lt;/p&gt;
&lt;p&gt;Look at these badges to see what it normalizes:&lt;/p&gt;
&lt;img alt="Creative Commons license badges showing the normalized family and variant icons" class="align-center" src="https://github.com/barseghyanartur/gist-blog-images/blob/main/images/creative-commons-783531_1280.webp?raw=true" style="width: 100%;" /&gt;
&lt;p&gt;Or the compatibility chart if you're remixing stuff:&lt;/p&gt;
&lt;img alt="License compatibility chart with checkmarks and crosses for remixing different licenses" class="align-center" src="https://github.com/barseghyanartur/gist-blog-images/blob/main/images/CC_License_Compatibility_Chart.png?raw=true" style="width: 100%;" /&gt;
&lt;p&gt;What makes it robust? Everything's file-driven — aliases, patterns, URLs live in JSON, so you add new
synonyms without touching code. Want strictness? Pass &lt;tt class="docutils literal"&gt;strict=True&lt;/tt&gt; and it'll raise an error if it
can't match. Debugging? Use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--explain&lt;/span&gt;&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;trace=True&lt;/tt&gt; to see the whole resolution path.&lt;/p&gt;
&lt;p&gt;Install's dead simple:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;licence-normaliser
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(or &lt;tt class="docutils literal"&gt;uv pip install&lt;/tt&gt; if you're fancy).&lt;/p&gt;
&lt;p&gt;CLI's handy too:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;licence-normaliser&lt;span class="w"&gt; &lt;/span&gt;normalise&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;MIT&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;# → mit&lt;/span&gt;
licence-normaliser&lt;span class="w"&gt; &lt;/span&gt;batch&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Apache-2.0&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;CC BY 4.0&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It's got only two stars right now — probably because it's niche — but if you're building anything
with license detection (think ScanCode integration, repo crawlers, academic tools), this quietly
solves a headache. It gets updated via CLI data pulls from SPDX, OSI, Creative Commons...
no manual hassle.&lt;/p&gt;
&lt;p&gt;Bottom line: if licenses are your mess, normalise them. This tool just works.&lt;/p&gt;
&lt;p style="margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #ddd; font-size: 0.9rem; color: #555; text-align: right;"&gt;Originally published as &lt;a href="https://gist.github.com/barseghyanartur/93cafc05616758479eed6377f6593246" target="_blank" rel="noopener"&gt;GitHub Gist #93cafc05616758479eed6377f6593246&lt;/a&gt;&lt;/p&gt;</content><category term="Tech"/><category term="python"/><category term="licence"/></entry><entry><title>Log4Brains images support</title><link href="/gist-blog/posts/2026/04/log4brains-images-support/" rel="alternate"/><published>2026-04-03T14:00:00+00:00</published><updated>2026-04-09T04:06:33.904234+00:00</updated><author><name>Artur Barseghyan</name></author><id>tag:None,2026-04-03:/gist-blog/posts/2026/04/log4brains-images-support/</id><summary type="html">&lt;p class="first last"&gt;Tired of broken images in your Architecture Decision Records? This quick fix shows you how to add static image support to Log4brains. By setting up a docs/l4b-static folder and adding a simple copy command to your Build script or GitHub Action, you can finally use local icons and diagrams directly in your ADRs. Perfect for keeping your architecture docs visual and professional without the hosting headaches.&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a class="reference external" href="https://github.com/thomvaill/log4brains"&gt;https://github.com/thomvaill/log4brains&lt;/a&gt;&lt;/p&gt;
&lt;div class="section" id="directory-structure"&gt;
&lt;h2&gt;Directory structure&lt;/h2&gt;
&lt;pre class="literal-block"&gt;
docs
├── adr
│   ├── 20240416-architecture-v2.md
│   ├── 20240916-llm-choice.md
│   ├── 20240916-rag-framework-choice.md
│   ├── index.md
│   └── template.md
└── l4b-static
    └── images
        ├── favicon-16x16.png
        └── favicon-32x32.png
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="makefile"&gt;
&lt;h2&gt;Makefile&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Filename: Makefile&lt;/em&gt;&lt;/p&gt;
&lt;pre class="literal-block"&gt;
add-adr:
    docker run --rm -ti -v $$(pwd):/workdir -p 4004:4004 thomvaill/log4brains adr new $(ARGS)

preview:
    docker run --rm -ti -v $$(pwd):/workdir -p 4004:4004 thomvaill/log4brains preview $(ARGS)

build:
    docker run --rm -ti -v $$(pwd):/workdir -p 4004:4004 thomvaill/log4brains build $(ARGS)
    cp -R docs/l4b-static/* .log4brains/out/l4b-static/

# Serve the built docs on port 5002
serve:
    cd .log4brains/out &amp;amp;&amp;amp; python3 -m http.server 5002
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="github-action"&gt;
&lt;h2&gt;GitHub Action&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Filename: .github/workflows/publish-log4brains.yml&lt;/em&gt;&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;Publish Log4brains&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;push&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;main&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p-Indicator"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;pre-commit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;${{ !github.event.pull_request.draft }}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;actions/checkout&amp;#64;v4&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;actions/setup-python&amp;#64;v4&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nt"&gt;python-version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'3.10'&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;pre-commit/action&amp;#64;v3.0.0&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nt"&gt;build-and-publish&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;Checkout&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;actions/checkout&amp;#64;v4&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nt"&gt;persist-credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# required by JamesIves/github-pages-deploy-action&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nt"&gt;fetch-depth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# required by Log4brains to work correctly (needs the whole Git history)&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;Install Node&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;actions/setup-node&amp;#64;v3&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nt"&gt;node-version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;16&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;Install and Build Log4brains&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p-Indicator"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="no"&gt;npm install -g log4brains&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="no"&gt;log4brains build&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="no"&gt;cp -R docs/l4b-static/* .log4brains/out/l4b-static/&lt;/span&gt;&lt;span class="w"&gt;

      &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;Deploy to GitHub Pages&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;peaceiris/actions-gh-pages&amp;#64;v3&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nt"&gt;github_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nt"&gt;publish_dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;.log4brains/out&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="usage-example"&gt;
&lt;h2&gt;Usage example&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Filename: docs/adr/index.md&lt;/em&gt;&lt;/p&gt;
&lt;pre class="code markdown literal-block"&gt;
&lt;span class="gh"&gt;# Architecture knowledge base&lt;/span&gt;
&lt;span class="w"&gt;
&lt;/span&gt;Welcome 👋 to the architecture knowledge base of Project ![&lt;span class="nt"&gt;Project&lt;/span&gt;](&lt;span class="na"&gt;/l4b-static/images/favicon-16x16.png&lt;/span&gt;).&lt;span class="w"&gt;
&lt;/span&gt;You will find here all the Architecture Decision Records (ADR) of the project.
&lt;/pre&gt;
&lt;/div&gt;
&lt;p style="margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #ddd; font-size: 0.9rem; color: #555; text-align: right;"&gt;Originally published as &lt;a href="https://gist.github.com/barseghyanartur/f041a013db9a730d2f2889663d1956f6" target="_blank" rel="noopener"&gt;GitHub Gist #f041a013db9a730d2f2889663d1956f6&lt;/a&gt;&lt;/p&gt;</content><category term="Tech"/><category term="ADRs"/><category term="log4brains"/></entry><entry><title>Make scrollbars always visible in Vivaldi browser using Stylus</title><link href="/gist-blog/posts/2026/04/make-scrollbars-always-visible-in-vivaldi-browser-using-stylus/" rel="alternate"/><published>2026-04-03T01:42:00+00:00</published><updated>2026-04-09T04:06:35.351017+00:00</updated><author><name>Artur Barseghyan</name></author><id>tag:None,2026-04-03:/gist-blog/posts/2026/04/make-scrollbars-always-visible-in-vivaldi-browser-using-stylus/</id><summary type="html">&lt;p class="first last"&gt;Tired of scrollbars playing hide-and-seek? This quick guide shows you how to use the Stylus extension to force scrollbars to stay visible in Vivaldi. With a simple CSS snippet, you can reclaim your navigation bars and even give them a custom look so they never disappear on you again.&lt;/p&gt;</summary><content type="html">&lt;!-- References --&gt;
&lt;div class="section" id="steps"&gt;
&lt;h2&gt;Steps&lt;/h2&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Install &lt;a class="reference external" href="https://chromewebstore.google.com/detail/stylus/clngdbkpkpeebahjckkjfobafhncgmne"&gt;Stylus&lt;/a&gt; Extension:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Go to the Chrome Web Store and search for &lt;a class="reference external" href="https://chromewebstore.google.com/detail/stylus/clngdbkpkpeebahjckkjfobafhncgmne"&gt;Stylus&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Install the &lt;a class="reference external" href="https://chromewebstore.google.com/detail/stylus/clngdbkpkpeebahjckkjfobafhncgmne"&gt;Stylus&lt;/a&gt; extension.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Create a New Style in &lt;a class="reference external" href="https://chromewebstore.google.com/detail/stylus/clngdbkpkpeebahjckkjfobafhncgmne"&gt;Stylus&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Click on the &lt;a class="reference external" href="https://chromewebstore.google.com/detail/stylus/clngdbkpkpeebahjckkjfobafhncgmne"&gt;Stylus&lt;/a&gt; icon in your browser toolbar.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Click on Manage &amp;gt; Write new style.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Add the following CSS code to make scrollbars always visible:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c"&gt;/* Always show vertical scrollbar */&lt;/span&gt;
&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;overflow-y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;scroll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Always show horizontal scrollbar */&lt;/span&gt;
&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;overflow-x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;scroll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Force scrollbar visibility for all elements */&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;-webkit-scrollbar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c"&gt;/* Set the width for vertical scrollbars */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;/* Set the height for horizontal scrollbars */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kp"&gt;-webkit-&lt;/span&gt;&lt;span class="k"&gt;appearance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;-webkit-scrollbar-thumb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;/* Customize the color of the scrollbar thumb */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;/* Customize the radius */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;/* Adjust border if needed */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;-webkit-scrollbar-track&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;/* Customize the background of the scrollbar track */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Save and Apply the Style:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Give your style a name, for example, &lt;cite&gt;Always Show Scrollbars&lt;/cite&gt;.&lt;/li&gt;
&lt;li&gt;Save and enable the style.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;p style="margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #ddd; font-size: 0.9rem; color: #555; text-align: right;"&gt;Originally published as &lt;a href="https://gist.github.com/barseghyanartur/b957e6e87d2f544054f402957d03bf7a" target="_blank" rel="noopener"&gt;GitHub Gist #b957e6e87d2f544054f402957d03bf7a&lt;/a&gt;&lt;/p&gt;</content><category term="Tech"/><category term="vivaldi"/><category term="browsers"/><category term="tweaks"/></entry><entry><title>CudaText installation and config for SublimeText users</title><link href="/gist-blog/posts/2026/04/cudatext-installation-and-config-for-sublimetext-users/" rel="alternate"/><published>2026-04-03T01:08:00+00:00</published><updated>2026-04-09T04:06:34.398527+00:00</updated><author><name>Artur Barseghyan</name></author><id>tag:None,2026-04-03:/gist-blog/posts/2026/04/cudatext-installation-and-config-for-sublimetext-users/</id><summary type="html">&lt;p class="first last"&gt;Switching from Sublime Text to CudaText? This guide gets you up and running on macOS with zero friction. It covers everything from bypassing security warnings and setting up a terminal alias to a curated list of essential plugins like Python IntelliSense and Markdown Preview. Plus, there's a ready-to-go user.json config to keep your UI clean and your indentation consistent—perfect for those who want that familiar, lightning-fast editor feel without the bloat.&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a class="reference external" href="https://github.com/Alexey-T/CudaText"&gt;https://github.com/Alexey-T/CudaText&lt;/a&gt;&lt;/p&gt;
&lt;div class="section" id="macos-releases"&gt;
&lt;h2&gt;macOS releases&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Go for aarch64 releases&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/Alexey-T/CudaText/discussions/5590#discussioncomment-15352982"&gt;https://github.com/Alexey-T/CudaText/discussions/5590#discussioncomment-15352982&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="tweaks"&gt;
&lt;h2&gt;Tweaks&lt;/h2&gt;
&lt;div class="section" id="post-installation"&gt;
&lt;h3&gt;Post-installation&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;xattr&lt;span class="w"&gt; &lt;/span&gt;-cr&lt;span class="w"&gt; &lt;/span&gt;/Applications/CudaText.app
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="zshrc"&gt;
&lt;h3&gt;.zshrc&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;# ----------------------------------------------------------------------------
# CudaText
# ----------------------------------------------------------------------------
alias cudatext=&amp;quot;/Applications/CudaText.app/Contents/MacOS/cudatext&amp;quot;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="plugins-list"&gt;
&lt;h2&gt;Plugins list&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/halfbrained/cudatext_plugins_list"&gt;https://github.com/halfbrained/cudatext_plugins_list&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;CudaExt&lt;/li&gt;
&lt;li&gt;CudaLint&lt;/li&gt;
&lt;li&gt;Differ&lt;/li&gt;
&lt;li&gt;Favorites&lt;/li&gt;
&lt;li&gt;Find in Files 4&lt;/li&gt;
&lt;li&gt;Highlight Occurrences&lt;/li&gt;
&lt;li&gt;Encode&lt;/li&gt;
&lt;li&gt;Spell Checker&lt;/li&gt;
&lt;li&gt;Case Converter&lt;/li&gt;
&lt;li&gt;CSV Helper&lt;/li&gt;
&lt;li&gt;Python IntelliSense&lt;/li&gt;
&lt;li&gt;SQL Tools&lt;/li&gt;
&lt;li&gt;Text Statistics&lt;/li&gt;
&lt;li&gt;Markdown Preview&lt;/li&gt;
&lt;li&gt;reStructuredText Preview&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="user-json"&gt;
&lt;h2&gt;user.json&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;ui_theme&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ebony&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;ui_theme_syntax&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ebony&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;ui_toolbar_show&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;ruler_show&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;minimap_show&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;margin_string&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;80 120&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;minimap_sel_always&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;minimap_sel_border&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;tab_size&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;tab_spaces&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;caret_blink_en&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p style="margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #ddd; font-size: 0.9rem; color: #555; text-align: right;"&gt;Originally published as &lt;a href="https://gist.github.com/barseghyanartur/65bd6a58ce5f2fda89594c2f4fac812a" target="_blank" rel="noopener"&gt;GitHub Gist #65bd6a58ce5f2fda89594c2f4fac812a&lt;/a&gt;&lt;/p&gt;</content><category term="Tech"/><category term="IDE"/><category term="text-editor"/><category term="CudaText"/></entry><entry><title>Useful plugins for VSCode</title><link href="/gist-blog/posts/2026/04/useful-plugins-for-vscode/" rel="alternate"/><published>2026-04-03T01:02:00+00:00</published><updated>2026-04-09T04:06:34.225867+00:00</updated><author><name>Artur Barseghyan</name></author><id>tag:None,2026-04-03:/gist-blog/posts/2026/04/useful-plugins-for-vscode/</id><summary type="html">&lt;p class="first last"&gt;Supercharge your VS Code setup with this curated list of essential plugins! Whether you're a Python pro needing Pylance and Makefile support, or a documentation stickler looking for Markdown and reStructuredText tools, these extensions bridge the gap between a basic editor and a powerhouse IDE. Plus, grab the PyCharm Theme to get that classic JetBrains look without sacrificing VS Code's lightweight speed.&lt;/p&gt;</summary><content type="html">&lt;!-- References: --&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://marketplace.visualstudio.com/items?itemName=Tyriar.sort-lines"&gt;Sort lines&lt;/a&gt;: Sort lines of text for VS Code&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://marketplace.visualstudio.com/items?itemName=nicohlr.pycharm"&gt;PyCharm Theme&lt;/a&gt;: PyCharm Theme for VS Code&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint"&gt;MarkdownLint&lt;/a&gt;: Markdown/CommonMark linting and style checking for VS Code&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.makefile-tools"&gt;Makefile Tools&lt;/a&gt;: Enhancing Makefile development in VS Code&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance"&gt;Pylance&lt;/a&gt;: A performant, feature-rich language server for Python in VS Code&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml"&gt;Even better TOML&lt;/a&gt;: A TOML language support for VS Code&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://marketplace.visualstudio.com/items?itemName=trond-snekvik.simple-rst"&gt;reStructuredText Syntax highlighting&lt;/a&gt;: Syntax highlighting and non-intrusive section navigation for reStructuredText.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #ddd; font-size: 0.9rem; color: #555; text-align: right;"&gt;Originally published as &lt;a href="https://gist.github.com/barseghyanartur/cd525950b1cc849dc79306251db183fe" target="_blank" rel="noopener"&gt;GitHub Gist #cd525950b1cc849dc79306251db183fe&lt;/a&gt;&lt;/p&gt;</content><category term="Tech"/><category term="python"/><category term="vs-code"/><category term="vs-code-plugins"/></entry><entry><title>open-webui setup on macOS</title><link href="/gist-blog/posts/2026/04/open-webui-setup-on-macos/" rel="alternate"/><published>2026-04-03T00:21:00+00:00</published><updated>2026-04-09T04:06:34.058082+00:00</updated><author><name>Artur Barseghyan</name></author><id>tag:None,2026-04-03:/gist-blog/posts/2026/04/open-webui-setup-on-macos/</id><summary type="html">&lt;p class="first last"&gt;Want to run Open WebUI as a seamless background service on your Mac? This guide walks you through setting it up with launchd, so it starts automatically every time you log in. Using uv for a clean install and a simple .plist configuration, you'll get persistent hosting, automatic crash recovery, and easy log management. No more manual terminal commands—just a reliable local AI interface ready whenever you need it.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Here’s how to set up Open WebUI as a launchd service on macOS.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;div class="section" id="prerequisites"&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;First, install Open WebUI via uv if you haven’t already:&lt;/p&gt;
&lt;pre class="code bash literal-block"&gt;
uv&lt;span class="w"&gt; &lt;/span&gt;tool&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;open-webui
&lt;/pre&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;div class="section" id="create-the-plist-file"&gt;
&lt;h2&gt;1. Create the plist file&lt;/h2&gt;
&lt;pre class="code sh literal-block"&gt;
cat&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;~/Library/LaunchAgents/com.user.openwebui.plist&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt; EOF
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;!DOCTYPE plist PUBLIC &amp;quot;-//Apple//DTD PLIST 1.0//EN&amp;quot; &amp;quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;quot;&amp;gt;
&amp;lt;plist version=&amp;quot;1.0&amp;quot;&amp;gt;&amp;lt;dict&amp;gt;
  &amp;lt;key&amp;gt;Label&amp;lt;/key&amp;gt;&amp;lt;string&amp;gt;com.user.openwebui&amp;lt;/string&amp;gt;
  &amp;lt;key&amp;gt;ProgramArguments&amp;lt;/key&amp;gt;&amp;lt;array&amp;gt;
    &amp;lt;string&amp;gt;$HOME/.local/share/uv/tools/open-webui/bin/open-webui&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;serve&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;--port&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;8081&amp;lt;/string&amp;gt;
  &amp;lt;/array&amp;gt;
  &amp;lt;key&amp;gt;WorkingDirectory&amp;lt;/key&amp;gt;&amp;lt;string&amp;gt;$HOME&amp;lt;/string&amp;gt;
  &amp;lt;key&amp;gt;EnvironmentVariables&amp;lt;/key&amp;gt;&amp;lt;dict&amp;gt;
    &amp;lt;key&amp;gt;HOME&amp;lt;/key&amp;gt;&amp;lt;string&amp;gt;$HOME&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;WEBUI_SECRET_KEY_FILE&amp;lt;/key&amp;gt;&amp;lt;string&amp;gt;$HOME/.webui_secret_key&amp;lt;/string&amp;gt;
  &amp;lt;/dict&amp;gt;
  &amp;lt;key&amp;gt;RunAtLoad&amp;lt;/key&amp;gt;&amp;lt;true/&amp;gt;
  &amp;lt;key&amp;gt;KeepAlive&amp;lt;/key&amp;gt;&amp;lt;true/&amp;gt;
  &amp;lt;key&amp;gt;StandardOutPath&amp;lt;/key&amp;gt;&amp;lt;string&amp;gt;$HOME/Library/Logs/openwebui.log&amp;lt;/string&amp;gt;
  &amp;lt;key&amp;gt;StandardErrorPath&amp;lt;/key&amp;gt;&amp;lt;string&amp;gt;$HOME/Library/Logs/openwebui.err&amp;lt;/string&amp;gt;
&amp;lt;/dict&amp;gt;&amp;lt;/plist&amp;gt;
EOF&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Save the plist to &lt;tt class="docutils literal"&gt;~/Library/LaunchAgents/com.user.openwebui.plist&lt;/tt&gt;.
Before doing so, verify the binary path matches your actual
installation:&lt;/p&gt;
&lt;pre class="code bash literal-block"&gt;
which&lt;span class="w"&gt; &lt;/span&gt;open-webui&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;# or
&lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;~/.local/share/uv/tools/open-webui/bin/open-webui
&lt;/pre&gt;
&lt;p&gt;If the path differs, update the &lt;tt class="docutils literal"&gt;ProgramArguments&lt;/tt&gt; string in the plist
accordingly.&lt;/p&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;div class="section" id="set-the-secret-key-optional-but-recommended"&gt;
&lt;h2&gt;2. Set the secret key (optional but recommended)&lt;/h2&gt;
&lt;p&gt;The plist references a secret key file. Create it:&lt;/p&gt;
&lt;pre class="code bash literal-block"&gt;
openssl&lt;span class="w"&gt; &lt;/span&gt;rand&lt;span class="w"&gt; &lt;/span&gt;-hex&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;~/.webui_secret_key&lt;span class="w"&gt;
&lt;/span&gt;chmod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/.webui_secret_key
&lt;/pre&gt;
&lt;p&gt;If you don’t want to use a secret key file, remove the
&lt;tt class="docutils literal"&gt;WEBUI_SECRET_KEY_FILE&lt;/tt&gt; entry from &lt;tt class="docutils literal"&gt;EnvironmentVariables&lt;/tt&gt; in the
plist.&lt;/p&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;div class="section" id="load-the-service"&gt;
&lt;h2&gt;3. Load the service&lt;/h2&gt;
&lt;pre class="code bash literal-block"&gt;
launchctl&lt;span class="w"&gt; &lt;/span&gt;load&lt;span class="w"&gt; &lt;/span&gt;~/Library/LaunchAgents/com.user.openwebui.plist
&lt;/pre&gt;
&lt;p&gt;This registers the service and starts it immediately (because
&lt;tt class="docutils literal"&gt;RunAtLoad&lt;/tt&gt; is &lt;tt class="docutils literal"&gt;true&lt;/tt&gt;). It will also restart automatically on crash
(&lt;tt class="docutils literal"&gt;KeepAlive&lt;/tt&gt; is &lt;tt class="docutils literal"&gt;true&lt;/tt&gt;) and run on every login.&lt;/p&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;div class="section" id="verify-it-s-running"&gt;
&lt;h2&gt;4. Verify it's running&lt;/h2&gt;
&lt;pre class="code bash literal-block"&gt;
&lt;span class="c1"&gt;# Check the process is up
&lt;/span&gt;launchctl&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;openwebui&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c1"&gt;# Tail the logs
&lt;/span&gt;tail&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;~/Library/Logs/openwebui.log&lt;span class="w"&gt;
&lt;/span&gt;tail&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;~/Library/Logs/openwebui.err
&lt;/pre&gt;
&lt;p&gt;Then visit &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;http://localhost:8081&lt;/span&gt;&lt;/tt&gt; in your browser.&lt;/p&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;div class="section" id="common-management-commands"&gt;
&lt;h2&gt;5. Common management commands&lt;/h2&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="33%" /&gt;
&lt;col width="67%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Action&lt;/th&gt;
&lt;th class="head"&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;Stop the service&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;launchctl unload ~/Library/LaunchAgents/com.user.openwebui.plist&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Start it again&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;launchctl load ~/Library/LaunchAgents/com.user.openwebui.plist&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Restart&lt;/td&gt;
&lt;td&gt;unload, then load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Disable autostart&lt;/td&gt;
&lt;td&gt;unload + delete the plist file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;div class="section" id="after-editing-the-plist"&gt;
&lt;h2&gt;6. After editing the plist&lt;/h2&gt;
&lt;p&gt;If you ever modify the plist, you must reload it for changes to take
effect:&lt;/p&gt;
&lt;pre class="code bash literal-block"&gt;
launchctl&lt;span class="w"&gt; &lt;/span&gt;unload&lt;span class="w"&gt; &lt;/span&gt;~/Library/LaunchAgents/com.user.openwebui.plist&lt;span class="w"&gt;
&lt;/span&gt;launchctl&lt;span class="w"&gt; &lt;/span&gt;load&lt;span class="w"&gt; &lt;/span&gt;~/Library/LaunchAgents/com.user.openwebui.plist
&lt;/pre&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;That’s it — after step 3, Open WebUI will start automatically on every
login without any further action needed.&lt;/p&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;div class="section" id="web-app"&gt;
&lt;h2&gt;7. Web-app&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Open your browser and navigate to &lt;a class="reference external" href="http://localhost:8081/"&gt;http://localhost:8081/&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Firefox and Chromium-based browsers (like Chrome or Edge) support
Progressive Web Apps. Click the &lt;tt class="docutils literal"&gt;Install&lt;/tt&gt; icon in the address bar
to run Open WebUI as a standalone application.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;p style="margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #ddd; font-size: 0.9rem; color: #555; text-align: right;"&gt;Originally published as &lt;a href="https://gist.github.com/barseghyanartur/916b8911e29ba9e5ee9b14916834d031" target="_blank" rel="noopener"&gt;GitHub Gist #916b8911e29ba9e5ee9b14916834d031&lt;/a&gt;&lt;/p&gt;</content><category term="Tech"/><category term="python"/><category term="open-webui"/><category term="macos"/></entry><entry><title>Hide *.py,cover files from IDE</title><link href="/gist-blog/posts/2026/04/hide-pycover-files-from-ide/" rel="alternate"/><published>2026-04-02T14:00:00+00:00</published><updated>2026-04-09T04:06:34.565760+00:00</updated><author><name>Artur Barseghyan</name></author><id>tag:None,2026-04-02:/gist-blog/posts/2026/04/hide-pycover-files-from-ide/</id><summary type="html">&lt;p class="first last"&gt;Clean up your workspace by hiding those pesky &lt;tt class="docutils literal"&gt;*.py,cover&lt;/tt&gt; files from your IDE. Whether you're using VS Code or PyCharm, this quick setting change keeps your file tree focused on your actual source code by filtering out coverage reports. Just a few clicks in your editor's &amp;quot;Excluded&amp;quot; or &amp;quot;Ignored&amp;quot; file settings, and those clutter-causing files disappear from view while remaining safe on your disk.&lt;/p&gt;</summary><content type="html">&lt;div class="section" id="vscode"&gt;
&lt;h2&gt;VSCode&lt;/h2&gt;
&lt;p&gt;Settings -&amp;gt; Text Editor -&amp;gt; Files -&amp;gt; Add Pattern: Add &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;**/*.py,cover&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="pycharm"&gt;
&lt;h2&gt;PyCharm&lt;/h2&gt;
&lt;p&gt;Editor -&amp;gt; File Types -&amp;gt; Ignored Files and Folders : Add &lt;tt class="docutils literal"&gt;*.py,cover&lt;/tt&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p style="margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #ddd; font-size: 0.9rem; color: #555; text-align: right;"&gt;Originally published as &lt;a href="https://gist.github.com/barseghyanartur/2e36de0a8b64198ed98e748a0a7b7f15" target="_blank" rel="noopener"&gt;GitHub Gist #2e36de0a8b64198ed98e748a0a7b7f15&lt;/a&gt;&lt;/p&gt;</content><category term="Tech"/><category term="python"/><category term="vs-code"/><category term="pycharm"/></entry><entry><title>Safezip - zero-dependency wrapper for secure ZIP extraction</title><link href="/gist-blog/posts/2026/02/safezip-zero-dependency-wrapper-for-secure-zip-extraction/" rel="alternate"/><published>2026-02-25T13:00:00+00:00</published><updated>2026-04-09T04:06:33.569136+00:00</updated><author><name>Artur Barseghyan</name></author><id>tag:None,2026-02-25:/gist-blog/posts/2026/02/safezip-zero-dependency-wrapper-for-secure-zip-extraction/</id><summary type="html">&lt;img src="https://raw.githubusercontent.com/barseghyanartur/safezip/main/docs/_static/safezip_logo.webp" alt="Preview" class="preview-image" style="max-width:100%; height:auto; border-radius:8px; margin-bottom:1.5rem; display:block;"&gt;&lt;p class="first last"&gt;&lt;cite&gt;Safezip&lt;/cite&gt; is a zero-dependency Python wrapper for zipfile that makes secure extraction the default. It provides essential protections against ZipSlip, ZIP bombs, and inconsistent ZIP64 headers—security features missing from the standard library. With support for atomic writes and environment-based configuration, it is an ideal drop-in solution for safely handling untrusted user uploads in production.&lt;/p&gt;</summary><content type="html">&lt;a class="reference external image-reference" href="https://github.com/barseghyanartur/safezip"&gt;
&lt;img alt="Safezip" src="https://raw.githubusercontent.com/barseghyanartur/safezip/main/docs/_static/safezip_logo.webp" /&gt;
&lt;/a&gt;
&lt;p&gt;I wrote a small, zero-dependency wrapper for secure ZIP extraction.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/barseghyanartur/safezip"&gt;https://github.com/barseghyanartur/safezip&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What My Project Does&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;safezip&lt;/tt&gt; is a zero-dependency wrapper around Python’s &lt;tt class="docutils literal"&gt;zipfile&lt;/tt&gt;
module that makes secure ZIP extraction the default. It protects
against:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;ZipSlip protection&lt;/strong&gt;: Blocks relative paths, absolute paths, Windows
UNC paths, Unicode lookalike attacks, and null bytes in filenames.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ZIP bomb prevention&lt;/strong&gt;: Enforces per-member and cumulative
decompression ratio limits at stream time — not based on untrusted
header values.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ZIP64 consistency checks&lt;/strong&gt;: Crafted archives with inconsistent ZIP64
extra fields are rejected before decompression begins.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Symlink policy&lt;/strong&gt; — configurable: &lt;tt class="docutils literal"&gt;REJECT&lt;/tt&gt; (default), &lt;tt class="docutils literal"&gt;IGNORE&lt;/tt&gt;,
or &lt;tt class="docutils literal"&gt;RESOLVE_INTERNAL&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Atomic writes&lt;/strong&gt;: Extracts to a temp file first and only moves it to
the destination if all checks pass. If something fails, you don’t end
up with half-extracted junk on your disk.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Environment variable overrides&lt;/strong&gt;: All numeric limits can be set via
&lt;tt class="docutils literal"&gt;SAFEZIP_*&lt;/tt&gt; environment variables for containerised deployments.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s meant to be an almost drop-in replacement. You can just do:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;safezip&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;safe_extract&lt;/span&gt;

&lt;span class="n"&gt;safe_extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;path/to/file.zip&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/var/files/extracted/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you need more control, there’s a &lt;tt class="docutils literal"&gt;SafeZipFile&lt;/tt&gt; context manager that
lets you tweak limits or monitor security events.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;safezip&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SafeZipFile&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;SafeZipFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;path/to/file.zip&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;zf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;namelist&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;zf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extractall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/var/files/extracted/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Target Audience&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you’re handling user uploads or processing ZIP files from untrusted
sources, this might save you some headache. It’s production-oriented but
currently in beta, so feedback and edge cases are very welcome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Comparison&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The standard library’s &lt;tt class="docutils literal"&gt;zipfile&lt;/tt&gt; module historically wasn’t safe to
use on untrusted files. Even the official docs warn against
&lt;tt class="docutils literal"&gt;extractall()&lt;/tt&gt; because of ZipSlip risks, and it doesn’t do much to
stop ZIP bombs from eating up your disk or memory. Python 3.12 did
address some of this — &lt;tt class="docutils literal"&gt;extractall()&lt;/tt&gt; now strips path components that
would escape the target directory — but it still leaves meaningful gaps:
no ZIP bomb protection, no stream-time size enforcement, no symlink
policy, no ZIP64 consistency checks, and no atomic writes. &lt;tt class="docutils literal"&gt;safezip&lt;/tt&gt;
fills all of those. I got tired of writing the same boilerplate every
time, so I packaged it up.&lt;/p&gt;
&lt;p&gt;-—&lt;/p&gt;
&lt;p&gt;Documentation: &lt;a class="reference external" href="https://safezip.readthedocs.io/en/latest/"&gt;https://safezip.readthedocs.io/en/latest/&lt;/a&gt;&lt;/p&gt;
&lt;p style="margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #ddd; font-size: 0.9rem; color: #555; text-align: right;"&gt;Originally published as &lt;a href="https://gist.github.com/barseghyanartur/160a1cd9c12448447882f12b16e14d35" target="_blank" rel="noopener"&gt;GitHub Gist #160a1cd9c12448447882f12b16e14d35&lt;/a&gt;&lt;/p&gt;</content><category term="Tech"/><category term="python"/><category term="zip"/><category term="security"/></entry><entry><title>PyData Amsterdam 2025 notes</title><link href="/gist-blog/posts/2025/09/pydata-amsterdam-2025-notes/" rel="alternate"/><published>2025-09-26T22:02:00+00:00</published><updated>2026-04-09T04:06:34.700500+00:00</updated><author><name>Artur Barseghyan</name></author><id>tag:None,2025-09-26:/gist-blog/posts/2025/09/pydata-amsterdam-2025-notes/</id><summary type="html">&lt;p class="first last"&gt;PyData Amsterdam 2025 highlighted the latest advancements in data engineering and AI, focusing on efficiency, performance, and modern development tools. Key trends included automated prompt optimization, zero-copy data sharing, and the rise of next-generation notebook environments. The event also showcased the growing maturity of the LLM ecosystem through improved RAG architectures and lightweight models for faster development.&lt;/p&gt;</summary><content type="html">&lt;div class="section" id="prompts"&gt;
&lt;h2&gt;Prompts&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Use DSpy &lt;a class="reference external" href="https://dspy.ai/"&gt;https://dspy.ai/&lt;/a&gt; for automatic prompt optimisation.&lt;/li&gt;
&lt;li&gt;Microsoft's attempt to fix the prompt management: &lt;a class="reference external" href="https://github.com/microsoft/prompty"&gt;https://github.com/microsoft/prompty&lt;/a&gt;.
Supported by LangChain.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="zero-copy"&gt;
&lt;h2&gt;Zero-copy&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Use DLPack &lt;a class="reference external" href="https://dmlc.github.io/dlpack/latest/"&gt;https://dmlc.github.io/dlpack/latest/&lt;/a&gt; for zero-data-copy of tensor data between between different libs (PyTorch, NumPy, etc.).&lt;/li&gt;
&lt;li&gt;Use Apache Arrow &lt;a class="reference external" href="https://arrow.apache.org/"&gt;https://arrow.apache.org/&lt;/a&gt; for zero-data-copy of tabular data between different libs (Pandas, Polars, DuckDB, etc.).&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="end-to-end-gpu-processing"&gt;
&lt;h2&gt;End-to-end GPU processing&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;If you're using NVIDIA GPUs, use cuDF-cu12 &lt;a class="reference external" href="https://docs.rapids.ai/api/cudf/stable/"&gt;https://docs.rapids.ai/api/cudf/stable/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="vector-dbs"&gt;
&lt;h2&gt;Vector DBs&lt;/h2&gt;
&lt;p&gt;Qdrant &lt;a class="reference external" href="https://github.com/qdrant/qdrant"&gt;https://github.com/qdrant/qdrant&lt;/a&gt; is generally loved by many as a vector DB.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="face-recognition"&gt;
&lt;h2&gt;Face-recognition&lt;/h2&gt;
&lt;p&gt;ArcFace &lt;a class="reference external" href="https://github.com/deepinsight/insightface"&gt;https://github.com/deepinsight/insightface&lt;/a&gt; and SigLip2 &lt;a class="reference external" href="https://huggingface.co/blog/siglip2"&gt;https://huggingface.co/blog/siglip2&lt;/a&gt; are your best friends here.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="notebooks"&gt;
&lt;h2&gt;Notebooks&lt;/h2&gt;
&lt;p&gt;Marimo is emerging as a Jupyter notebook replacement.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="llms"&gt;
&lt;h2&gt;LLMs&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Some suggest to use universal LLM proxies for corporate use. LiteLLM &lt;a class="reference external" href="https://docs.litellm.ai/"&gt;https://docs.litellm.ai/&lt;/a&gt; is one of such. Same folks suggest using Open WebUI as a generally available tool for corporate use.&lt;/li&gt;
&lt;li&gt;MCP gets tremendous amount of attention.&lt;/li&gt;
&lt;li&gt;Proper (optimal) implementation of RAGs get a lot of attention too. Make sure you're familiar with pre, mid, and post optimisations.&lt;/li&gt;
&lt;li&gt;Agents get tremendous amount of attention. New frameworks like Dapr &lt;a class="reference external" href="https://github.com/dapr/dapr"&gt;https://github.com/dapr/dapr&lt;/a&gt; emerge.&lt;/li&gt;
&lt;li&gt;Dutch local governance is actively integrating LLMs into their processes.&lt;/li&gt;
&lt;li&gt;Some suggest to use outlines &lt;a class="reference external" href="https://github.com/dottxt-ai/outlines"&gt;https://github.com/dottxt-ai/outlines&lt;/a&gt; for structured output.&lt;/li&gt;
&lt;li&gt;Some suggest SmolLM &lt;a class="reference external" href="https://github.com/huggingface/smollm"&gt;https://github.com/huggingface/smollm&lt;/a&gt; or &lt;a class="reference external" href="https://ollama.com/library/smollm"&gt;https://ollama.com/library/smollm&lt;/a&gt; for development/testing, as it's extremely small and fast. There's SmolVLM too, if you need a multimodal version.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="engineering"&gt;
&lt;h2&gt;Engineering&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Some suggest to use DuckDB &lt;a class="reference external" href="https://duckdb.org/why_duckdb.html"&gt;https://duckdb.org/why_duckdb.html&lt;/a&gt; for querying extremely large datasets.&lt;/li&gt;
&lt;li&gt;Docling is generally loved by many for format conversion. Check docling-langchain &lt;a class="reference external" href="https://github.com/docling-project/docling-langchain/"&gt;https://github.com/docling-project/docling-langchain/&lt;/a&gt; for better LangChain integration.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p style="margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #ddd; font-size: 0.9rem; color: #555; text-align: right;"&gt;Originally published as &lt;a href="https://gist.github.com/barseghyanartur/2d4cdb080895d58846427c5e8eec5331" target="_blank" rel="noopener"&gt;GitHub Gist #2d4cdb080895d58846427c5e8eec5331&lt;/a&gt;&lt;/p&gt;</content><category term="Tech"/><category term="python"/><category term="pydata-amsterdam"/></entry><entry><title>Why is Vivaldi browser a great companion?</title><link href="/gist-blog/posts/2025/03/why-is-vivaldi-browser-a-great-companion/" rel="alternate"/><published>2025-03-12T14:03:00+00:00</published><updated>2026-04-09T04:06:35.197545+00:00</updated><author><name>Artur Barseghyan</name></author><id>tag:None,2025-03-12:/gist-blog/posts/2025/03/why-is-vivaldi-browser-a-great-companion/</id><summary type="html">&lt;p class="first last"&gt;While most browsers treat websites as simple tabs, Vivaldi wins by letting you turn any site—even those without official support—into a standalone Progressive Web App (PWA). This guide explores how a single right-click can transform utility sites like Google Translate or JSON tools into clean, independent windows that live on your dock or taskbar, helping you declutter your browser and turn the web into a personalized app library.&lt;/p&gt;</summary><content type="html">&lt;!-- References: --&gt;
&lt;p&gt;TL;DR&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://vivaldi.com/"&gt;Vivaldi&lt;/a&gt; browser has 1 killing feature, that no other browser has
implemented. It can turn any website into a progressive web app.&lt;/p&gt;
&lt;p&gt;Ever wanted to have &lt;a class="reference external" href="https://translate.google.com/"&gt;Google Translate&lt;/a&gt; or &lt;a class="reference external" href="https://translate.google.com/"&gt;GMail&lt;/a&gt; as a web app?
No problem! From this point, you could jump directly to &lt;a class="reference internal" href="#how-to"&gt;How to&lt;/a&gt; section.&lt;/p&gt;
&lt;div class="section" id="background"&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;I have many browsers installed on the systems I use. I use most of them simultaneously.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://mozilla.org/firefox/"&gt;Firefox&lt;/a&gt; for strictly work related private web sites (such as company portal, knowledge base) only. With a minimal set of trusted plugins.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://brave.com/"&gt;Brave&lt;/a&gt; as a primary browser for personal use and main browser for everything else, which isn't company private. Brave's out of the box security is great.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://microsoft.com/edge/"&gt;Edge&lt;/a&gt; as a secondary browser next to &lt;a class="reference external" href="https://brave.com/"&gt;Brave&lt;/a&gt;. I use it primarily for Microsoft CoPilot and ChatGPT.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://google.com/chrome/"&gt;Google Chrome&lt;/a&gt; for installing progressive web apps (such as &lt;a class="reference external" href="https://whatsapp.com/"&gt;WhatsApp&lt;/a&gt;, &lt;a class="reference external" href="https://meet.google.com/"&gt;Google Meet&lt;/a&gt; or &lt;a class="reference external" href="https://regex101.com/"&gt;RegEx101&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://opera.com/"&gt;Opera&lt;/a&gt; for anonymous browsing. I never log into sites using &lt;a class="reference external" href="https://opera.com/"&gt;Opera&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://chromium.org/"&gt;Chromium&lt;/a&gt; for automated frontend testing. Occasionally.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a class="reference external" href="https://vivaldi.com/"&gt;Vivaldi&lt;/a&gt; joined the group recently and immediately climbed to same shelf as
&lt;a class="reference external" href="https://google.com/chrome/"&gt;Google Chrome&lt;/a&gt;. I primarily use &lt;a class="reference external" href="https://vivaldi.com/"&gt;Vivaldi&lt;/a&gt; for web apps that aren't yet
progressive (such as &lt;a class="reference external" href="https://jsonformatter.org/"&gt;JSONFormatter&lt;/a&gt;, &lt;a class="reference external" href="https://jsondiff.com/"&gt;JSONDiff&lt;/a&gt; or &lt;a class="reference external" href="https://translate.google.com/"&gt;Google Translate&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="why-is-vivaldi-great"&gt;
&lt;h2&gt;Why is Vivaldi great?&lt;/h2&gt;
&lt;div class="section" id="my-use-case"&gt;
&lt;h3&gt;My use case&lt;/h3&gt;
&lt;p&gt;I use many browsers. In these browsers I have many tabs. I like it that way.
There are many things I want to come back to later on. Some things I check
occasionally. I don't like to use favourites.&lt;/p&gt;
&lt;p&gt;I use &lt;a class="reference external" href="https://jsonformatter.org/"&gt;JSONFormatter&lt;/a&gt; a lot. &lt;a class="reference external" href="https://jsondiff.com/"&gt;JSONDiff&lt;/a&gt; too. &lt;a class="reference external" href="https://translate.google.com/"&gt;Google Translate&lt;/a&gt; helps me from time to time.
I simply want to switch to them quickly. I don't want to have it in a browser.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="how-to"&gt;
&lt;h2&gt;How to&lt;/h2&gt;
&lt;p&gt;It's ridiculously simple — no extensions, no extra setup, and it works for &lt;em&gt;any&lt;/em&gt; website, even the ones that aren't proper PWAs yet.&lt;/p&gt;
&lt;p&gt;Here's exactly what I do:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Open Vivaldi and navigate to the site you want (e.g. &lt;a class="reference external" href="https://jsonformatter.org/"&gt;JSONFormatter&lt;/a&gt;, &lt;a class="reference external" href="https://jsondiff.com/"&gt;JSONDiff&lt;/a&gt;, &lt;a class="reference external" href="https://translate.google.com/"&gt;Google Translate&lt;/a&gt;, whatever).&lt;/li&gt;
&lt;li&gt;Right-click the tab on the tab bar.&lt;/li&gt;
&lt;li&gt;From the context menu choose &lt;strong&gt;Install [website name]&lt;/strong&gt; (if the site supports full PWA) or &lt;strong&gt;Create a shortcut&lt;/strong&gt; (for everything else — this is the killer part).&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Install&lt;/strong&gt; in the tiny confirmation popup.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Done.&lt;/p&gt;
&lt;p&gt;The site now lives as its own standalone window, just like &lt;a class="reference external" href="https://whatsapp.com/"&gt;WhatsApp&lt;/a&gt; or &lt;a class="reference external" href="https://meet.google.com/"&gt;Google Meet&lt;/a&gt; do in Chrome. No browser chrome, no stray tabs, no digging through bookmarks or favourites. You can pin it to your taskbar/start menu/dock, launch it with one click, and it feels exactly like a real app.&lt;/p&gt;
&lt;p&gt;That's it. That single right-click is why Vivaldi instantly earned the same spot in my workflow as Chrome. Every utility site I use regularly now has its own clean little window, and I can keep my main browsers for actual browsing instead of permanent tab graveyards.&lt;/p&gt;
&lt;/div&gt;
&lt;p style="margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #ddd; font-size: 0.9rem; color: #555; text-align: right;"&gt;Originally published as &lt;a href="https://gist.github.com/barseghyanartur/7fc4de46270ba1ced2f46dc654c21add" target="_blank" rel="noopener"&gt;GitHub Gist #7fc4de46270ba1ced2f46dc654c21add&lt;/a&gt;&lt;/p&gt;</content><category term="Tech"/><category term="vivaldi"/><category term="browsers"/><category term="progressive-web-apps"/></entry><entry><title>How to uninstall Progressive Web App installed with Vivaldi</title><link href="/gist-blog/posts/2024/10/how-to-uninstall-progressive-web-app-installed-with-vivaldi/" rel="alternate"/><published>2024-10-30T19:00:00+00:00</published><updated>2026-04-09T04:06:35.035655+00:00</updated><author><name>Artur Barseghyan</name></author><id>tag:None,2024-10-30:/gist-blog/posts/2024/10/how-to-uninstall-progressive-web-app-installed-with-vivaldi/</id><summary type="html">&lt;img src="https://github.com/barseghyanartur/gist-blog-images/blob/main/images/vivaldi.webp?raw=true" alt="Preview" class="preview-image" style="max-width:100%; height:auto; border-radius:8px; margin-bottom:1.5rem; display:block;"&gt;&lt;p class="first last"&gt;How to uninstall Progressive Web App installed with Vivaldi&lt;/p&gt;</summary><content type="html">&lt;ol class="arabic simple"&gt;
&lt;li&gt;Open the progresssive-web-app/shortcut.&lt;/li&gt;
&lt;li&gt;Navigate to the top-right menu, expand the menu and click on &amp;quot;Uninstall {app-name}&amp;quot;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #ddd; font-size: 0.9rem; color: #555; text-align: right;"&gt;Originally published as &lt;a href="https://gist.github.com/barseghyanartur/b48103b4e8091150654373280e09ac90" target="_blank" rel="noopener"&gt;GitHub Gist #b48103b4e8091150654373280e09ac90&lt;/a&gt;&lt;/p&gt;</content><category term="Tech"/><category term="vivaldi"/></entry><entry><title>How to debug Python applications</title><link href="/gist-blog/posts/2022/06/how-to-debug-python-applications/" rel="alternate"/><published>2022-06-21T11:00:00+00:00</published><updated>2026-04-09T04:06:33.740059+00:00</updated><author><name>Artur Barseghyan</name></author><id>tag:None,2022-06-21:/gist-blog/posts/2022/06/how-to-debug-python-applications/</id><summary type="html">&lt;img src="https://i.postimg.cc/rp4SskQv/rp-L4mz-N2-ZJ0-Pnopju2da-1-kguub.jpg" alt="Preview" class="preview-image" style="max-width:100%; height:auto; border-radius:8px; margin-bottom:1.5rem; display:block;"&gt;&lt;p class="first last"&gt;In software development, testing is an essential part of ensuring that code works as intended. One critical aspect of testing is debugging, which involves finding and fixing errors or bugs in a program. In this article, we’ll explore how to debug Python applications and highlight some of the most commonly used methods for debugging Python code.&lt;/p&gt;</summary><content type="html">&lt;a class="reference external image-reference" href="https://postimg.cc/XZVZ1TM8"&gt;
&lt;img alt="How to debug Python applications" src="https://i.postimg.cc/rp4SskQv/rp-L4mz-N2-ZJ0-Pnopju2da-1-kguub.jpg" /&gt;
&lt;/a&gt;
&lt;div class="section" id="introduction"&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;In software development, testing is an essential part of ensuring that
code works as intended. One critical aspect of testing is debugging,
which involves finding and fixing errors or bugs in a program. In this
article, we’ll explore how to debug Python applications and highlight
some of the most commonly used methods for debugging Python code.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="debugging-options"&gt;
&lt;h2&gt;Debugging options&lt;/h2&gt;
&lt;p&gt;When it comes to debugging Python, there are multiple options available,
and you should consider which one suits your needs best. Two popular
options are IDE debugging tools and package debugging tools.&lt;/p&gt;
&lt;div class="section" id="ide-debugging-tools"&gt;
&lt;h3&gt;IDE Debugging Tools&lt;/h3&gt;
&lt;p&gt;IDEs like PyCharm and VSCode offer debugging tools that allow you to set
breakpoints in your code and run it in debug mode. This allows you to
step through your code line by line, inspect variables, and evaluate
expressions. Here are some resources for learning how to use the
debugging tools in PyCharm and VSCode:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.jetbrains.com/help/pycharm/debugging-your-first-python-application.html"&gt;Debugging Python in
PyCharm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://code.visualstudio.com/docs/python/debugging"&gt;Debugging Python in
VSCode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="package-debugging-tools"&gt;
&lt;h3&gt;Package Debugging Tools&lt;/h3&gt;
&lt;p&gt;Links:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://docs.python.org/3/library/pdb.html"&gt;pdb&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://pypi.org/project/ipdb/"&gt;ipdb&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://ipython.readthedocs.io/en/stable/interactive/tutorial.html"&gt;IPython&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Python also offers built-in debugging tools, such as the &lt;tt class="docutils literal"&gt;pdb&lt;/tt&gt; module,
which allow you to set breakpoints and step through your code in a
console-based debugger. Additionally, there are alternative packages
like &lt;tt class="docutils literal"&gt;ipdb&lt;/tt&gt;, which is based on the &lt;tt class="docutils literal"&gt;IPython&lt;/tt&gt; tool and provides a
more powerful debugger. Here is an example of how to use the &lt;tt class="docutils literal"&gt;pdb&lt;/tt&gt;
module in your code:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;my_module.py&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;something&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot; world&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pdb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;pdb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_trace&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# set a breakpoint&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="c1"&gt;# If you want a more powerful debugger, use `ipdb`.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="c1"&gt;# Note, that this requires installation of `ipdb`&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="c1"&gt;#     $ pip install ipdb&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="c1"&gt;# Then, comment out the `mport pdb; ...` and&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="c1"&gt;# uncomment the following line:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="c1"&gt;# import ipdb; ipdb.set_trace()&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;my_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Hello&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;something&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_str&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;In the example code above, we set a breakpoint in the &lt;tt class="docutils literal"&gt;something()&lt;/tt&gt;
function using the &lt;tt class="docutils literal"&gt;pdb.set_trace()&lt;/tt&gt; function. When the code reaches
this point, it will pause execution and drop into the debugger, allowing
you to inspect variables and step through the code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Run it&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="code sh literal-block"&gt;
python&lt;span class="w"&gt; &lt;/span&gt;my_module.py
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;What would you see&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="code sh literal-block"&gt;
$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;my_module.py&lt;span class="w"&gt;
&lt;/span&gt;--Return--&lt;span class="w"&gt;
&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/home/artur.local/repos/tryouts/debug/my_module.py&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;something&lt;span class="o"&gt;()&lt;/span&gt;-&amp;gt;None&lt;span class="w"&gt;
&lt;/span&gt;-&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;import&lt;span class="w"&gt; &lt;/span&gt;pdb&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pdb.set_trace&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# set a breakpoint
&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Pdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;locals&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'val'&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Hello world'&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'pdb'&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;module&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'pdb'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/usr/lib64/python3.11/pdb.py'&lt;/span&gt;&amp;gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'__return__'&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;None&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Pdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;val&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="s1"&gt;'Hello world'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Pdb&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Study &lt;tt class="docutils literal"&gt;pdb&lt;/tt&gt; documentation for more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Good to know&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It works similarly with your web views (like, FastAPI/Flask/Django).&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;your_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;   &lt;span class="c1"&gt;# ...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;   &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pdb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;pdb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_trace&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="best-practices-for-debugging-python"&gt;
&lt;h2&gt;Best Practices for Debugging Python&lt;/h2&gt;
&lt;p&gt;While debugging is an essential part of the development process, there
are some best practices to keep in mind to ensure that your code remains
clean and maintainable.&lt;/p&gt;
&lt;div class="section" id="dont-commit-your-debug-code"&gt;
&lt;h3&gt;Don’t commit your debug code!&lt;/h3&gt;
&lt;p&gt;Links:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://pre-commit.com/"&gt;precommit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/charliermarsh/ruff"&gt;ruff&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One critical practice is to avoid committing your debug code to your
code repository. Debug code can clutter your codebase and make it more
challenging to maintain. To avoid committing debug code, use a tool like
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pre-commit&lt;/span&gt;&lt;/tt&gt; and linters like &lt;tt class="docutils literal"&gt;ruff&lt;/tt&gt; to catch and prevent it from
being committed.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="debugging-in-docker"&gt;
&lt;h3&gt;Debugging in Docker&lt;/h3&gt;
&lt;p&gt;If you use Docker for development, you need to configure your
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;docker-compose.yml&lt;/span&gt;&lt;/tt&gt; to allow debugging. You can do this by setting
the &lt;tt class="docutils literal"&gt;stdin_open&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;tty&lt;/tt&gt; options for your service:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'3'&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c1"&gt;# Other configuration&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;stdin_open&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;tty&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;After configuring your Docker environment, assuming that you have it
running already, you can find the container ID (&lt;tt class="docutils literal"&gt;CONTAINER ID&lt;/tt&gt;) for
your service (in the example above - &lt;tt class="docutils literal"&gt;api&lt;/tt&gt; service) using
&lt;tt class="docutils literal"&gt;docker ps&lt;/tt&gt; and attach to it with the &lt;tt class="docutils literal"&gt;docker attach&lt;/tt&gt;
(&lt;tt class="docutils literal"&gt;docker attach {CONTAINER ID}&lt;/tt&gt;) command to start debugging. Note,
that you will need to run the &lt;tt class="docutils literal"&gt;docker attach&lt;/tt&gt; command in a separate
shell/terminal tab and that’s where the debug prompt will appear.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Debugging Python is an essential skill for any developer, and it’s
crucial to understand the available options and best practices. In this
article, we explored two popular options for debugging Python: IDE
debugging tools and package debugging tools. We also highlighted some
best practices for debugging in Python, including avoiding committing
debug code and configuring Docker for debugging. With this knowledge,
you’ll be better equipped to debug Python applications and write clean,
maintainable code.&lt;/p&gt;
&lt;/div&gt;
&lt;p style="margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #ddd; font-size: 0.9rem; color: #555; text-align: right;"&gt;Originally published as &lt;a href="https://gist.github.com/barseghyanartur/27c2f93e772330a4798fa7269a5ddb9b" target="_blank" rel="noopener"&gt;GitHub Gist #27c2f93e772330a4798fa7269a5ddb9b&lt;/a&gt;&lt;/p&gt;</content><category term="Tech"/><category term="python"/><category term="debugging"/></entry></feed>