CINXE.COM
sentinel · PyPI
<!DOCTYPE html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="defaultLanguage" content="en"> <meta name="availableLanguages" content="en, es, fr, ja, pt_BR, uk, el, de, zh_Hans, zh_Hant, ru, he, eo, ko"> <title>sentinel · PyPI</title> <meta name="description" content="Create sentinel objects, akin to None, NotImplemented, Ellipsis"> <link rel="stylesheet" href="/static/css/warehouse-ltr.4afe342a.css"> <link rel="stylesheet" href="/static/css/fontawesome.080e1050.css"> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+3:400,400italic,600,600italic,700,700italic%7CSource+Code+Pro:500"> <noscript> <link rel="stylesheet" href="/static/css/noscript.0673c9ea.css"> </noscript> <link rel="icon" href="/static/images/favicon.35549fe8.ico" type="image/x-icon"> <link rel="alternate" type="application/rss+xml" title="RSS: 40 latest updates" href="/rss/updates.xml"> <link rel="alternate" type="application/rss+xml" title="RSS: 40 newest packages" href="/rss/packages.xml"> <link rel="alternate" type="application/rss+xml" title="RSS: latest releases for sentinel" href="/rss/project/sentinel/releases.xml"> <link rel="canonical" href="https://pypi.org/project/sentinel/"> <meta property="og:url" content="https://pypi.org/project/sentinel/"> <meta property="og:site_name" content="PyPI"> <meta property="og:type" content="website"> <meta property="og:image" content="https://pypi.org/static/images/twitter.abaf4b19.webp"> <meta property="og:title" content="sentinel"> <meta property="og:description" content="Create sentinel objects, akin to None, NotImplemented, Ellipsis"> <link rel="search" type="application/opensearchdescription+xml" title="PyPI" href="/opensearch.xml"> <script async data-ga-id="UA-55961911-1" data-ga4-id="G-RW7D75DF8V" src="/static/js/warehouse.33c92630.js"> </script> <script> MathJax = { tex: { inlineMath: [['$', '$'], ['\\(', '\\)']] }, }; </script> <script async src="https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/tex-svg.js" integrity="sha256-1CldwzdEg2k1wTmf7s5RWVd7NMXI/7nxxjJM2C4DqII=" crossorigin="anonymous" ></script> <script async src="https://www.googletagmanager.com/gtag/js?id=UA-55961911-1"></script> <script async src="https://www.googletagmanager.com/gtag/js?id=G-RW7D75DF8V"></script> <script defer src="https://www.fastly-insights.com/insights.js?k=6a52360a-f306-421e-8ed5-7417d0d4a4e9&dnt=true"></script> <script async src="https://media.ethicalads.io/media/client/v1.4.0/ethicalads.min.js" integrity="sha256-U3hKDidudIaxBDEzwGJApJgPEf2mWk6cfMWghrAa6i0= sha384-UcmsCqcNRSLW/dV3Lo1oCi2/VaurXbib6p4HyUEOeIa/4OpsrnucrugAefzVZJfI sha512-q4t1L4xEjGV2R4hzqCa41P8jrgFUS8xTb8rdNv4FGvw7FpydVj/kkxBJHOiaoxHa8olCcx1Slk9K+3sNbsM4ug==" crossorigin="anonymous" ></script> </head> <body data-controller="viewport-toggle"> <!-- Accessibility: this link should always be the first piece of content inside the body--> <a href="#content" class="skip-to-content">Skip to main content</a> <button type="button" class="button button--primary button--switch-to-mobile hidden" data-viewport-toggle-target="switchToMobile" data-action="viewport-toggle#switchToMobile"> Switch to mobile version </button> <div id="sticky-notifications" class="stick-to-top js-stick-to-top"> <!-- Add browser warning. Will show for ie9 and below --> <!--[if IE]> <div class="notification-bar notification-bar--warning" role="status"> <span class="notification-bar__icon"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> <span class="sr-only">Warning</span> </span> <span class="notification-bar__message">You are using an unsupported browser, upgrade to a newer version.</span> </div> <![endif]--> <noscript> <div class="notification-bar notification-bar--warning" role="status"> <span class="notification-bar__icon"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> <span class="sr-only">Warning</span> </span> <span class="notification-bar__message">Some features may not work without JavaScript. Please try enabling it if you encounter problems.</span> </div> </noscript> <div data-html-include="/_includes/unauthed/notification-banners/"></div> </div> <div data-html-include="/_includes/authed/flash-messages/"></div> <div data-html-include="/_includes/authed/session-notifications/"></div> <header class="site-header "> <div class="site-container"> <div class="split-layout"> <div class="split-layout"> <div> <a class="site-header__logo" href="/"> <img alt="PyPI" src="/static/images/logo-small.8998e9d1.svg"> </a> </div> <form class="search-form search-form--primary" action="/search/" role="search"> <label for="search" class="sr-only">Search PyPI</label> <input id="search" class="search-form__search" type="text" name="q" placeholder="Search projects" value="" autocomplete="off" autocapitalize="off" spellcheck="false" data-controller="search-focus" data-action="keydown@window->search-focus#focusSearchField" data-search-focus-target="searchField"> <button type="submit" class="search-form__button"> <i class="fa fa-search" aria-hidden="true"></i> <span class="sr-only">Search</span> </button> </form> </div> <div data-html-include="/_includes/authed/current-user-indicator/"> <div id="user-indicator" class="horizontal-menu horizontal-menu--light horizontal-menu--tall"> <nav class="horizontal-menu horizontal-menu--light horizontal-menu--tall hide-on-tablet" aria-label="Main navigation"> <ul> <li class="horizontal-menu__item"><a href="/help/" class="horizontal-menu__link">Help</a></li> <li class="horizontal-menu__item"><a href="/sponsors/" class="horizontal-menu__link">Sponsors</a></li> <li class="horizontal-menu__item"><a href="/account/login/" class="horizontal-menu__link">Log in</a></li> <li class="horizontal-menu__item"><a href="/account/register/" class="horizontal-menu__link">Register</a></li> </ul> </nav> <nav class="dropdown dropdown--on-menu hidden show-on-tablet" aria-label="Main navigation"> <button type="button" class="horizontal-menu__link dropdown__trigger" aria-haspopup="true" aria-expanded="false" aria-label="View menu"> Menu <span class="dropdown__trigger-caret"> <i class="fa fa-caret-down" aria-hidden="true"></i> </span> </button> <ul class="dropdown__content" aria-hidden="true" aria-label="Main menu"> <li><a class="dropdown__link" href="/help/">Help</a></li> <li><a class="dropdown__link" href="/sponsors/">Sponsors</a></li> <li><a class="dropdown__link" href="/account/login/">Log in</a></li> <li><a class="dropdown__link" href="/account/register/">Register</a></li> </ul> </nav> </div> </div> </div> </div> </header> <div class="mobile-search"> <form class="search-form search-form--fullwidth" action="/search/" role="search"> <label for="mobile-search" class="sr-only">Search PyPI</label> <input id="mobile-search" class="search-form__search" type="text" name="q" placeholder="Search projects" value="" autocomplete="off" autocapitalize="off" spellcheck="false"> <button type="submit" class="search-form__button"> <i class="fa fa-search" aria-hidden="true"></i> <span class="sr-only">Search</span> </button> </form> </div> <main id="content"> <div class="banner"> <div class="package-header"> <div class="package-header__left"> <h1 class="package-header__name"> sentinel 1.0.0 </h1> <div data-controller="clipboard"> <p class="package-header__pip-instructions"> <span id="pip-command" data-clipboard-target="source">pip install sentinel</span> <button type="button" class="copy-tooltip copy-tooltip-s" data-action="clipboard#copy" data-clipboard-target="tooltip" data-clipboard-tooltip-value="Copy to clipboard"> <i class="fa fa-copy" aria-hidden="true"></i> <span class="sr-only">Copy PIP instructions</span> </button> </p> </div> </div> <div class="package-header__right"> <a class="status-badge status-badge--good" href="/project/sentinel/"> <span>Latest version</span> </a> <p class="package-header__date"> Released: <time datetime="2022-07-23T10:22:16+0000" data-controller="localized-time" data-localized-time-relative="true" data-localized-time-show-time="false"> Jul 23, 2022 </time> </p> </div> </div> </div> <div class="horizontal-section horizontal-section--grey horizontal-section--thin"> <div class="site-container"> <div data-html-include="/_includes/authed/administer-project-include/sentinel"></div> <div class="split-layout split-layout--middle package-description"> <p class="package-description__summary">Create sentinel objects, akin to None, NotImplemented, Ellipsis</p> <div data-html-include="/_includes/authed/edit-project-button/sentinel"></div> </div> </div> </div> <div data-controller="project-tabs"> <div class="tabs-container"> <div class="vertical-tabs"> <div class="vertical-tabs__tabs"> <div class="sidebar-section"> <h3 class="sidebar-section__title">Navigation</h3> <nav aria-label="Navigation for sentinel"> <ul class="vertical-tabs__list" role="tablist"> <li role="tab"> <a id="description-tab" href="#description" data-project-tabs-target="tab" data-action="project-tabs#onTabClick" class="vertical-tabs__tab vertical-tabs__tab--with-icon vertical-tabs__tab--is-active" aria-selected="true" aria-label="Project description. Focus will be moved to the description."> <i class="fa fa-align-left" aria-hidden="true"></i> Project description </a> </li> <li role="tab"> <a id="history-tab" href="#history" data-project-tabs-target="tab" data-action="project-tabs#onTabClick" class="vertical-tabs__tab vertical-tabs__tab--with-icon" aria-label="Release history. Focus will be moved to the history panel."> <i class="fa fa-history" aria-hidden="true"></i> Release history </a> </li> <li role="tab"> <a id="files-tab" href="#files" data-project-tabs-target="tab" data-action="project-tabs#onTabClick" class="vertical-tabs__tab vertical-tabs__tab--with-icon" aria-label="Download files. Focus will be moved to the project files."> <i class="fa fa-download" aria-hidden="true"></i> Download files </a> </li> </ul> </nav> </div> <div class="sidebar-section verified"> <h3 class="sidebar-section__title"> Verified details <i class="fa fa-circle-check check" title="Verified by PyPI on 2022-07-23"></i> </h3> <small><i>These details have been <a href="https://docs.pypi.org/project_metadata/#verified-details">verified by PyPI</a></i></small> <h6>Maintainers</h6> <span class="sidebar-section__maintainer"> <a href="/user/eddieantonio/" aria-label=""> <span class="sidebar-section__user-gravatar"> <img src="https://pypi-camo.freetls.fastly.net/53b6831cc2d84b92e319f40a4cdcc0d00a77eba1/68747470733a2f2f7365637572652e67726176617461722e636f6d2f6176617461722f31303334626638353762663032336336323930333837333566396262393466323f73697a653d3530" height="50" width="50" alt="Avatar for eddieantonio from gravatar.com" title="Avatar for eddieantonio from gravatar.com"> </span> <span class="sidebar-section__user-gravatar-text"> eddieantonio </span> </a> </span> </div> <div class="sidebar-section unverified"> <h3 class="sidebar-section__title">Unverified details</h3> <small><i>These details have <b>not</b> been verified by PyPI</i></small> <h6>Project links</h6> <ul class="vertical-tabs__list"> <li> <a class="vertical-tabs__tab vertical-tabs__tab--with-icon vertical-tabs__tab--condensed" href="https://github.com/eddieantonio/sentinel" rel="nofollow"> <i class="fas fa-home" aria-hidden="true"></i>Homepage </a> </li> <li> <a class="vertical-tabs__tab vertical-tabs__tab--with-icon vertical-tabs__tab--condensed" href="https://github.com/eddieantonio/sentinel.git" rel="nofollow"> <i class="fab fa-github" aria-hidden="true"></i>Repository </a> </li> </ul> <div class="sidebar-section unverified"> <h6>Meta</h6> <ul> <li> <span> <strong>License:</strong> MIT License (MIT) </span> </li> <li> <span> <strong>Author:</strong> <a href="mailto:hello@eddieantonio.ca">Eddie Antonio Santos</a> </span> </li> <li> <span> <strong>Requires:</strong> Python >=3.6, <4.0 </span> </li> </ul> </div> <div class="sidebar-section unverified"> <h6 class="sidebar-section__title">Classifiers</h6> <ul class="sidebar-section__classifiers"> <li> <strong>Development Status</strong> <ul> <li> <a href="/search/?c=Development+Status+%3A%3A+5+-+Production%2FStable"> 5 - Production/Stable </a> </li> </ul> </li> <li> <strong>Intended Audience</strong> <ul> <li> <a href="/search/?c=Intended+Audience+%3A%3A+Developers"> Developers </a> </li> </ul> </li> <li> <strong>License</strong> <ul> <li> <a href="/search/?c=License+%3A%3A+OSI+Approved+%3A%3A+MIT+License"> OSI Approved :: MIT License </a> </li> </ul> </li> <li> <strong>Operating System</strong> <ul> <li> <a href="/search/?c=Operating+System+%3A%3A+OS+Independent"> OS Independent </a> </li> </ul> </li> <li> <strong>Programming Language</strong> <ul> <li> <a href="/search/?c=Programming+Language+%3A%3A+Python+%3A%3A+3"> Python :: 3 </a> </li> <li> <a href="/search/?c=Programming+Language+%3A%3A+Python+%3A%3A+3.6"> Python :: 3.6 </a> </li> <li> <a href="/search/?c=Programming+Language+%3A%3A+Python+%3A%3A+3.7"> Python :: 3.7 </a> </li> <li> <a href="/search/?c=Programming+Language+%3A%3A+Python+%3A%3A+3.8"> Python :: 3.8 </a> </li> <li> <a href="/search/?c=Programming+Language+%3A%3A+Python+%3A%3A+3.9"> Python :: 3.9 </a> </li> <li> <a href="/search/?c=Programming+Language+%3A%3A+Python+%3A%3A+3.10"> Python :: 3.10 </a> </li> </ul> </li> <li> <strong>Topic</strong> <ul> <li> <a href="/search/?c=Topic+%3A%3A+Software+Development+%3A%3A+Libraries+%3A%3A+Python+Modules"> Software Development :: Libraries :: Python Modules </a> </li> </ul> </li> </ul> </div> </div><div class="sidebar-section" data-ea-publisher="psf" data-ea-type="psf" data-ea-keywords="pypi-sidebar"></div> <div data-html-include="https://pypi.org/_includes/authed/submit-malware-report/sentinel"></div> </div> <div class="vertical-tabs__panel"> <!-- mobile menu --> <nav aria-label="Navigation for sentinel"> <ul class="vertical-tabs__list" role="tablist"> <li role="tab"> <a id="mobile-description-tab" href="#description" data-project-tabs-target="mobileTab" data-action="project-tabs#onTabClick" class="vertical-tabs__tab vertical-tabs__tab--with-icon vertical-tabs__tab--mobile vertical-tabs__tab--no-top-border vertical-tabs__tab--is-active" aria-selected="true" aria-label="Project description. Focus will be moved to the description."> <i class="fa fa-align-left" aria-hidden="true"></i> Project description </a> </li> <li role="tab"> <a id="mobile-data-tab" href="#data" data-project-tabs-target="mobileTab" data-action="project-tabs#onTabClick" class="vertical-tabs__tab vertical-tabs__tab--with-icon vertical-tabs__tab--mobile" aria-label="Project details. Focus will be moved to the project details."> <i class="fa fa-info-circle" aria-hidden="true"></i> Project details </a> </li> <li role="tab"> <a id="mobile-history-tab" href="#history" data-project-tabs-target="mobileTab" data-action="project-tabs#onTabClick" class="vertical-tabs__tab vertical-tabs__tab--with-icon vertical-tabs__tab--mobile" aria-label="Release history. Focus will be moved to the history panel."> <i class="fa fa-history" aria-hidden="true"></i> Release history </a> </li> <li role="tab"> <a id="mobile-files-tab" href="#files" data-project-tabs-target="mobileTab" data-action="project-tabs#onTabClick" class="vertical-tabs__tab vertical-tabs__tab--with-icon vertical-tabs__tab--mobile" aria-label="Download files. Focus will be moved to the project files."> <i class="fa fa-download" aria-hidden="true"></i> Download files </a> </li> </ul> </nav> <div id="description" data-project-tabs-target="content" class="vertical-tabs__content" role="tabpanel" aria-labelledby="description-tab mobile-description-tab" tabindex="-1"> <h2 class="page-title">Project description</h2> <div class="project-description"> <p><a href="https://github.com/eddieantonio/sentinel/actions?query=workflow%3A%22Test+and+Lint%22" rel=nofollow><img alt=Tests src="https://pypi-camo.freetls.fastly.net/75587d85b1de93f4f9f53ff2c8cc5f294f6aeedf/68747470733a2f2f6769746875622e636f6d2f6564646965616e746f6e696f2f73656e74696e656c2f776f726b666c6f77732f54657374253230616e642532304c696e742f62616467652e737667"></a> <a href="https://pypi.org/project/sentinel/" rel=nofollow><img alt="PyPI version" src="https://pypi-camo.freetls.fastly.net/3d55e42887b28be6c1c0eba2416dcc77a7bfbd49/68747470733a2f2f696d672e736869656c64732e696f2f707970692f762f73656e74696e656c"></a></p> <p>Creates simple sentinel objects.</p> <section id=install> <h2>Install</h2> <p>Basic features:</p> <pre>pip install sentinel</pre> <p>with extra magic features powered by <a href="https://github.com/pwwang/python-varname" rel=nofollow>python-varname</a>:</p> <pre>pip install 'sentinel[varname]'</pre> </section> <section id=what-is-a-sentinel> <h2>What is a sentinel?</h2> <p><a href="http://en.wikipedia.org/wiki/Sentinel_nodes" rel=nofollow>Sentinels</a> are <a href="http://en.wikipedia.org/wiki/Singleton_pattern" rel=nofollow>singleton</a> objects that typically represent some terminating (end) condition or have a special, symbolic meaning. Python’s built-in <span class="docutils literal">None</span> is a sentinel. Python also has other sentinels like <span class="docutils literal">NotImplemented</span> and <span class="docutils literal">Ellipsis</span>.</p> <p>If you want to create your own sentinels, use this library! Make your calls to <span class="docutils literal">dict.get()</span> more meaningful! You can replace the <span class="docutils literal">object()</span> idiom with a sentinel:</p> <pre><code><span class=n>d</span> <span class=o>=</span> <span class=p>{</span><span class=s2>"a"</span><span class=p>:</span> <span class=mi>1</span><span class=p>,</span> <span class=s2>"b"</span><span class=p>:</span> <span class=kc>None</span><span class=p>}</span><span class=w> </span><span class=c1># Before sentinel:</span><span class=w> </span><span class=n>missing</span> <span class=o>=</span> <span class=nb>object</span><span class=p>()</span><span class=w> </span><span class=k>if</span> <span class=n>d</span><span class=o>.</span><span class=n>get</span><span class=p>(</span><span class=s2>"c"</span><span class=p>,</span> <span class=n>missing</span><span class=p>)</span> <span class=ow>is</span> <span class=n>missing</span><span class=p>:</span><span class=w> </span> <span class=o>...</span> <span class=c1># do some stuff</span><span class=w> </span><span class=c1># After sentinel:</span><span class=w> </span><span class=n>Missing</span> <span class=o>=</span> <span class=n>sentinel</span><span class=o>.</span><span class=n>create</span><span class=p>()</span><span class=w> </span><span class=k>if</span> <span class=n>d</span><span class=o>.</span><span class=n>get</span><span class=p>(</span><span class=s2>"c"</span><span class=p>,</span> <span class=n>Missing</span><span class=p>)</span> <span class=ow>is</span> <span class=n>Missing</span><span class=p>:</span><span class=w> </span> <span class=o>...</span> <span class=c1># do some stuff</span></code></pre> <section id=features> <h3>Features</h3> <ul class=simple> <li><p>sentinels are unique</li> <li><p>sentinels are singletons — the <strong>only</strong> instance of their own anonymous class</li> <li><p>sentinels can be used with <span class="docutils literal">is</span> comparisons</li> <li><p>sentinels can be used with <span class="docutils literal">pickle</span></li> <li><p>sentinels can be used with <span class="docutils literal">copy.deepcopy</span></li> <li><p>you can <strong>add</strong> arbitrary attributes and methods to sentinels</li> <li><p>sentinels have a nice, self-documenting <span class="docutils literal">__repr__</span>!</li> </ul> </section> </section> <section id=usage> <h2>Usage</h2> <p>Create a sentinel:</p> <pre>>>> import sentinel >>> MySentinel = sentinel.create("MySentinel") >>> MySentinel MySentinel </pre> <p>If you have <a href="https://github.com/pwwang/python-varname" rel=nofollow>python-varname</a> installed, or installed this module using <span class="docutils literal">pip install 'sentinel[varname]'</span>, <span class="docutils literal">sentinel.create()</span> can infer the name from the assignment expression:</p> <pre><code><span class=kn>import</span> <span class=nn>sentinel</span><span class=w> </span><span class=n>MySentinel</span> <span class=o>=</span> <span class=n>sentinel</span><span class=o>.</span><span class=n>create</span><span class=p>()</span><span class=w> </span><span class=nb>print</span><span class=p>(</span><span class=n>MySentinel</span><span class=p>)</span> <span class=c1># prints `MySentinel`</span></code></pre> <p><strong>NOTE</strong>: this will not work in the interactive console!</p> <pre>>>> import sentinel >>> # Fails because varname can't find the source code for the interactive console! >>> MySentinel = sentinel.create("MySentinel") </pre> <section id=example> <h3>Example</h3> <p>Sentinels are useful when other objects such as <span class="docutils literal">None</span>, <span class="docutils literal">False</span>, <span class="docutils literal">0</span>, <span class="docutils literal"><span class=pre>-1</span></span>, are valid values within some data structure. For example, setting default values when all other values are valid with: <span class="docutils literal">dict.setdefault()</span>:</p> <pre><code><span class=n>d</span> <span class=o>=</span> <span class=p>{</span><span class=s2>"stdout"</span><span class=p>:</span> <span class=kc>None</span><span class=p>,</span> <span class=s2>"stdin"</span><span class=p>:</span> <span class=mi>0</span><span class=p>,</span> <span class=s2>"EOF"</span><span class=p>:</span> <span class=o>-</span><span class=mi>1</span><span class=p>}</span><span class=w> </span><span class=n>MissingEntry</span> <span class=o>=</span> <span class=n>sentinel</span><span class=o>.</span><span class=n>create</span><span class=p>()</span><span class=w> </span><span class=p>[</span><span class=n>d</span><span class=o>.</span><span class=n>setdefault</span><span class=p>(</span><span class=n>key</span><span class=p>,</span> <span class=n>MissingEntry</span><span class=p>)</span> <span class=k>for</span> <span class=n>key</span> <span class=ow>in</span> <span class=p>(</span><span class=s2>"stdin"</span><span class=p>,</span> <span class=s2>"stdout"</span><span class=p>,</span> <span class=s2>"stderr"</span><span class=p>)]</span><span class=w> </span><span class=p>[</span><span class=mi>0</span><span class=p>,</span> <span class=kc>None</span><span class=p>,</span> <span class=n>MissingEntry</span><span class=p>]</span></code></pre> <p>Alternatively, using <span class="docutils literal">dict.get()</span> when fetching values:</p> <pre>>>> d = {"stdout": None, "stdin": 0, "EOF": -1} >>> d.get("stdout", MissingEntry) None >>> d.get("stdin", MissingEntry) 0 >>> d.get("stderr", MissingEntry) MissingEntry </pre> <p>Since a new sentinel can never occur in the original dictionary, you can tell which entries are missing or unset in a dictionary in a self-documenting way:</p> <pre><code><span class=n>Unset</span> <span class=o>=</span> <span class=n>sentinel</span><span class=o>.</span><span class=n>create</span><span class=p>()</span><span class=w> </span><span class=k>if</span> <span class=n>d</span><span class=o>.</span><span class=n>get</span><span class=p>(</span><span class=s2>"stdin"</span><span class=p>,</span> <span class=n>Unset</span><span class=p>)</span> <span class=ow>is</span> <span class=n>Unset</span><span class=p>:</span><span class=w> </span> <span class=n>stdin</span> <span class=o>=</span> <span class=mi>0</span> <span class=c1># some reasonable default</span></code></pre> </section> <section id=adding-extra-methods-and-class-attributes> <h3>Adding extra methods and class attributes</h3> <p>Sentinels may also inherit from base classes, or implement extra methods.</p> <p>Consider a binary search tree with two kinds of nodes: interior nodes (<span class="docutils literal">Node</span>) which contain some payload and leaves (<span class="docutils literal">Leaf</span>), which simply terminate traversal.</p> <p>To create singleton leaf which implements a <span class="docutils literal">search</span> method and an <span class="docutils literal">is_leaf</span> property, you may provide any extra class attributes in the <span class="docutils literal">cls_dict</span> keyword argument. The following is a full example of both the singleton <span class="docutils literal">Leaf</span> and its <span class="docutils literal">Node</span> counterpart:</p> <pre><code><span class=k>def</span> <span class=nf>_search_leaf</span><span class=p>(</span><span class=bp>self</span><span class=p>,</span> <span class=n>key</span><span class=p>):</span><span class=w> </span> <span class=k>raise</span> <span class=ne>KeyError</span><span class=p>(</span><span class=n>key</span><span class=p>)</span><span class=w> </span><span class=n>Leaf</span> <span class=o>=</span> <span class=n>sentinel</span><span class=o>.</span><span class=n>create</span><span class=p>(</span><span class=s1>'Leaf'</span><span class=p>,</span> <span class=n>cls_dict</span><span class=o>=</span><span class=p>{</span><span class=w> </span> <span class=s1>'search'</span><span class=p>:</span> <span class=n>_search_leaf</span><span class=p>,</span><span class=w> </span> <span class=s1>'is_leaf'</span><span class=p>:</span> <span class=nb>property</span><span class=p>(</span><span class=k>lambda</span> <span class=bp>self</span><span class=p>:</span> <span class=kc>True</span><span class=p>)</span><span class=w> </span><span class=p>})</span><span class=w> </span><span class=k>class</span> <span class=nc>Node</span><span class=p>(</span><span class=nb>object</span><span class=p>):</span><span class=w> </span> <span class=k>def</span> <span class=fm>__init__</span><span class=p>(</span><span class=bp>self</span><span class=p>,</span> <span class=n>key</span><span class=p>,</span> <span class=n>payload</span><span class=p>,</span> <span class=n>left</span><span class=o>=</span><span class=n>Leaf</span><span class=p>,</span> <span class=n>right</span><span class=o>=</span><span class=n>Leaf</span><span class=p>):</span><span class=w> </span> <span class=bp>self</span><span class=o>.</span><span class=n>left</span> <span class=o>=</span> <span class=n>left</span><span class=w> </span> <span class=bp>self</span><span class=o>.</span><span class=n>right</span> <span class=o>=</span> <span class=n>right</span><span class=w> </span> <span class=bp>self</span><span class=o>.</span><span class=n>key</span> <span class=o>=</span> <span class=n>key</span><span class=w> </span> <span class=bp>self</span><span class=o>.</span><span class=n>payload</span> <span class=o>=</span> <span class=n>payload</span><span class=w> </span> <span class=k>def</span> <span class=nf>search</span><span class=p>(</span><span class=bp>self</span><span class=p>,</span> <span class=n>key</span><span class=p>):</span><span class=w> </span> <span class=k>if</span> <span class=n>key</span> <span class=o><</span> <span class=bp>self</span><span class=o>.</span><span class=n>key</span><span class=p>:</span><span class=w> </span> <span class=k>return</span> <span class=bp>self</span><span class=o>.</span><span class=n>left</span><span class=o>.</span><span class=n>search</span><span class=p>(</span><span class=n>key</span><span class=p>)</span><span class=w> </span> <span class=k>elif</span> <span class=n>key</span> <span class=o>></span> <span class=bp>self</span><span class=o>.</span><span class=n>key</span><span class=p>:</span><span class=w> </span> <span class=k>return</span> <span class=bp>self</span><span class=o>.</span><span class=n>right</span><span class=o>.</span><span class=n>search</span><span class=p>(</span><span class=n>key</span><span class=p>)</span><span class=w> </span> <span class=k>else</span><span class=p>:</span><span class=w> </span> <span class=k>return</span> <span class=bp>self</span><span class=o>.</span><span class=n>payload</span><span class=w> </span> <span class=n>is_leaf</span> <span class=o>=</span> <span class=nb>property</span><span class=p>(</span><span class=k>lambda</span><span class=p>:</span> <span class=n>false</span><span class=p>)</span></code></pre> <p>Example usage:</p> <pre>>>> tree = Node(2, 'bar', Node(1, 'foo'), Node(3, 'baz')) >>> tree.search(1) 'foo' >>> tree.search(4) Traceback (most recent call last): ... KeyError: 2 </pre> </section> </section> <section id=contributing> <h2>Contributing</h2> <p>This project uses <a href="https://python-poetry.org/" rel=nofollow>Poetry</a>. To contribute to the codebase, make sure to <a href="https://python-poetry.org/docs/#installation" rel=nofollow>install poetry</a>, With Poetry installed, clone then repo, then within the repo directory, install the developer dependencies:</p> <pre>$ poetry install --extras varname</pre> <p>Next, I recommend you do all development tasks within the <span class="docutils literal">poetry shell</span>:</p> <pre>$ poetry shell (sentinel-nUnrocCf-py3.9) $ black . (sentinel-nUnrocCf-py3.9) $ pytest</pre> </section> </div> </div> <div id="data" data-project-tabs-target="content" class="vertical-tabs__content" role="tabpanel" aria-labelledby="mobile-data-tab" tabindex="-1"> <h2 class="page-title">Project details</h2> <div class="sidebar-section verified"> <h3 class="sidebar-section__title"> Verified details <i class="fa fa-circle-check check" title="Verified by PyPI on 2022-07-23"></i> </h3> <small><i>These details have been <a href="https://docs.pypi.org/project_metadata/#verified-details">verified by PyPI</a></i></small> <h6>Maintainers</h6> <span class="sidebar-section__maintainer"> <a href="/user/eddieantonio/" aria-label=""> <span class="sidebar-section__user-gravatar"> <img src="https://pypi-camo.freetls.fastly.net/53b6831cc2d84b92e319f40a4cdcc0d00a77eba1/68747470733a2f2f7365637572652e67726176617461722e636f6d2f6176617461722f31303334626638353762663032336336323930333837333566396262393466323f73697a653d3530" height="50" width="50" alt="Avatar for eddieantonio from gravatar.com" title="Avatar for eddieantonio from gravatar.com"> </span> <span class="sidebar-section__user-gravatar-text"> eddieantonio </span> </a> </span> </div> <div class="sidebar-section unverified"> <h3 class="sidebar-section__title">Unverified details</h3> <small><i>These details have <b>not</b> been verified by PyPI</i></small> <h6>Project links</h6> <ul class="vertical-tabs__list"> <li> <a class="vertical-tabs__tab vertical-tabs__tab--with-icon vertical-tabs__tab--condensed" href="https://github.com/eddieantonio/sentinel" rel="nofollow"> <i class="fas fa-home" aria-hidden="true"></i>Homepage </a> </li> <li> <a class="vertical-tabs__tab vertical-tabs__tab--with-icon vertical-tabs__tab--condensed" href="https://github.com/eddieantonio/sentinel.git" rel="nofollow"> <i class="fab fa-github" aria-hidden="true"></i>Repository </a> </li> </ul> <div class="sidebar-section unverified"> <h6>Meta</h6> <ul> <li> <span> <strong>License:</strong> MIT License (MIT) </span> </li> <li> <span> <strong>Author:</strong> <a href="mailto:hello@eddieantonio.ca">Eddie Antonio Santos</a> </span> </li> <li> <span> <strong>Requires:</strong> Python >=3.6, <4.0 </span> </li> </ul> </div> <div class="sidebar-section unverified"> <h6 class="sidebar-section__title">Classifiers</h6> <ul class="sidebar-section__classifiers"> <li> <strong>Development Status</strong> <ul> <li> <a href="/search/?c=Development+Status+%3A%3A+5+-+Production%2FStable"> 5 - Production/Stable </a> </li> </ul> </li> <li> <strong>Intended Audience</strong> <ul> <li> <a href="/search/?c=Intended+Audience+%3A%3A+Developers"> Developers </a> </li> </ul> </li> <li> <strong>License</strong> <ul> <li> <a href="/search/?c=License+%3A%3A+OSI+Approved+%3A%3A+MIT+License"> OSI Approved :: MIT License </a> </li> </ul> </li> <li> <strong>Operating System</strong> <ul> <li> <a href="/search/?c=Operating+System+%3A%3A+OS+Independent"> OS Independent </a> </li> </ul> </li> <li> <strong>Programming Language</strong> <ul> <li> <a href="/search/?c=Programming+Language+%3A%3A+Python+%3A%3A+3"> Python :: 3 </a> </li> <li> <a href="/search/?c=Programming+Language+%3A%3A+Python+%3A%3A+3.6"> Python :: 3.6 </a> </li> <li> <a href="/search/?c=Programming+Language+%3A%3A+Python+%3A%3A+3.7"> Python :: 3.7 </a> </li> <li> <a href="/search/?c=Programming+Language+%3A%3A+Python+%3A%3A+3.8"> Python :: 3.8 </a> </li> <li> <a href="/search/?c=Programming+Language+%3A%3A+Python+%3A%3A+3.9"> Python :: 3.9 </a> </li> <li> <a href="/search/?c=Programming+Language+%3A%3A+Python+%3A%3A+3.10"> Python :: 3.10 </a> </li> </ul> </li> <li> <strong>Topic</strong> <ul> <li> <a href="/search/?c=Topic+%3A%3A+Software+Development+%3A%3A+Libraries+%3A%3A+Python+Modules"> Software Development :: Libraries :: Python Modules </a> </li> </ul> </li> </ul> </div> </div> <br> </div> <div id="history" data-project-tabs-target="content" class="vertical-tabs__content" role="tabpanel" aria-labelledby="history-tab mobile-history-tab" tabindex="-1"> <h2 class="page-title split-layout"> <span>Release history</span> <span class="reset-text margin-top"> <a href="/help/#project-release-notifications">Release notifications</a> | <a href="/rss/project/sentinel/releases.xml">RSS feed <i class="fa fa-rss" aria-hidden="true"></i></a> </span> </h2> <div class="release-timeline"> <div class="release release--latest release--current"> <div class="release__meta"> <span class="badge">This version</span> </div> <div class="release__graphic"> <div class="release__line"></div> <img class="release__node" alt="" src="https://pypi.org/static/images/blue-cube.572a5bfb.svg"> </div> <a class="card release__card" href="/project/sentinel/1.0.0/"> <p class="release__version"> 1.0.0 </p> <p class="release__version-date"> <time datetime="2022-07-23T10:22:16+0000" data-controller="localized-time" data-localized-time-relative="true" data-localized-time-show-time="false"> Jul 23, 2022 </time> </p> </a> </div> <div class="release"> <div class="release__meta"> </div> <div class="release__graphic"> <div class="release__line"></div> <img class="release__node" alt="" src="https://pypi.org/static/images/white-cube.2351a86c.svg"> </div> <a class="card release__card" href="/project/sentinel/0.3.0/"> <p class="release__version"> 0.3.0 </p> <p class="release__version-date"> <time datetime="2020-12-31T20:36:58+0000" data-controller="localized-time" data-localized-time-relative="true" data-localized-time-show-time="false"> Dec 31, 2020 </time> </p> </a> </div> <div class="release"> <div class="release__meta"> </div> <div class="release__graphic"> <div class="release__line"></div> <img class="release__node" alt="" src="https://pypi.org/static/images/white-cube.2351a86c.svg"> </div> <a class="card release__card" href="/project/sentinel/0.3.0a1/"> <p class="release__version"> 0.3.0a1 <span class="badge badge--warning"> pre-release </span> </p> <p class="release__version-date"> <time datetime="2020-12-31T20:28:00+0000" data-controller="localized-time" data-localized-time-relative="true" data-localized-time-show-time="false"> Dec 31, 2020 </time> </p> </a> </div> <div class="release"> <div class="release__meta"> </div> <div class="release__graphic"> <div class="release__line"></div> <img class="release__node" alt="" src="https://pypi.org/static/images/white-cube.2351a86c.svg"> </div> <a class="card release__card" href="/project/sentinel/0.2.1a0/"> <p class="release__version"> 0.2.1a0 <span class="badge badge--warning"> pre-release </span> </p> <p class="release__version-date"> <time datetime="2020-12-30T21:05:06+0000" data-controller="localized-time" data-localized-time-relative="true" data-localized-time-show-time="false"> Dec 30, 2020 </time> </p> </a> </div> <div class="release"> <div class="release__meta"> </div> <div class="release__graphic"> <div class="release__line"></div> <img class="release__node" alt="" src="https://pypi.org/static/images/white-cube.2351a86c.svg"> </div> <a class="card release__card" href="/project/sentinel/0.2.0/"> <p class="release__version"> 0.2.0 </p> <p class="release__version-date"> <time datetime="2020-12-30T21:52:58+0000" data-controller="localized-time" data-localized-time-relative="true" data-localized-time-show-time="false"> Dec 30, 2020 </time> </p> </a> </div> <div class="release"> <div class="release__meta"> </div> <div class="release__graphic"> <div class="release__line"></div> <img class="release__node" alt="" src="https://pypi.org/static/images/white-cube.2351a86c.svg"> </div> <a class="card release__card" href="/project/sentinel/0.1.2/"> <p class="release__version"> 0.1.2 </p> <p class="release__version-date"> <time datetime="2019-10-22T03:01:15+0000" data-controller="localized-time" data-localized-time-relative="true" data-localized-time-show-time="false"> Oct 22, 2019 </time> </p> </a> </div> <div class="release"> <div class="release__meta"> </div> <div class="release__graphic"> <div class="release__line"></div> <img class="release__node" alt="" src="https://pypi.org/static/images/white-cube.2351a86c.svg"> </div> <a class="card release__card" href="/project/sentinel/0.1.1/"> <p class="release__version"> 0.1.1 </p> <p class="release__version-date"> <time datetime="2014-08-23T05:24:12+0000" data-controller="localized-time" data-localized-time-relative="true" data-localized-time-show-time="false"> Aug 23, 2014 </time> </p> </a> </div> <div class="release release--oldest"> <div class="release__meta"> </div> <div class="release__graphic"> <div class="release__line"></div> <img class="release__node" alt="" src="https://pypi.org/static/images/white-cube.2351a86c.svg"> </div> <a class="card release__card" href="/project/sentinel/0.1.0/"> <p class="release__version"> 0.1.0 </p> <p class="release__version-date"> <time datetime="2014-08-22T23:36:53+0000" data-controller="localized-time" data-localized-time-relative="true" data-localized-time-show-time="false"> Aug 22, 2014 </time> </p> </a> </div> </div> </div> <div id="files" data-project-tabs-target="content" class="vertical-tabs__content" role="tabpanel" aria-labelledby="files-tab mobile-files-tab" tabindex="-1"> <h2 class="page-title">Download files</h2> <p>Download the file for your platform. If you're not sure which to choose, learn more about <a href="https://packaging.python.org/tutorials/installing-packages/" title="External link" target="_blank" rel="noopener">installing packages</a>.</p> <h3> Source Distribution </h3> <div class="file"> <div class="file__graphic"> <i class="far fa-file" aria-hidden="true"></i> </div> <div class="card file__card"> <a href="https://files.pythonhosted.org/packages/4a/d8/49115169583d02b38e7d93909a474c7ed0863f7d4df27095588344f2e66a/sentinel-1.0.0.tar.gz"> sentinel-1.0.0.tar.gz </a> (7.0 kB <a href="#sentinel-1.0.0.tar.gz" data-project-tabs-target="tab" data-action="project-tabs#onTabClick">view details</a>) <p class="file__meta"> Uploaded <time datetime="2022-07-23T10:22:19+0000" data-controller="localized-time" data-localized-time-relative="true" data-localized-time-show-time="false"> Jul 23, 2022 </time> <code>Source</code> </p> </div> </div> <h3> Built Distribution </h3> <div class="file"> <div class="file__graphic"> <i class="far fa-file" aria-hidden="true"></i> </div> <div class="card file__card"> <a href="https://files.pythonhosted.org/packages/f3/c4/37cd564e7c5ee72afc864e43b872c716ed43604e50ea0adbb510d720f92d/sentinel-1.0.0-py3-none-any.whl"> sentinel-1.0.0-py3-none-any.whl </a> (6.5 kB <a href="#sentinel-1.0.0-py3-none-any.whl" data-project-tabs-target="tab" data-action="project-tabs#onTabClick">view details</a>) <p class="file__meta"> Uploaded <time datetime="2022-07-23T10:22:16+0000" data-controller="localized-time" data-localized-time-relative="true" data-localized-time-show-time="false"> Jul 23, 2022 </time> <code>Python 3</code> </p> </div> </div> </div> <div id="sentinel-1.0.0.tar.gz" data-project-tabs-target="content" class="vertical-tabs__content" role="tabpanel" aria-labelledby="file-tab mobile-file-tab" tabindex="-1"> <h2 class="page-title">File details</h2> <p>Details for the file <code>sentinel-1.0.0.tar.gz</code>.</p> <h3>File metadata</h3> <div> <ul> <li> Download URL: <a href="https://files.pythonhosted.org/packages/4a/d8/49115169583d02b38e7d93909a474c7ed0863f7d4df27095588344f2e66a/sentinel-1.0.0.tar.gz"> sentinel-1.0.0.tar.gz </a> </li> <li>Upload date: <time datetime="2022-07-23T10:22:19+0000" data-controller="localized-time" data-localized-time-relative="true" data-localized-time-show-time="false"> Jul 23, 2022 </time></li> <li>Size: 7.0 kB</li> <li>Tags: Source</li> <li> Uploaded using Trusted Publishing? No </li> <li>Uploaded via: poetry/1.1.14 CPython/3.10.5 Linux/5.15.0-1014-azure</li> </ul> </div> <h3>File hashes</h3> <div> <table class="table table--hashes"> <caption class="sr-only">Hashes for sentinel-1.0.0.tar.gz</caption> <thead> <tr> <th scope="col">Algorithm</th> <th scope="col">Hash digest</th> <th></th> </tr> </thead> <tbody> <tr data-controller="clipboard"> <th scope="row">SHA256</th> <td><code data-clipboard-target="source">190928f9951af6e94a1f84eefcaed791c28097dd152b88e988906be300451fd2</code></td> <td class="table__align-right"> <button type="button" class="button button--small copy-tooltip copy-tooltip-w" data-action="clipboard#copy" data-clipboard-target="tooltip" data-clipboard-tooltip-value="Copy to clipboard"> Copy </button> </td> </tr> <tr data-controller="clipboard"> <th scope="row">MD5</th> <td><code data-clipboard-target="source">e73697867a33f3009e3e6dfea81e2bbc</code></td> <td class="table__align-right"> <button type="button" class="button button--small copy-tooltip copy-tooltip-w" data-action="clipboard#copy" data-clipboard-target="tooltip" data-clipboard-tooltip-value="Copy to clipboard"> Copy </button> </td> </tr> <tr data-controller="clipboard"> <th scope="row">BLAKE2b-256</th> <td><code data-clipboard-target="source">4ad849115169583d02b38e7d93909a474c7ed0863f7d4df27095588344f2e66a</code></td> <td class="table__align-right"> <button type="button" class="button button--small copy-tooltip copy-tooltip-w" data-action="clipboard#copy" data-clipboard-target="tooltip" data-clipboard-tooltip-value="Copy to clipboard"> Copy </button> </td> </tr> </tbody> </table> <p> <a href="https://pip.pypa.io/en/stable/topics/secure-installs/#hash-checking-mode" title="External link" target="_blank" rel="noopener">See more details on using hashes here.</a> </p> </div> </div> <div id="sentinel-1.0.0-py3-none-any.whl" data-project-tabs-target="content" class="vertical-tabs__content" role="tabpanel" aria-labelledby="file-tab mobile-file-tab" tabindex="-1"> <h2 class="page-title">File details</h2> <p>Details for the file <code>sentinel-1.0.0-py3-none-any.whl</code>.</p> <h3>File metadata</h3> <div> <ul> <li> Download URL: <a href="https://files.pythonhosted.org/packages/f3/c4/37cd564e7c5ee72afc864e43b872c716ed43604e50ea0adbb510d720f92d/sentinel-1.0.0-py3-none-any.whl"> sentinel-1.0.0-py3-none-any.whl </a> </li> <li>Upload date: <time datetime="2022-07-23T10:22:16+0000" data-controller="localized-time" data-localized-time-relative="true" data-localized-time-show-time="false"> Jul 23, 2022 </time></li> <li>Size: 6.5 kB</li> <li>Tags: Python 3</li> <li> Uploaded using Trusted Publishing? No </li> <li>Uploaded via: poetry/1.1.14 CPython/3.10.5 Linux/5.15.0-1014-azure</li> </ul> </div> <h3>File hashes</h3> <div> <table class="table table--hashes"> <caption class="sr-only">Hashes for sentinel-1.0.0-py3-none-any.whl</caption> <thead> <tr> <th scope="col">Algorithm</th> <th scope="col">Hash digest</th> <th></th> </tr> </thead> <tbody> <tr data-controller="clipboard"> <th scope="row">SHA256</th> <td><code data-clipboard-target="source">24f02a34cc9f0fcba5a666a23b6c7f56aff332fc624632ee442e7237751a9f60</code></td> <td class="table__align-right"> <button type="button" class="button button--small copy-tooltip copy-tooltip-w" data-action="clipboard#copy" data-clipboard-target="tooltip" data-clipboard-tooltip-value="Copy to clipboard"> Copy </button> </td> </tr> <tr data-controller="clipboard"> <th scope="row">MD5</th> <td><code data-clipboard-target="source">734b932fd2eec3dfd8e54afe71407c29</code></td> <td class="table__align-right"> <button type="button" class="button button--small copy-tooltip copy-tooltip-w" data-action="clipboard#copy" data-clipboard-target="tooltip" data-clipboard-tooltip-value="Copy to clipboard"> Copy </button> </td> </tr> <tr data-controller="clipboard"> <th scope="row">BLAKE2b-256</th> <td><code data-clipboard-target="source">f3c437cd564e7c5ee72afc864e43b872c716ed43604e50ea0adbb510d720f92d</code></td> <td class="table__align-right"> <button type="button" class="button button--small copy-tooltip copy-tooltip-w" data-action="clipboard#copy" data-clipboard-target="tooltip" data-clipboard-tooltip-value="Copy to clipboard"> Copy </button> </td> </tr> </tbody> </table> <p> <a href="https://pip.pypa.io/en/stable/topics/secure-installs/#hash-checking-mode" title="External link" target="_blank" rel="noopener">See more details on using hashes here.</a> </p> </div> </div> </div> </div> </div> </div> </main> <footer class="footer"> <div class="footer__logo"> <img src="/static/images/white-cube.2351a86c.svg" alt="" class="-js-white-cube"> </div> <div class="footer__menus"> <div class="footer__menu"> <h2>Help</h2> <nav aria-label="Help navigation"> <ul> <li><a href="https://packaging.python.org/tutorials/installing-packages/" title="External link" target="_blank" rel="noopener">Installing packages</a></li> <li><a href="https://packaging.python.org/tutorials/packaging-projects/" title="External link" target="_blank" rel="noopener">Uploading packages</a></li> <li><a href="https://packaging.python.org/" title="External link" target="_blank" rel="noopener">User guide</a></li> <li><a href="https://www.python.org/dev/peps/pep-0541/" title="External link" target="_blank" rel="noopener">Project name retention</a></li> <li><a href="/help/">FAQs</a></li> </ul> </nav> </div> <div class="footer__menu"> <h2>About PyPI</h2> <nav aria-label="About PyPI navigation"> <ul> <li><a href="https://blog.pypi.org" title="External link" target="_blank" rel="noopener">PyPI Blog</a></li> <li><a href="https://dtdg.co/pypi" title="External link" target="_blank" rel="noopener">Infrastructure dashboard</a></li> <li><a href="/stats/">Statistics</a></li> <li><a href="/trademarks/">Logos & trademarks</a></li> <li><a href="/sponsors/">Our sponsors</a></li> </ul> </nav> </div> <div class="footer__menu"> <h2>Contributing to PyPI</h2> <nav aria-label="How to contribute navigation"> <ul> <li><a href="/help/#feedback">Bugs and feedback</a></li> <li><a href="https://github.com/pypi/warehouse" title="External link" target="_blank" rel="noopener">Contribute on GitHub</a></li> <li><a href="https://hosted.weblate.org/projects/pypa/warehouse/" title="External link" target="_blank" rel="noopener">Translate PyPI</a></li> <li><a href="/sponsors/">Sponsor PyPI</a></li> <li><a href="https://github.com/pypi/warehouse/graphs/contributors" title="External link" target="_blank" rel="noopener">Development credits</a></li> </ul> </nav> </div> <div class="footer__menu"> <h2>Using PyPI</h2> <nav aria-label="Using PyPI navigation"> <ul> <li><a href="https://policies.python.org/pypi.org/Terms-of-Service/" title="External link" target="_blank" rel="noopener">Terms of Service</a></li> <li><a href="/security/">Report security issue</a></li> <li><a href="https://policies.python.org/python.org/code-of-conduct/" title="External link" target="_blank" rel="noopener">Code of conduct</a></li> <li><a href="https://policies.python.org/pypi.org/Privacy-Notice/" title="External link" target="_blank" rel="noopener">Privacy Notice</a></li> <li><a href="https://policies.python.org/pypi.org/Acceptable-Use-Policy/" title="External link" target="_blank" rel="noopener">Acceptable Use Policy</a></li> </ul> </nav> </div> </div> <hr class="footer__divider"> <div class="footer__text"> <p>Status:<a href="https://status.python.org/" title="External link" target="_blank" rel="noopener"> <span data-statuspage-domain="https://2p66nmmycsj3.statuspage.io">all systems operational</span></a> </p> <p> Developed and maintained by the Python community, for the Python community. <br> <a href="https://donate.pypi.org">Donate today!</a> </p> <p> "PyPI", "Python Package Index", and the blocks logos are registered <a href="/trademarks/">trademarks</a> of the <a href="https://www.python.org/psf-landing" target="_blank" rel="noopener">Python Software Foundation</a>.<br> </p> <p> © 2025 <a href="https://www.python.org/psf-landing/" title="External link" target="_blank" rel="noopener">Python Software Foundation</a><br> <a href="/sitemap/">Site map</a> </p> </div> <div class="centered hide-on-desktop"> <button type="button" class="button button--switch-to-desktop hidden" data-viewport-toggle-target="switchToDesktop" data-action="viewport-toggle#switchToDesktop"> Switch to desktop version </button> </div> </footer> <div class="language-switcher"> <form action="/locale/"> <ul> <li> <button class="language-switcher__selected" name="locale_id" value="en" type="submit" > English </button> </li> <li> <button name="locale_id" value="es" type="submit" > español </button> </li> <li> <button name="locale_id" value="fr" type="submit" > français </button> </li> <li> <button name="locale_id" value="ja" type="submit" > 日本語 </button> </li> <li> <button name="locale_id" value="pt_BR" type="submit" > português (Brasil) </button> </li> <li> <button name="locale_id" value="uk" type="submit" > українська </button> </li> <li> <button name="locale_id" value="el" type="submit" > Ελληνικά </button> </li> <li> <button name="locale_id" value="de" type="submit" > Deutsch </button> </li> <li> <button name="locale_id" value="zh_Hans" type="submit" > 中文 (简体) </button> </li> <li> <button name="locale_id" value="zh_Hant" type="submit" > 中文 (繁體) </button> </li> <li> <button name="locale_id" value="ru" type="submit" > русский </button> </li> <li> <button name="locale_id" value="he" type="submit" > עברית </button> </li> <li> <button name="locale_id" value="eo" type="submit" > Esperanto </button> </li> <li> <button name="locale_id" value="ko" type="submit" > 한국어 </button> </li> </ul> </form> </div> <div class="sponsors"> <p class="sponsors__title">Supported by</p> <div class="sponsors__divider"></div> <a class="sponsors__sponsor" target="_blank" rel="noopener" href="https://aws.amazon.com/"> <img class=sponsors__image src="https://pypi-camo.freetls.fastly.net/ed7074cadad1a06f56bc520ad9bd3e00d0704c5b/68747470733a2f2f73746f726167652e676f6f676c65617069732e636f6d2f707970692d6173736574732f73706f6e736f726c6f676f732f6177732d77686974652d6c6f676f2d7443615473387a432e706e67" alt=AWS loading=lazy> <span class="sponsors__name">AWS</span> <span class="sponsors__service"> Cloud computing and Security Sponsor </span> </a> <a class="sponsors__sponsor" target="_blank" rel="noopener" href="https://www.datadoghq.com/"> <img class=sponsors__image src="https://pypi-camo.freetls.fastly.net/8855f7c063a3bdb5b0ce8d91bfc50cf851cc5c51/68747470733a2f2f73746f726167652e676f6f676c65617069732e636f6d2f707970692d6173736574732f73706f6e736f726c6f676f732f64617461646f672d77686974652d6c6f676f2d6668644c4e666c6f2e706e67" alt=Datadog loading=lazy> <span class="sponsors__name">Datadog</span> <span class="sponsors__service"> Monitoring </span> </a> <a class="sponsors__sponsor" target="_blank" rel="noopener" href="https://www.fastly.com/"> <img class=sponsors__image src="https://pypi-camo.freetls.fastly.net/df6fe8829cbff2d7f668d98571df1fd011f36192/68747470733a2f2f73746f726167652e676f6f676c65617069732e636f6d2f707970692d6173736574732f73706f6e736f726c6f676f732f666173746c792d77686974652d6c6f676f2d65684d3077735f6f2e706e67" alt=Fastly loading=lazy> <span class="sponsors__name">Fastly</span> <span class="sponsors__service"> CDN </span> </a> <a class="sponsors__sponsor" target="_blank" rel="noopener" href="https://careers.google.com/"> <img class=sponsors__image src="https://pypi-camo.freetls.fastly.net/420cc8cf360bac879e24c923b2f50ba7d1314fb0/68747470733a2f2f73746f726167652e676f6f676c65617069732e636f6d2f707970692d6173736574732f73706f6e736f726c6f676f732f676f6f676c652d77686974652d6c6f676f2d616734424e3774332e706e67" alt=Google loading=lazy> <span class="sponsors__name">Google</span> <span class="sponsors__service"> Download Analytics </span> </a> <a class="sponsors__sponsor" target="_blank" rel="noopener" href="https://www.pingdom.com/"> <img class=sponsors__image src="https://pypi-camo.freetls.fastly.net/d01053c02f3a626b73ffcb06b96367fdbbf9e230/68747470733a2f2f73746f726167652e676f6f676c65617069732e636f6d2f707970692d6173736574732f73706f6e736f726c6f676f732f70696e67646f6d2d77686974652d6c6f676f2d67355831547546362e706e67" alt=Pingdom loading=lazy> <span class="sponsors__name">Pingdom</span> <span class="sponsors__service"> Monitoring </span> </a> <a class="sponsors__sponsor" target="_blank" rel="noopener" href="https://getsentry.com/for/python"> <img class=sponsors__image src="https://pypi-camo.freetls.fastly.net/67af7117035e2345bacb5a82e9aa8b5b3e70701d/68747470733a2f2f73746f726167652e676f6f676c65617069732e636f6d2f707970692d6173736574732f73706f6e736f726c6f676f732f73656e7472792d77686974652d6c6f676f2d4a2d6b64742d706e2e706e67" alt=Sentry loading=lazy> <span class="sponsors__name">Sentry</span> <span class="sponsors__service"> Error logging </span> </a> <a class="sponsors__sponsor" target="_blank" rel="noopener" href="https://statuspage.io"> <img class=sponsors__image src="https://pypi-camo.freetls.fastly.net/b611884ff90435a0575dbab7d9b0d3e60f136466/68747470733a2f2f73746f726167652e676f6f676c65617069732e636f6d2f707970692d6173736574732f73706f6e736f726c6f676f732f737461747573706167652d77686974652d6c6f676f2d5467476c6a4a2d502e706e67" alt=StatusPage loading=lazy> <span class="sponsors__name">StatusPage</span> <span class="sponsors__service"> Status page </span> </a> </div> </body> </html>