<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jhoel Peralta</title>
    <description>The latest articles on DEV Community by Jhoel Peralta (@jhoelp).</description>
    <link>https://dev.to/jhoelp</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3926027%2Fceeda3de-b7a1-4fef-a5c5-40f7844e8d22.png</url>
      <title>DEV Community: Jhoel Peralta</title>
      <link>https://dev.to/jhoelp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jhoelp"/>
    <language>en</language>
    <item>
      <title>"When `_method` isn't enough: enforcing access control in Python at runtime"</title>
      <dc:creator>Jhoel Peralta</dc:creator>
      <pubDate>Tue, 12 May 2026 01:10:42 +0000</pubDate>
      <link>https://dev.to/jhoelp/when-method-isnt-enough-enforcing-access-control-in-python-at-runtime-52ao</link>
      <guid>https://dev.to/jhoelp/when-method-isnt-enough-enforcing-access-control-in-python-at-runtime-52ao</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR.&lt;/strong&gt; Python's underscore prefix is documentation, not enforcement.&lt;br&gt;
In small codebases that's fine. In codebases shared across teams, the&lt;br&gt;
convention drifts — and reviewers spend cycles pointing it out instead&lt;br&gt;
of catching real bugs. I shipped a 1.0 of &lt;a href="https://pypi.org/project/strictaccess/" rel="noopener noreferrer"&gt;strictaccess&lt;/a&gt;,&lt;br&gt;
a small library that turns the convention into a runtime contract with&lt;br&gt;
&lt;code&gt;@private&lt;/code&gt; / &lt;code&gt;@protected&lt;/code&gt; / &lt;code&gt;@public&lt;/code&gt; decorators. It also has an explicit&lt;br&gt;
"Limitations" page declaring it is &lt;strong&gt;not&lt;/strong&gt; a security boundary.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;You've seen this in every Python codebase past 20 contributors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gateway&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt;      &lt;span class="c1"&gt;# "protected by convention"
&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;       &lt;span class="c1"&gt;# "internal helper, don't call"
&lt;/span&gt;        &lt;span class="bp"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three months later, someone writes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Quick fix: bypass validation, the manager said it's urgent.
&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;tip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works. The underscore is just a hint. There is &lt;strong&gt;no enforcement&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In code review the lead spots it and asks for a refactor. In another PR the&lt;br&gt;
same pattern slips through. In a third, the &lt;code&gt;_charge&lt;/code&gt; signature changes and&lt;br&gt;
two external callers break in production. Nobody knew they existed.&lt;/p&gt;

&lt;p&gt;This is the cost of a convention that has no teeth.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why the obvious fixes don't fully work
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. "Just rely on &lt;code&gt;__name&lt;/code&gt; mangling."&lt;/strong&gt; Python mangles &lt;code&gt;__x&lt;/code&gt; to &lt;code&gt;_ClassName__x&lt;/code&gt;&lt;br&gt;
so external code can't accidentally collide. But it's also not enforcement —&lt;br&gt;
&lt;code&gt;obj._ClassName__x&lt;/code&gt; is one line of code away. Tooling-wise, IDEs gray it out&lt;br&gt;
and that's it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. "Write unit tests for access boundaries."&lt;/strong&gt; Tests fail after the fact.&lt;br&gt;
You wanted the call to never happen in the first place. Tests also can't&lt;br&gt;
cover every call site at every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. "Use a linter rule."&lt;/strong&gt; Linters can catch direct access from outside the&lt;br&gt;
class. They can't reason about runtime call chains (subclasses, callbacks,&lt;br&gt;
factory functions, async handlers). False positives are common; false&lt;br&gt;
negatives are worse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. "Use &lt;code&gt;@property&lt;/code&gt; everywhere."&lt;/strong&gt; Heavy. Now every field needs a getter&lt;br&gt;
and a setter, and you've introduced a different problem: subclass overrides&lt;br&gt;
break, and &lt;code&gt;repr&lt;/code&gt; doesn't show internal state.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I wanted
&lt;/h2&gt;

&lt;p&gt;The semantics of Java/C++ &lt;code&gt;private&lt;/code&gt;/&lt;code&gt;protected&lt;/code&gt;/&lt;code&gt;public&lt;/code&gt;, but as decorators,&lt;br&gt;
preserved type information, no breaking changes to existing classes, and&lt;br&gt;
honest about what it can and cannot guarantee.&lt;/p&gt;
&lt;h2&gt;
  
  
  strictaccess
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;strictaccess
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strictaccess&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;strict_access_control&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;private&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;protected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PrivateAccessError&lt;/span&gt;

&lt;span class="nd"&gt;@strict_access_control&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gateway&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt;          &lt;span class="c1"&gt;# underscore -&amp;gt; protected by convention
&lt;/span&gt;
    &lt;span class="nd"&gt;@private&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# ... validation, logging, atomic update ...
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@protected&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_build_audit_record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;...}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;# OK: internal call
&lt;/span&gt;

&lt;span class="n"&gt;svc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                       &lt;span class="c1"&gt;# OK
&lt;/span&gt;&lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;tip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           &lt;span class="c1"&gt;# raises PrivateAccessError
&lt;/span&gt;&lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gateway&lt;/span&gt;                             &lt;span class="c1"&gt;# raises ProtectedAccessError
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;@private&lt;/code&gt; allows calls from inside the defining class only. &lt;code&gt;@protected&lt;/code&gt;&lt;br&gt;
allows the class and its subclasses. Attributes whose names start with &lt;code&gt;_&lt;/code&gt;&lt;br&gt;
are protected automatically; name-mangled &lt;code&gt;__x&lt;/code&gt; is private automatically.&lt;/p&gt;

&lt;p&gt;The decorator preserves the decorated class's type, so &lt;code&gt;mypy --strict&lt;/code&gt;&lt;br&gt;
keeps working on consumer code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;acct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PaymentService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;reveal_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;               &lt;span class="c1"&gt;# PaymentService, not Any
&lt;/span&gt;&lt;span class="nf"&gt;reveal_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;    &lt;span class="c1"&gt;# the actual return type of `process`
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Debug mode for legacy migrations
&lt;/h2&gt;

&lt;p&gt;The hardest part of adopting any enforcement library is not the new code —&lt;br&gt;
it's the existing call sites. &lt;code&gt;strictaccess&lt;/code&gt; ships a debug mode that logs&lt;br&gt;
violations as &lt;code&gt;WARNING&lt;/code&gt; instead of raising:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@strict_access_control&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# Existing callers keep working; you collect a violation log.
# Fix them gradually, then flip debug=False.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It emits via &lt;code&gt;logging.getLogger("strictaccess")&lt;/code&gt; so you can route it to a&lt;br&gt;
file, a SIEM, or just &lt;code&gt;caplog&lt;/code&gt; in pytest.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest part: what it can't do
&lt;/h2&gt;

&lt;p&gt;This is the section every library should have and most don't. &lt;strong&gt;&lt;code&gt;strictaccess&lt;/code&gt;&lt;br&gt;
is a discipline tool, not a security boundary.&lt;/strong&gt; Specifically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;object.__getattribute__(obj, name)&lt;/code&gt; bypasses every check in one line.&lt;/strong&gt;
This is inherent to anything built on &lt;code&gt;__getattribute__&lt;/code&gt;. If you need true
isolation, you need a different process, a different language, or a
different design.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pickling a decorated instance can fail.&lt;/strong&gt; The wrapper class is created
dynamically via &lt;code&gt;type(...)&lt;/code&gt;. &lt;code&gt;copy.deepcopy&lt;/code&gt; works; &lt;code&gt;pickle.dumps&lt;/code&gt; may not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The runtime cost is ~20× a plain attribute access&lt;/strong&gt;, because every
read goes through a Python-level &lt;code&gt;__getattribute__&lt;/code&gt;. In absolute terms
that's roughly 700ns per access — fine for business logic, not fine for
a 10M-iteration hot loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requires Python 3.11+&lt;/strong&gt; because the caller-detection engine reads
&lt;code&gt;frame.f_code.co_qualname&lt;/code&gt;, which was introduced in CPython 3.11.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are documented on a &lt;a href="https://jhoelperaltap.github.io/strictaccess/limitations/" rel="noopener noreferrer"&gt;dedicated Limitations page&lt;/a&gt;,&lt;br&gt;
not buried in a README footnote.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Codebases shared across teams where the underscore convention has drifted.&lt;/li&gt;
&lt;li&gt;Library boundaries — mark internals so consumers can't accidentally build
on them and complain when you change them.&lt;/li&gt;
&lt;li&gt;Legacy refactors — turn on debug mode, collect the violation log, fix the
call sites, then flip to strict.&lt;/li&gt;
&lt;li&gt;Teaching environments where reinforcing OOP semantics in Python helps
students coming from Java/C++.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When not to use it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Performance-critical inner loops on decorated objects.&lt;/li&gt;
&lt;li&gt;Code you ship to untrusted environments expecting strict isolation.&lt;/li&gt;
&lt;li&gt;A solo project where you trust yourself with the convention.&lt;/li&gt;
&lt;li&gt;Codebases stuck on Python 3.10 or older.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;strictaccess
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docs:&lt;/strong&gt; &lt;a href="https://jhoelperaltap.github.io/strictaccess/" rel="noopener noreferrer"&gt;https://jhoelperaltap.github.io/strictaccess/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitations:&lt;/strong&gt; &lt;a href="https://jhoelperaltap.github.io/strictaccess/limitations/" rel="noopener noreferrer"&gt;https://jhoelperaltap.github.io/strictaccess/limitations/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repo (issues / PRs welcome):&lt;/strong&gt; &lt;a href="https://github.com/Jhoelperaltap/strictaccess" rel="noopener noreferrer"&gt;https://github.com/Jhoelperaltap/strictaccess&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feedback I'd value most:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Legitimate use cases I haven't documented.&lt;/li&gt;
&lt;li&gt;Design decisions you'd argue against (the API surface is small on
purpose, but the trade-offs are still up for discussion).&lt;/li&gt;
&lt;li&gt;Real-world adoption stories — even "we tried it and went back to the
convention because X" is gold.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you've worked on a codebase where the underscore convention failed in a&lt;br&gt;
memorable way, I want to hear that story.&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>oop</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
