CINXE.COM
Python Software Foundation News: 06/01/2024 - 07/01/2024
<!DOCTYPE html> <html class='v2' dir='ltr' lang='en' xmlns='http://www.w3.org/1999/xhtml' xmlns:b='http://www.google.com/2005/gml/b' xmlns:data='http://www.google.com/2005/gml/data' xmlns:expr='http://www.google.com/2005/gml/expr'> <head> <link href='https://www.blogger.com/static/v1/widgets/3566091532-css_bundle_v2.css' rel='stylesheet' type='text/css'/> <!-- Google tag (gtag.js) --> <script async='async' src='https://www.googletagmanager.com/gtag/js?id=G-H6ZX4B41B7'></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-H6ZX4B41B7'); </script> <meta content='Python Software Foundation Blog' property='og:site_name'/> <meta content='https://pyfound.blogspot.com' property='og:url'/> <meta content='' property='og:description'/> <meta content='' property='og:type'/> <meta content='https://s3.dualstack.us-east-2.amazonaws.com/pythondotorg-assets/media/psf/grants/report-2006-10/psf-logo.png' property='og:image'/> <meta content='width=1100' name='viewport'/> <meta content='text/html; charset=UTF-8' http-equiv='Content-Type'/> <meta content='blogger' name='generator'/> <link href='https://pyfound.blogspot.com/favicon.ico' rel='icon' type='image/x-icon'/> <link href='https://pyfound.blogspot.com/2024/06/' rel='canonical'/> <link rel="alternate" type="application/atom+xml" title="Python Software Foundation News - Atom" href="https://pyfound.blogspot.com/feeds/posts/default" /> <link rel="alternate" type="application/rss+xml" title="Python Software Foundation News - RSS" href="https://pyfound.blogspot.com/feeds/posts/default?alt=rss" /> <link rel="service.post" type="application/atom+xml" title="Python Software Foundation News - Atom" href="https://www.blogger.com/feeds/8520/posts/default" /> <!--Can't find substitution for tag [blog.ieCssRetrofitLinks]--> <meta content='https://pyfound.blogspot.com/2024/06/' property='og:url'/> <meta content='Python Software Foundation News' property='og:title'/> <meta content='聽聽News from the Python Software Foundation' property='og:description'/> <title>Python Software Foundation News: 06/01/2024 - 07/01/2024</title> <style id='page-skin-1' type='text/css'><!-- /* ----------------------------------------------- Blogger Template Style Name: Simple Designer: Blogger URL: www.blogger.com ----------------------------------------------- */ /* Variable definitions ==================== <Variable name="keycolor" description="Main Color" type="color" default="#66bbdd"/> <Group description="Page Text" selector="body"> <Variable name="body.font" description="Font" type="font" default="normal normal 12px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="body.text.color" description="Text Color" type="color" default="#222222"/> </Group> <Group description="Backgrounds" selector=".body-fauxcolumns-outer"> <Variable name="body.background.color" description="Outer Background" type="color" default="#66bbdd"/> <Variable name="content.background.color" description="Main Background" type="color" default="#ffffff"/> <Variable name="header.background.color" description="Header Background" type="color" default="transparent"/> </Group> <Group description="Links" selector=".main-outer"> <Variable name="link.color" description="Link Color" type="color" default="#2288bb"/> <Variable name="link.visited.color" description="Visited Color" type="color" default="#888888"/> <Variable name="link.hover.color" description="Hover Color" type="color" default="#33aaff"/> </Group> <Group description="Blog Title" selector=".header h1"> <Variable name="header.font" description="Font" type="font" default="normal normal 60px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="header.text.color" description="Title Color" type="color" default="#3399bb" /> </Group> <Group description="Blog Description" selector=".header .description"> <Variable name="description.text.color" description="Description Color" type="color" default="#777777" /> </Group> <Group description="Tabs Text" selector=".tabs-inner .widget li a"> <Variable name="tabs.font" description="Font" type="font" default="normal normal 14px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="tabs.text.color" description="Text Color" type="color" default="#999999"/> <Variable name="tabs.selected.text.color" description="Selected Color" type="color" default="#000000"/> </Group> <Group description="Tabs Background" selector=".tabs-outer .PageList"> <Variable name="tabs.background.color" description="Background Color" type="color" default="#f5f5f5"/> <Variable name="tabs.selected.background.color" description="Selected Color" type="color" default="#eeeeee"/> </Group> <Group description="Post Title" selector="h3.post-title, .comments h4"> <Variable name="post.title.font" description="Font" type="font" default="normal normal 22px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> </Group> <Group description="Date Header" selector=".date-header"> <Variable name="date.header.color" description="Text Color" type="color" default="#666666"/> <Variable name="date.header.background.color" description="Background Color" type="color" default="transparent"/> <Variable name="date.header.font" description="Text Font" type="font" default="normal bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="date.header.padding" description="Date Header Padding" type="string" default="inherit"/> <Variable name="date.header.letterspacing" description="Date Header Letter Spacing" type="string" default="inherit"/> <Variable name="date.header.margin" description="Date Header Margin" type="string" default="inherit"/> </Group> <Group description="Post Footer" selector=".post-footer"> <Variable name="post.footer.text.color" description="Text Color" type="color" default="#666666"/> <Variable name="post.footer.background.color" description="Background Color" type="color" default="#f9f9f9"/> <Variable name="post.footer.border.color" description="Shadow Color" type="color" default="#eeeeee"/> </Group> <Group description="Gadgets" selector="h2"> <Variable name="widget.title.font" description="Title Font" type="font" default="normal bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="widget.title.text.color" description="Title Color" type="color" default="#000000"/> <Variable name="widget.alternate.text.color" description="Alternate Color" type="color" default="#999999"/> </Group> <Group description="Images" selector=".main-inner"> <Variable name="image.background.color" description="Background Color" type="color" default="#ffffff"/> <Variable name="image.border.color" description="Border Color" type="color" default="#eeeeee"/> <Variable name="image.text.color" description="Caption Text Color" type="color" default="#666666"/> </Group> <Group description="Accents" selector=".content-inner"> <Variable name="body.rule.color" description="Separator Line Color" type="color" default="#eeeeee"/> <Variable name="tabs.border.color" description="Tabs Border Color" type="color" default="#ffd343"/> </Group> <Variable name="body.background" description="Body Background" type="background" color="#eeeeee" default="$(color) none repeat scroll top left"/> <Variable name="body.background.override" description="Body Background Override" type="string" default=""/> <Variable name="body.background.gradient.cap" description="Body Gradient Cap" type="url" default="url(https://resources.blogblog.com/blogblog/data/1kt/simple/gradients_light.png)"/> <Variable name="body.background.gradient.tile" description="Body Gradient Tile" type="url" default="url(https://resources.blogblog.com/blogblog/data/1kt/simple/body_gradient_tile_light.png)"/> <Variable name="content.background.color.selector" description="Content Background Color Selector" type="string" default=".content-inner"/> <Variable name="content.padding" description="Content Padding" type="length" default="10px" min="0" max="100px"/> <Variable name="content.padding.horizontal" description="Content Horizontal Padding" type="length" default="10px" min="0" max="100px"/> <Variable name="content.shadow.spread" description="Content Shadow Spread" type="length" default="40px" min="0" max="100px"/> <Variable name="content.shadow.spread.webkit" description="Content Shadow Spread (WebKit)" type="length" default="5px" min="0" max="100px"/> <Variable name="content.shadow.spread.ie" description="Content Shadow Spread (IE)" type="length" default="10px" min="0" max="100px"/> <Variable name="main.border.width" description="Main Border Width" type="length" default="0" min="0" max="10px"/> <Variable name="header.background.gradient" description="Header Gradient" type="url" default="none"/> <Variable name="header.shadow.offset.left" description="Header Shadow Offset Left" type="length" default="-1px" min="-50px" max="50px"/> <Variable name="header.shadow.offset.top" description="Header Shadow Offset Top" type="length" default="-1px" min="-50px" max="50px"/> <Variable name="header.shadow.spread" description="Header Shadow Spread" type="length" default="1px" min="0" max="100px"/> <Variable name="header.padding" description="Header Padding" type="length" default="30px" min="0" max="100px"/> <Variable name="header.border.size" description="Header Border Size" type="length" default="1px" min="0" max="10px"/> <Variable name="header.bottom.border.size" description="Header Bottom Border Size" type="length" default="1px" min="0" max="10px"/> <Variable name="header.border.horizontalsize" description="Header Horizontal Border Size" type="length" default="0" min="0" max="10px"/> <Variable name="description.text.size" description="Description Text Size" type="string" default="140%"/> <Variable name="tabs.margin.top" description="Tabs Margin Top" type="length" default="0" min="0" max="100px"/> <Variable name="tabs.margin.side" description="Tabs Side Margin" type="length" default="30px" min="0" max="100px"/> <Variable name="tabs.background.gradient" description="Tabs Background Gradient" type="url" default="url(https://resources.blogblog.com/blogblog/data/1kt/simple/gradients_light.png)"/> <Variable name="tabs.border.width" description="Tabs Border Width" type="length" default="1px" min="0" max="10px"/> <Variable name="tabs.bevel.border.width" description="Tabs Bevel Border Width" type="length" default="1px" min="0" max="10px"/> <Variable name="post.margin.bottom" description="Post Bottom Margin" type="length" default="25px" min="0" max="100px"/> <Variable name="image.border.small.size" description="Image Border Small Size" type="length" default="2px" min="0" max="10px"/> <Variable name="image.border.large.size" description="Image Border Large Size" type="length" default="5px" min="0" max="10px"/> <Variable name="page.width.selector" description="Page Width Selector" type="string" default=".region-inner"/> <Variable name="page.width" description="Page Width" type="string" default="auto"/> <Variable name="main.section.margin" description="Main Section Margin" type="length" default="15px" min="0" max="100px"/> <Variable name="main.padding" description="Main Padding" type="length" default="15px" min="0" max="100px"/> <Variable name="main.padding.top" description="Main Padding Top" type="length" default="30px" min="0" max="100px"/> <Variable name="main.padding.bottom" description="Main Padding Bottom" type="length" default="30px" min="0" max="100px"/> <Variable name="paging.background" color="#ffffff" description="Background of blog paging area" type="background" default="transparent none no-repeat scroll top center"/> <Variable name="footer.bevel" description="Bevel border length of footer" type="length" default="0" min="0" max="10px"/> <Variable name="mobile.background.overlay" description="Mobile Background Overlay" type="string" default="transparent none repeat scroll top left"/> <Variable name="mobile.background.size" description="Mobile Background Size" type="string" default="auto"/> <Variable name="mobile.button.color" description="Mobile Button Color" type="color" default="#ffffff" /> <Variable name="startSide" description="Side where text starts in blog language" type="automatic" default="left"/> <Variable name="endSide" description="Side where text ends in blog language" type="automatic" default="right"/> */ /* Content ----------------------------------------------- */ body { font: normal normal 12px 'Trebuchet MS', Trebuchet, Verdana, sans-serif; color: #666666; background: #eeeeee none repeat scroll top left; padding: 0 0 0 0; } html body .region-inner { min-width: 0; max-width: 100%; width: auto; } h2 { font-size: 22px; } a:link { text-decoration:none; color: #2b5b84; } a:visited { text-decoration:none; color: #2b5b84; } a:hover { text-decoration:underline; color: #3776ab; } .body-fauxcolumn-outer .fauxcolumn-inner { background: transparent none repeat scroll top left; _background-image: none; } .body-fauxcolumn-outer .cap-top { position: absolute; z-index: 1; height: 400px; width: 100%; } .body-fauxcolumn-outer .cap-top .cap-left { width: 100%; background: transparent none repeat-x scroll top left; _background-image: none; } .content-outer { -moz-box-shadow: 0 0 0 rgba(0, 0, 0, .15); -webkit-box-shadow: 0 0 0 rgba(0, 0, 0, .15); -goog-ms-box-shadow: 0 0 0 #333333; box-shadow: 0 0 0 rgba(0, 0, 0, .15); margin-bottom: 1px; } .content-inner { padding: 10px 40px; } .content-inner { background-color: #ffffff; } /* Header ----------------------------------------------- */ .header-outer { background: #2b5b84 none repeat-x scroll 0 -400px; _background-image: none; } .Header h1 { font: normal normal 40px 'Trebuchet MS',Trebuchet,Verdana,sans-serif; color: #000000; text-shadow: 0 0 0 rgba(0, 0, 0, .2); } .Header h1 a { color: #000000; } .Header .description { font-size: 18px; color: #ffd343; } .header-inner .Header .titlewrapper { padding: 22px 0; } .header-inner .Header .descriptionwrapper { padding: 0 0; } /* Tabs ----------------------------------------------- */ .tabs-inner .section:first-child { border-top: 0 solid #dddddd; } .tabs-inner .section:first-child ul { margin-top: -1px; border-top: 1px solid #dddddd; border-left: 1px solid #dddddd; border-right: 1px solid #dddddd; } .tabs-inner .widget ul { background: transparent none repeat-x scroll 0 -800px; _background-image: none; border-bottom: 1px solid #dddddd; margin-top: 0; margin-left: -30px; margin-right: -30px; } .tabs-inner .widget li a { display: inline-block; padding: .6em 1em; font: normal normal 12px 'Trebuchet MS', Trebuchet, Verdana, sans-serif; color: #000000; border-left: 1px solid #ffffff; border-right: 1px solid #dddddd; } .tabs-inner .widget li:first-child a { border-left: none; } .tabs-inner .widget li.selected a, .tabs-inner .widget li a:hover { color: #000000; background-color: #eeeeee; text-decoration: none; } /* Columns ----------------------------------------------- */ .main-outer { border-top: 0 solid #ffd343; } .fauxcolumn-left-outer .fauxcolumn-inner { border-right: 1px solid #ffd343; } .fauxcolumn-right-outer .fauxcolumn-inner { border-left: 1px solid #ffd343; } /* Headings ----------------------------------------------- */ div.widget > h2, div.widget h2.title { margin: 0 0 1em 0; font: normal bold 11px 'Trebuchet MS',Trebuchet,Verdana,sans-serif; color: #000000; } /* Widgets ----------------------------------------------- */ .widget .zippy { color: #999999; text-shadow: 2px 2px 1px rgba(0, 0, 0, .1); } .widget .popular-posts ul { list-style: none; } /* Posts ----------------------------------------------- */ h2.date-header { font: normal bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif; } .date-header span { background-color: #bbbbbb; color: #ffffff; padding: 0.4em; letter-spacing: 3px; margin: inherit; } .main-inner { padding-top: 35px; padding-bottom: 65px; } .main-inner .column-center-inner { padding: 0 0; } .main-inner .column-center-inner .section { margin: 0 1em; } .post { margin: 0 0 45px 0; } h3.post-title, .comments h4 { font: normal normal 22px 'Trebuchet MS',Trebuchet,Verdana,sans-serif; margin: .75em 0 0; } .post-body { font-size: 110%; line-height: 1.4; position: relative; } .post-body img, .post-body .tr-caption-container, .Profile img, .Image img, .BlogList .item-thumbnail img { padding: 2px; background: #ffffff; border: 1px solid #eeeeee; -moz-box-shadow: 1px 1px 5px rgba(0, 0, 0, .1); -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, .1); box-shadow: 1px 1px 5px rgba(0, 0, 0, .1); } .post-body img, .post-body .tr-caption-container { padding: 5px; } .post-body .tr-caption-container { color: #666666; } .post-body .tr-caption-container img { padding: 0; background: transparent; border: none; -moz-box-shadow: 0 0 0 rgba(0, 0, 0, .1); -webkit-box-shadow: 0 0 0 rgba(0, 0, 0, .1); box-shadow: 0 0 0 rgba(0, 0, 0, .1); } .post-header { margin: 0 0 1.5em; line-height: 1.6; font-size: 90%; } .post-footer { margin: 20px -2px 0; padding: 5px 10px; color: #666666; background-color: #eeeeee; border-bottom: 1px solid #eeeeee; line-height: 1.6; font-size: 90%; } #comments .comment-author { padding-top: 1.5em; border-top: 1px solid #ffd343; background-position: 0 1.5em; } #comments .comment-author:first-child { padding-top: 0; border-top: none; } .avatar-image-container { margin: .2em 0 0; } #comments .avatar-image-container img { border: 1px solid #eeeeee; } /* Comments ----------------------------------------------- */ .comments .comments-content .icon.blog-author { background-repeat: no-repeat; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9sLFwMeCjjhcOMAAAD+SURBVDjLtZSvTgNBEIe/WRRnm3U8RC1neQdsm1zSBIU9VVF1FkUguQQsD9ITmD7ECZIJSE4OZo9stoVjC/zc7ky+zH9hXwVwDpTAWWLrgS3QAe8AZgaAJI5zYAmc8r0G4AHYHQKVwII8PZrZFsBFkeRCABYiMh9BRUhnSkPTNCtVXYXURi1FpBDgArj8QU1eVXUzfnjv7yP7kwu1mYrkWlU33vs1QNu2qU8pwN0UpKoqokjWwCztrMuBhEhmh8bD5UDqur75asbcX0BGUB9/HAMB+r32hznJgXy2v0sGLBcyAJ1EK3LFcbo1s91JeLwAbwGYu7TP/3ZGfnXYPgAVNngtqatUNgAAAABJRU5ErkJggg==); } .comments .comments-content .loadmore a { border-top: 1px solid #999999; border-bottom: 1px solid #999999; } .comments .comment-thread.inline-thread { background-color: #eeeeee; } .comments .continue { border-top: 2px solid #999999; } /* Accents ---------------------------------------------- */ .section-columns td.columns-cell { border-left: 1px solid #ffd343; } .blog-pager { background: transparent url(//www.blogblog.com/1kt/simple/paging_dot.png) repeat-x scroll top center; } .blog-pager-older-link, .home-link, .blog-pager-newer-link { background-color: #ffffff; padding: 5px; } .footer-outer { border-top: 1px dashed #bbbbbb; } /* Mobile ----------------------------------------------- */ body.mobile { background-size: auto; } .mobile .body-fauxcolumn-outer { background: transparent none repeat scroll top left; } .mobile .body-fauxcolumn-outer .cap-top { background-size: 100% auto; } .mobile .content-outer { -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, .15); box-shadow: 0 0 3px rgba(0, 0, 0, .15); } .mobile .tabs-inner .widget ul { margin-left: 0; margin-right: 0; } .mobile .post { margin: 0; } .mobile .main-inner .column-center-inner .section { margin: 0; } .mobile .date-header span { padding: 0.1em 10px; margin: 0 -10px; } .mobile h3.post-title { margin: 0; } .mobile .blog-pager { background: transparent none no-repeat scroll top center; } .mobile .footer-outer { border-top: none; } .mobile .main-inner, .mobile .footer-inner { background-color: #ffffff; } .mobile-index-contents { color: #666666; } .mobile-link-button { background-color: #2b5b84; } .mobile-link-button a:link, .mobile-link-button a:visited { color: #ffffff; } .mobile .tabs-inner .section:first-child { border-top: none; } .mobile .tabs-inner .PageList .widget-content { background-color: #eeeeee; color: #000000; border-top: 1px solid #dddddd; border-bottom: 1px solid #dddddd; } .mobile .tabs-inner .PageList .widget-content .pagelist-arrow { border-left: 1px solid #dddddd; } --></style> <style id='template-skin-1' type='text/css'><!-- body { min-width: 960px; } .content-outer, .content-fauxcolumn-outer, .region-inner { min-width: 960px; max-width: 960px; _width: 960px; } .main-inner .columns { padding-left: 0; padding-right: 310px; } .main-inner .fauxcolumn-center-outer { left: 0; right: 310px; /* IE6 does not respect left and right together */ _width: expression(this.parentNode.offsetWidth - parseInt("0") - parseInt("310px") + 'px'); } .main-inner .fauxcolumn-left-outer { width: 0; } .main-inner .fauxcolumn-right-outer { width: 310px; } .main-inner .column-left-outer { width: 0; right: 100%; margin-left: -0; } .main-inner .column-right-outer { width: 310px; margin-right: -310px; } #layout { min-width: 0; } #layout .content-outer { min-width: 0; width: 800px; } #layout .region-inner { min-width: 0; width: auto; } body#layout div.add_widget { padding: 8px; } body#layout div.add_widget a { margin-left: 32px; } --></style> <script type='text/javascript'> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-55961911-3', 'auto', 'blogger'); ga('blogger.send', 'pageview'); </script> <link href='https://www.blogger.com/dyn-css/authorization.css?targetBlogID=8520&zx=7dac3870-f7be-457f-8232-1b0e440c089b' media='none' onload='if(media!='all')media='all'' rel='stylesheet'/><noscript><link href='https://www.blogger.com/dyn-css/authorization.css?targetBlogID=8520&zx=7dac3870-f7be-457f-8232-1b0e440c089b' rel='stylesheet'/></noscript> <meta name='google-adsense-platform-account' content='ca-host-pub-1556223355139109'/> <meta name='google-adsense-platform-domain' content='blogspot.com'/> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?display=swap&family=Montserrat"></head> <body class='loading'> <div class='navbar section' id='navbar' name='Navbar'><div class='widget Navbar' data-version='1' id='Navbar1'><script type="text/javascript"> function setAttributeOnload(object, attribute, val) { if(window.addEventListener) { window.addEventListener('load', function(){ object[attribute] = val; }, false); } else { window.attachEvent('onload', function(){ object[attribute] = val; }); } } </script> <div id="navbar-iframe-container"></div> <script type="text/javascript" src="https://apis.google.com/js/platform.js"></script> <script type="text/javascript"> gapi.load("gapi.iframes:gapi.iframes.style.bubble", function() { if (gapi.iframes && gapi.iframes.getContext) { gapi.iframes.getContext().openChild({ url: 'https://www.blogger.com/navbar.g?targetBlogID\x3d8520\x26blogName\x3dPython+Software+Foundation+News\x26publishMode\x3dPUBLISH_MODE_BLOGSPOT\x26navbarType\x3dLIGHT\x26layoutType\x3dLAYOUTS\x26searchRoot\x3dhttps://pyfound.blogspot.com/search\x26blogLocale\x3den\x26v\x3d2\x26homepageUrl\x3dhttps://pyfound.blogspot.com/\x26vt\x3d-6418538651529203108', where: document.getElementById("navbar-iframe-container"), id: "navbar-iframe", messageHandlersFilter: gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER, messageHandlers: { 'blogger-ping': function() {} } }); } }); </script><script type="text/javascript"> (function() { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = '//pagead2.googlesyndication.com/pagead/js/google_top_exp.js'; var head = document.getElementsByTagName('head')[0]; if (head) { head.appendChild(script); }})(); </script> </div></div> <div class='body-fauxcolumns'> <div class='fauxcolumn-outer body-fauxcolumn-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <div class='content'> <div class='content-fauxcolumns'> <div class='fauxcolumn-outer content-fauxcolumn-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <div class='content-outer'> <div class='content-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left content-fauxborder-left'> <div class='fauxborder-right content-fauxborder-right'></div> <div class='content-inner'> <header> <div class='header-outer'> <div class='header-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left header-fauxborder-left'> <div class='fauxborder-right header-fauxborder-right'></div> <div class='region-inner header-inner'> <div class='header section' id='header' name='Header'><div class='widget Header' data-version='1' id='Header1'> <div id='header-inner'> <a href='https://pyfound.blogspot.com/' style='display: block'> <img alt='Python Software Foundation News' height='82px; ' id='Header1_headerimg' src='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgD_WIGHsseqGaGnsXVjcNYZ5g6EIb1XoDEmltAoM6Bi6_R9uDS1BPJP9oPJKj1k4I5R8ALUVZZfy4_DLjEDs6i2GxsCDnf3xO5eo54utZQ5E2pyUhtbQGOiiwBrmLU5asONQ/s1600-r/psf-logo.png' style='display: block' width='334px; '/> </a> <div class='descriptionwrapper'> <p class='description'><span>  News from the Python Software Foundation</span></p> </div> </div> </div></div> </div> </div> <div class='header-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </header> <div class='tabs-outer'> <div class='tabs-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left tabs-fauxborder-left'> <div class='fauxborder-right tabs-fauxborder-right'></div> <div class='region-inner tabs-inner'> <div class='tabs no-items section' id='crosscol' name='Cross-Column'></div> <div class='tabs no-items section' id='crosscol-overflow' name='Cross-Column 2'></div> </div> </div> <div class='tabs-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='main-outer'> <div class='main-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left main-fauxborder-left'> <div class='fauxborder-right main-fauxborder-right'></div> <div class='region-inner main-inner'> <div class='columns fauxcolumns'> <div class='fauxcolumn-outer fauxcolumn-center-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='fauxcolumn-outer fauxcolumn-left-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='fauxcolumn-outer fauxcolumn-right-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <!-- corrects IE6 width calculation --> <div class='columns-inner'> <div class='column-center-outer'> <div class='column-center-inner'> <div class='main section' id='main' name='Main'><div class='widget Blog' data-version='1' id='Blog1'> <div class='blog-posts hfeed'> <div class="date-outer"> <h2 class='date-header'><span>Thursday, June 27, 2024</span></h2> <div class="date-posts"> <div class='post-outer'> <div class='post hentry uncustomized-post-template' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='8520' itemprop='blogId'/> <meta content='1681543785645287726' itemprop='postId'/> <a name='1681543785645287726'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='https://pyfound.blogspot.com/2024/06/announcing-psf-board-candidates-for-2024.html'>Announcing the PSF Board Candidates for 2024!</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-1681543785645287726' itemprop='description articleBody'> <p>What an exciting list! Please take a look at who is running for the PSF Board this year on the <a href="https://www.python.org/nominations/elections/2024-python-software-foundation-board/nominees/" target="_blank">PSF Board Election 2024 Nominees page</a>. This year there are 3 seats open on the PSF board. You can see who is currently on the board on the <a href="https://www.python.org/psf/records/board/history/" target="_blank">PSF Officers & Directors page</a>. (D茅bora Azevedo, Kwon-Han Bae, and Tania Allard are at the end of their current terms.)<br /></p><h3 style="text-align: left;">Board Election Timeline</h3><ul style="text-align: left;"><li>Nominations open: Tuesday, June 11th, 2:00 pm UTC</li><li>Nomination cut-off: Tuesday, June 25th, 2:00 pm UTC</li><li>Voter application/affirmation cut-off date: Tuesday, June 25th, 2:00 pm UTC</li><li>Announce candidates: Thursday, June 27th</li><li>Voting start date: Tuesday, July 2nd, 2:00 pm UTC</li><li>Voting end date: Tuesday, July 16th, 2:00 pm UTC</li></ul><p>Not sure what UTC is for you locally? Check using this <a href="https://savvytime.com/converter/utc" target="_blank">timezone converter</a>! </p><h3 style="text-align: left;">Voting</h3><p>Voting opens on Tuesday, July 2nd at 2:00 pm UTC, through Tuesday, July 16th, 2024 2:00 pm UTC. Check the <a href="https://www.python.org/nominations/elections/" target="_blank">Elections page</a> to see how much time you have left to vote. </p><p>If you are a voting member of the PSF that affirmed your intention to participate in this year’s election, you will receive an email from “OpaVote Voting Link <noreply@opavote.com>” with your ballot, the subject line will read “Python Software Foundation Board of Directors Election 2024”. If you haven’t seen your ballot by Wednesday, please first check your spam folder for a message from “noreply@opavote.com”. If you don’t see anything get in touch by emailing psf-elections@python.org so we can look into your account and make sure we have the most up-to-date email for you.<br /></p><p>If you have questions about your membership status or the election, please email psf-elections@python.org. You are welcome to join the discussion about the PSF Board election on the <a href="https://discuss.python.org/c/python-software-foundation/9" target="_blank">PSF Discuss forum</a>.</p> <div style='clear: both;'></div> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'> <span class='post-author vcard'> Posted by <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://www.blogger.com/profile/15138793242213676112' itemprop='url'/> <a class='g-profile' href='https://www.blogger.com/profile/15138793242213676112' rel='author' title='author profile'> <span itemprop='name'>Marie Nordin</span> </a> </span> </span> <span class='post-timestamp'> at <meta content='https://pyfound.blogspot.com/2024/06/announcing-psf-board-candidates-for-2024.html' itemprop='url'/> <a class='timestamp-link' href='https://pyfound.blogspot.com/2024/06/announcing-psf-board-candidates-for-2024.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2024-06-27T14:57:00-04:00'>6/27/2024 02:57:00 PM</abbr></a> </span> <span class='post-comment-link'> </span> <span class='post-icons'> <span class='item-control blog-admin pid-964068386'> <a href='https://www.blogger.com/post-edit.g?blogID=8520&postID=1681543785645287726&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <div class='post-share-buttons goog-inline-block'> </div> </div> <div class='post-footer-line post-footer-line-2'> <span class='post-labels'> </span> </div> <div class='post-footer-line post-footer-line-3'> <span class='post-location'> </span> </div> </div> </div> </div> </div></div> <div class="date-outer"> <h2 class='date-header'><span>Tuesday, June 25, 2024</span></h2> <div class="date-posts"> <div class='post-outer'> <div class='post hentry uncustomized-post-template' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='8520' itemprop='blogId'/> <meta content='2323569769861106171' itemprop='postId'/> <a name='2323569769861106171'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='https://pyfound.blogspot.com/2024/06/faq-for-proposed-changes-to-psf-bylaws.html'>FAQ for Proposed Changes to PSF Bylaws</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-2323569769861106171' itemprop='description articleBody'> <p style="text-align: left;"><span style="font-family: arial; font-size: small;">As part of the PSF Board Election, there are <a href="https://pyfound.blogspot.com/2024/06/for-your-consideration-proposed-bylaws.html" target="_blank">three proposed changes to the PSF Bylaws</a> which will be on the upcoming ballot. We have received a significant amount of feedback relating to proposed change #3 (allowing for the removal of Fellows by a majority vote of the Board of Directors). We have been working on a response, which has taken us some time because we want to be as transparent as possible, and there have been many questions raised over the last week or so. Thank you for voicing feedback and your patience with us while we worked on our response.</span><span style="font-family: arial;"><br /><br /><span style="font-size: small;">Please keep in mind that as we are the group who is responsible for the health of the Foundation, we need to be conscious about our statements. There may be direct consequences for our words, and some of the specifics are legally privileged.</span><br /><br /><span style="font-size: small;">The broad categories of questions we’ve included are about the importance of this change, alternatives that were considered and rejected, how the Board is structured against abuse of this change, and a few general questions.</span><br /><br /><span style="font-size: small;">If your specific question isn’t answered here, please join the PSF Board Election Bylaws Office Hour session on Thursday June 27th at 1PM UTC in the <a href="https://discord.gg/XZRseGBpwb" target="_blank">#psf-elections channel on the PSF Discord</a>. You can also email your questions to either psf-elections@pyfound.org or membership-wg@pyfound.org or by responding to the <a href="https://discuss.python.org/t/for-your-consideration-proposed-bylaws-changes-to-improve-our-membership-experience/55696/1" target="_blank">For your consideration: Proposed bylaws changes to improve our membership experience thread</a> on the PSF Discuss forum.</span><br /><br /><span style="font-size: small;">We hope that our transparency, the Office Hour session, and our responses in the FAQ below encourage you to vote in favor of all three of the proposed Bylaw changes.</span><br /><br /><span style="font-size: small;">With thanks,</span><br /><br /><span style="font-size: small;">- The PSF Board of Directors</span></span></p><p style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;"><span style="background-color: transparent; color: black; font-family: arial; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span></p><h2 style="text-align: left;"><span style="font-family: arial;">Importance of a measure like this <br /></span></h2><h4 style="text-align: left;"><span style="font-family: arial;"> </span></h4><h4 style="text-align: left;"><span style="font-family: arial;">Q: Why is this even necessary? What kind of legal advice did you ask for? <br /></span></h4><p><span style="font-family: arial; font-size: small;"><b>A:</b> The Board has a responsibility to act in the interests of the Foundation. Our legal counsel has advised us of a possibility where knowingly allowing a bad actor to continue to operate with our implied endorsement would open us up to certain kinds of liability. Our Bylaws do not currently have a mechanism for revoking the Fellow designation, and this change is intended to close that gap.<br /><br />The text of all three Bylaws changes were proposed or vetted by our legal counsel, and we are confident that the text as proposed allows us to act according to the intent we described in our original post.</span></p><h4 style="text-align: left;"><span style="font-family: arial;">Q: Is this in response to a specific event?<br /></span></h4><p><span style="font-family: arial;"><b>A:</b> The Board needs to balance transparency with our duty to act in the best interest of the Foundation. We can’t discuss any events that would hypothetically lead to removing a Fellow, or even whether there have even been any events that would warrant a removal, because releasing details — or even the existence — of investigations where we failed to remove a fellow would open us up to the possibility of liability.</span></p><h4 style="text-align: left;"><span style="font-family: arial;"><br />Q: Does the Board stand by this amendment? Was this a majority or a unanimous decision?<br /></span></h4><p><span style="font-family: arial;"><b>A:</b> The board voted unanimously in favor of this amendment, 10 in favor, 0 against, 0 abstentions. While the Bylaws do not allow proxy votes to be formally counted, both Board members who could not attend the meeting also explicitly registered their support for the amendment with other Board members.<br /></span></p><h4 style="text-align: left;"><span style="font-family: arial;">Q: Why can’t we publicly discuss Fellows who have received complaints and decide together as a community? <br /></span></h4><p><span style="font-family: arial;"><b>A:</b> Some people will not make Code of Conduct reports if they are likely to go to a large public discussion or are unlikely to be acted on. The current lifelong nature of the Fellow designation has created a special group of people who are functionally exempt from the effects of the Code of Conduct.<br /></span></p><h4 style="text-align: left;"><span style="font-family: arial;">Q: Does the Board retain legal counsel?<br /></span></h4><p><span style="font-family: arial;"><b>A: </b>Even though there is no longer a full-time General Counsel as part of the Board of Directors, the PSF retains legal counsel. Legal advice was sought, provided, and followed as part of this amendment process.<br /></span></p><h4 style="text-align: left;"><span style="font-family: arial;">Q: How do changes to the Bylaws work?<br /></span></h4><p><span style="font-family: arial;"><b>A:</b> The PSF’s Bylaws define the legal structure of the Foundation, the Membership, and how the organization is governed. If an aspect of Membership or the Board’s ability to make decisions needs to be changed, the Bylaws need to be changed. This usually happens by discussion amongst the Board, working groups, or even directly from the Membership, resulting in a proposal to amend the Bylaws.<br /><br />To change the Bylaws, the Board must vote on a proposed amendment, and then the Membership usually has the opportunity to vote to approve those changes. As an organization that represents a diverse community, we strongly value the consent and community buy-in that comes from a vote. <br /><br />There are other ways to change the bylaws, including not seeking Member approval, or by Membership seeking Bylaws changes without Board approval, but these have never been used.<br /></span></p><h2 style="text-align: left;"><span style="font-family: arial;">Alternatives considered and rejected<br /></span></h2><h4 style="text-align: left;"><span style="font-family: arial;"> </span></h4><h4 style="text-align: left;"><span style="font-family: arial;">Q: Why does this only require a majority vote, not a supermajority?<br /></span></h4><p><span style="font-family: arial;"><b>A:</b> This amendment as written already requires a higher standard than most business the Board handles. It requires a majority of the full Board of Directors, not merely a majority of the quorum (as is otherwise required in Section 5.8). <br /><br />With the current size of the Board, a majority is 7 Directors, and a supermajority (two thirds) is 8. The Board would be open to amending the requirements to a supermajority in the future, but we wish to highlight how small the difference would be in practice.<br /></span></p><h4 style="text-align: left;"><span style="font-family: arial;">Q: Why does this not require a unanimous vote?<br /></span></h4><p><span style="font-family: arial;"><b>A:</b> A unanimous vote requirement would create scope for abuse. A unanimous vote requirement would allow for a single dissenting Director to prevent the removal of a problematic Fellow, opening the Foundation to liability. <br /><br />In certain cases, that liability could extend to individual Directors, even those who voted to remove the Fellow, simply because the action failed to pass due to one dissenting vote (subject to the provisions of Article XII “Limits on Liability of Directors”). <br /><br />This personal liability would be a significant barrier to many community members' willingness to serve on the Board.<br /></span></p><h2 style="text-align: left;"><span style="font-family: arial;">Protections against misuse<br /></span></h2><h4 style="text-align: left;"><span style="font-family: arial;"> </span></h4><h4 style="text-align: left;"><span style="font-family: arial;">Q: Why does the Board need to act responsibly?<br /></span></h4><p><span style="font-family: arial;"><b>A:</b> The Board needs to act in the service of the Foundation’s mission, and has a responsibility to the community to keep vital infrastructure like PyPI running, providing fiscal sponsorship to community groups like PyLadies chapters, or giving grants to international communities. <br /><br />Acting against the interests of the membership would cause the community to lose trust in us, and threaten our ability to keep Python’s infrastructure running.<br /></span></p><h4 style="text-align: left;"><span style="font-family: arial;">Q: What protections are available to prevent the Board from misusing this provision?<br /></span></h4><p><span style="font-family: arial;"><b>A:</b> This proposed Bylaws amendment requires a Member to fail a “condition of membership” to be removed. Such a condition would need to have been previously enacted by the Board and would apply to any Member in that class of Membership. This prevents the Board from removing a Member arbitrarily.<br /><br />The Membership regularly holds the Board accountable through annual elections. Should there be an immediate need to act, the Membership can call a special meeting of the Board or the Membership and hold the Board to account. The proposed change allows a removed Member 15 business days before their removal is final, during which time they can use the tools available to hold the Board to account.<br /><br /></span></p><h4 style="text-align: left;"><span style="font-family: arial;">Q: What if a future board becomes controlled by a large group of untrustworthy and like-minded people?<br /></span></h4><p><span style="font-family: arial;"><b>A:</b> The Board is elected in cohorts, such that usually only 3-4 seats are open each year. Any “hostile takeover” would need to be conducted over the course of a few years and could not be engineered by any company or other formal entity because we already have rules preventing Board capture in <a href="https://www.python.org/psf/bylaws/" target="_blank">our Bylaws</a> (section 5.15). <br /><br />“No more than one quarter (1/4) of the members of the Board of Directors may share a common affiliation.” <br /></span></p><h2 style="text-align: left;"><span style="font-family: arial;">Other questions<br /></span></h2><h4 style="text-align: left;"><span style="font-family: arial;"> </span></h4><h4 style="text-align: left;"><span style="font-family: arial;">Q: Why does this offer the possibility of covering non-Code of Conduct policies?<br /></span></h4><p><span style="font-family: arial;"><b>A:</b> The Amendment gives the Board the right to add other qualifications to membership, and the Bylaws do not prevent the Board from amending the Code of Conduct (<a href="https://pyfound.blogspot.com/2019/09/the-python-software-foundation-has_24.html" target="_blank">and we have done so multiple times before</a>). If we were to change the Bylaws such that the only policy that allowed us to remove members was the Code of Conduct, this would have the perverse effect of incentivizing the Board to amend the Code of Conduct to cover other cases where removing a Member may be a necessity. This would make the Code of Conduct too long, complicated, and unfocused to be effective in the cases where it is already used.<br /></span></p><h4 style="text-align: left;"><span style="font-family: arial;">Q: Why did the Board single out Fellows in the announcement?<br /></span></h4><p><span style="font-family: arial;"><b>A: </b>It is true that the amended text covers all classes of Membership, however, in practice the only truly new ability granted to the board is being able to remove Fellows. <br /><br />The text of the Bylaws already grants mechanisms that could allow the Board to make Members in other classes ineligible for Membership, including the ability to set “alternate eligibility criteria” (section 4.6-4.7) beyond those in the Bylaws or an “applicable membership fee” (section 4.5). The only class of membership for which there is no way to restrict eligibility on existing Members are the Fellows, who are granted life membership, except if they are removed by a vote of the Membership. This amendment makes it possible to remove Members, no matter which class, using the same tools.<br /></span></p><h4 style="text-align: left;"><span style="font-family: arial;">Q: Who comprises the Code of Conduct Work Group? Is it diverse? Are they accepting new members?<br /></span></h4><p><span style="font-family: arial;"><b>A:</b> The current membership and the past members of the Conduct Work Group are listed in the charter which can be found on the <a href="https://wiki.python.org/psf/ConductWG/Charter" target="_blank">Code of Conduct Work Group Charter page</a>. The group consists of diverse representatives from around the world. The charter lists the process for applying to join the Work Group.<br /></span></p> <div style='clear: both;'></div> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'> <span class='post-author vcard'> Posted by <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://www.blogger.com/profile/06173618710147431813' itemprop='url'/> <a class='g-profile' href='https://www.blogger.com/profile/06173618710147431813' rel='author' title='author profile'> <span itemprop='name'>Deb Nicholson</span> </a> </span> </span> <span class='post-timestamp'> at <meta content='https://pyfound.blogspot.com/2024/06/faq-for-proposed-changes-to-psf-bylaws.html' itemprop='url'/> <a class='timestamp-link' href='https://pyfound.blogspot.com/2024/06/faq-for-proposed-changes-to-psf-bylaws.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2024-06-25T13:38:00-04:00'>6/25/2024 01:38:00 PM</abbr></a> </span> <span class='post-comment-link'> </span> <span class='post-icons'> <span class='item-control blog-admin pid-1723525585'> <a href='https://www.blogger.com/post-edit.g?blogID=8520&postID=2323569769861106171&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <div class='post-share-buttons goog-inline-block'> </div> </div> <div class='post-footer-line post-footer-line-2'> <span class='post-labels'> </span> </div> <div class='post-footer-line post-footer-line-3'> <span class='post-location'> </span> </div> </div> </div> </div> </div></div> <div class="date-outer"> <h2 class='date-header'><span>Friday, June 14, 2024</span></h2> <div class="date-posts"> <div class='post-outer'> <div class='post hentry uncustomized-post-template' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjBl46EDVOvlYeI9mLrRk0O66qjd4kt3EOUIUcTF1dBwJibsg8IuJV2YnG3ssJzQpnqlKtGfEGeIt4WMKlC4zy36CkHaIUpbFSG_NLSPM9YL6GFU9cUoewDn1A2IY8hWfGEtIfY9IHo1-sihJA8rwbS2cTwiNKKK_cpvhflDM16G-Inhf6zw/s320/20240515_140951.jpg' itemprop='image_url'/> <meta content='8520' itemprop='blogId'/> <meta content='6575827673653005818' itemprop='postId'/> <a name='6575827673653005818'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-pyrepl-new-default-repl-for-python.html'>The Python Language Summit 2024: PyREPL -- New default REPL written in Python</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-6575827673653005818' itemprop='description articleBody'> <p></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjBl46EDVOvlYeI9mLrRk0O66qjd4kt3EOUIUcTF1dBwJibsg8IuJV2YnG3ssJzQpnqlKtGfEGeIt4WMKlC4zy36CkHaIUpbFSG_NLSPM9YL6GFU9cUoewDn1A2IY8hWfGEtIfY9IHo1-sihJA8rwbS2cTwiNKKK_cpvhflDM16G-Inhf6zw/s4000/20240515_140951.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="3000" data-original-width="4000" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjBl46EDVOvlYeI9mLrRk0O66qjd4kt3EOUIUcTF1dBwJibsg8IuJV2YnG3ssJzQpnqlKtGfEGeIt4WMKlC4zy36CkHaIUpbFSG_NLSPM9YL6GFU9cUoewDn1A2IY8hWfGEtIfY9IHo1-sihJA8rwbS2cTwiNKKK_cpvhflDM16G-Inhf6zw/s320/20240515_140951.jpg" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Lysandros showing the mistake we've all made, no longer a problem in the new REPL<br />(Photo credit: Hugo van Kemenade)<br /></td></tr></tbody></table><br /><p></p><p>One of the <a href="https://docs.python.org/3.13/whatsnew/3.13.html#whatsnew313-better-interactive-interpreter">headline features of Python 3.13</a> is the new interactive interpreter, sometimes known as a "REPL" (Read-Evaluate-Print-Loop) which was <a href="https://github.com/python/cpython/issues/111201">contributed</a> by Pablo Galindo Salgado, 艁ukasz Langa, and Lysandros Nikolaou and based on the <a href="https://www.pypy.org/">PyPy project</a>'s own interactive interpreter, PyREPL. Pablo, 艁ukasz, and Lysandros all were at the Language Summit 2024 to present about this new feature coming to Python.<br /></p><h3 style="text-align: left;">Why does Python need a new interpreter?</h3><p style="text-align: left;">Python already has an interactive interpreter, so why do we need a new one? Lysandros explained that the existing interpreter is "deeply tangled" to Python's tokenizer which means adding new features or making changes is extremely difficult.</p><p style="text-align: left;"></p><p></p><p style="text-align: left;">To lend further color to this point, Lysandros dug into how the tokenizer had changed since Python was first developed. Lysandros noted that "for the first 12 years [of Python], Guido was the only one who touched the tokenizer" and only later after the parser was replaced did anyone else meaningfully contribute to the tokenizer.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgN0vlG40dCudDiNJmJkmJie-v5L2tj14bf56BZhVh5Y5FgoXstLPTRJGizg-HUNSiTT-y5PdbDc8SH2xxl6PLptEeMLmZp-rtaJOWj1MXblEoAnZNsrOecNMOItPrG766TSY4TWVkMak2geSMt0o8wwHdiu1ffVrVGYdamclMh2tFkdeRIRg/s1195/Screenshot%20from%202024-05-29%2014-23-55.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="709" data-original-width="1195" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgN0vlG40dCudDiNJmJkmJie-v5L2tj14bf56BZhVh5Y5FgoXstLPTRJGizg-HUNSiTT-y5PdbDc8SH2xxl6PLptEeMLmZp-rtaJOWj1MXblEoAnZNsrOecNMOItPrG766TSY4TWVkMak2geSMt0o8wwHdiu1ffVrVGYdamclMh2tFkdeRIRg/s320/Screenshot%20from%202024-05-29%2014-23-55.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Terse example code for Python's tokenizer<br /></td></tr></tbody></table><p style="text-align: left;">Meanwhile, there are other REPLs for Python that "have many new features that [Python's] interpreter doesn't have that users have grown to expect", Lysandros explained. Some basic features that were listed as examples included lack of color support meaning no syntax highlighting, the ergonomics issues around exit versus exit(), no support for multi-line editing and buffer history, and poor ergonomics around pasting code into the interpreter.</p><h3 style="text-align: left;">Why PyREPL? <br /></h3><p style="text-align: left;">"We've settled on starting our solution around PyREPL", Pablo explained, "our reasoning being that maintaining terminal applications is hard. Starting from scratch would have a much higher risk for users". Pablo also noted that "most people who would interact with the REPL wouldn't test in betas", because Python pre-releases are generally used for running automated tests in continuous integration and not interactively tested manually.</p><p style="text-align: left;">Pablo explained that there are many different terminals and platforms which are all sources of behaviors and bugs that are hard to get right the first time. "[PyREPL] provided us with a solid base that we know works and we can start modifying". <br /></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEEE9xoCPFmO1z93-3b68hgQIGUNzt-XTJucAyskU3NzK84cPoG9WkYaW6gwki9xmuqSujNZ9hzPNzzk_Qj9bAY8dUNjmZrl5C98PJpxWbuGrwbyt6mzs8eH2zooeZxbTJ2yG4mdzydMXfufk61kSRarwu2bey9Id3MH4Rw2E5MFboedhF_w/s1439/Screenshot%20from%202024-05-29%2014-58-33.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="812" data-original-width="1439" height="181" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEEE9xoCPFmO1z93-3b68hgQIGUNzt-XTJucAyskU3NzK84cPoG9WkYaW6gwki9xmuqSujNZ9hzPNzzk_Qj9bAY8dUNjmZrl5C98PJpxWbuGrwbyt6mzs8eH2zooeZxbTJ2yG4mdzydMXfufk61kSRarwu2bey9Id3MH4Rw2E5MFboedhF_w/s320/Screenshot%20from%202024-05-29%2014-58-33.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Tasteful modern art or bug in the REPL?<br /></td></tr></tbody></table><p>Another major contributing factor was that PyREPL is written in Python. Pablo emphasized that "now people that want to start contributing to the REPL can actually contribute because it's written in Python".<br /></p><p style="text-align: left;">Finally, Pablo pointed out that because the implementation is now partially shared between CPython and PyPy that both implementations can benefit from bug fixes to the shared parts of the codebase. Support for Chinese characters in the REPL was fixed in CPython and is being contributed back to PyPy.</p><p style="text-align: left;">艁ukasz noted that adopting PyREPL wasn't a straightforward copy-paste job, there were multiple ideas in PyPy's PyREPL that don't make sense for CPython. Notably, PyPy is written to also support Python 2, so the code was simplified to only handle Python 3 code. PyREPL for PyPy also came with support for PyGame which wasn't necessary for CPython.<br /></p><p style="text-align: left;">Type hints and strict type checking using <a href="https://mypy-lang.org/">mypy</a> were also added to PyREPL, making the PyREPL module the <a href="https://github.com/python/cpython/tree/main/Lib/_pyrepl">first in the Python standard library</a> to be type-checked on pull requests. Adding type hints to the code immediately found bugs which were fixed and reported back to PyPy.</p><h3 style="text-align: left;">What are the new features in 3.13?<br /></h3><p style="text-align: left;">Pablo gave a demonstration of the new features of PyREPL, including:</p><ul style="text-align: left;"><li style="text-align: left;">Colored prompts</li><li style="text-align: left;">F1 for help, F3 for bracketed paste</li><li style="text-align: left;">Multi-line editing and history</li><li style="text-align: left;">Better support for pasting blocks of code<br /> </li></ul><p style="text-align: left;">Below are some recreated highlights from the demo. Pasting code samples into the old REPL that contain multiple newlines would often result in SyntaxErrors due to multiple newlines in a row resulting in that statement being evaluated. Multi-line editing also helps modifying code all in one place rather than having to piece a snippet together line-by-line, modifying what you want as you go:<br /></p><p style="text-align: left;"></p><p></p><p style="text-align: left;"></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOmyWRdLgCGdtM4Kvz4j5A4qh6_7t-2Jei0ArpdDmvlnWupnnIgsnG-aeekdkq9_9qcKN1F7Quu-ZweP3SMEEt5Kpo-Bl6j_sqZHcTq-VDmNlqbS8uW9SJa3GlvE6VGGo8joyXaHqVH5HpFuGyBlSbVxJwn5o-gQagi-WuSFhENUahSwl0_Q/s987/try4.gif" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="588" data-original-width="987" height="382" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOmyWRdLgCGdtM4Kvz4j5A4qh6_7t-2Jei0ArpdDmvlnWupnnIgsnG-aeekdkq9_9qcKN1F7Quu-ZweP3SMEEt5Kpo-Bl6j_sqZHcTq-VDmNlqbS8uW9SJa3GlvE6VGGo8joyXaHqVH5HpFuGyBlSbVxJwn5o-gQagi-WuSFhENUahSwl0_Q/w640-h382/try4.gif" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Demo of multi-line paste in Python 3.13</td></tr></tbody></table> <p></p><p style="text-align: left;">And the "exit versus exit()" paper-cut has been bothering Python users for long enough. This error was especially taunting because the REPL clearly knows what your intent is with it's helpful message to "Use exit() to exit":<br /></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgWz3R7HEvuVpbMSpHID-9JWA2sGqwTzAO3zXfHJ7fCv3_pWB7cSSQkyLuzRo1Jxr98OOnzXNh4kh00zDh_0wIQfruSsVJl0V0tGZMIe-JZ0IdE5LG45RGOILYOPb3yvWNP7Ab03QPpQVH5FVh-7v1cuVmC5Q-oRhU9u26JnB-ef4zbTlVOA/s987/try3.gif" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="308" data-original-width="987" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgWz3R7HEvuVpbMSpHID-9JWA2sGqwTzAO3zXfHJ7fCv3_pWB7cSSQkyLuzRo1Jxr98OOnzXNh4kh00zDh_0wIQfruSsVJl0V0tGZMIe-JZ0IdE5LG45RGOILYOPb3yvWNP7Ab03QPpQVH5FVh-7v1cuVmC5Q-oRhU9u26JnB-ef4zbTlVOA/w640-h200/try3.gif" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">"exit" without parenthesis just works, finally!<br /></td></tr></tbody></table><h3 style="text-align: left;">Windows and terminals</h3><p style="text-align: left;">Support is already available for Unix consoles (Linux and macOS) in Python 3.13.0-beta1 and the standout feature request so far for PyREPL has been Windows support. Windows was left out because "historically the console on Windows was way different than Unix consoles". 艁ukasz continued, saying that "they don't intend to support right now" offering a "yes, but..." for users asking for Windows support.</p><p style="text-align: left;">Windows has two consoles today, cmd.exe of yore and the new "Windows Terminal" which supports many of the same features as Unix consoles including <a href="https://en.wikipedia.org/wiki/ANSI_escape_code">VT100 escape codes</a>. The team's plan is to support the new Windows Terminal, and "to use our sprints here in Pittsburgh to finish". Windows support will also require removing CPython dependencies on the <a href="https://en.wikipedia.org/wiki/Curses_(programming_library)">curses</a> and <a href="https://en.wikipedia.org/wiki/GNU_Readline">readline</a> libraries.</p><h3 style="text-align: left;">What's next for PyREPL?</h3><p style="text-align: left;">The team already has plans cooking up for what to add to the REPL in Python 3.14. 艁ukasz commented that "syntax highlighting is an obvious idea to tackle". 艁ukasz also referenced an idea from Tania Allard for accessibility improvements similar to those in <a href="https://ipython.org/">IPython</a>.</p><p style="text-align: left;">艁ukasz reiterated that the goal isn't to make an "uber REPL" or "replace IPython", but instead to make a REPL that core developers can use while testing development branches (where dependencies aren't working yet).</p><p style="text-align: left;">艁ukasz continued that core developers aren't the only ones that these improvements benefit: "many teachers are using straight-up Python, IDLE, or the terminal because the computers they're using don't allow them to install anything else."</p><p style="text-align: left;">Given the applause from the room during the demos, it's safe to say that this work has been received well. There were only concerns about platform support and rollout for the new REPL.</p><p style="text-align: left;">Gregory Smith informed the team that functionality that requires a "Function" key (ie F1, F2, etc) must also be supported without Function keys due to some computers lacking them, like Chromebooks. <br /></p><p style="text-align: left;">Carol Willing was concerned about releasing PyREPL without support for Windows Terminal, especially from a teaching perspective, describing that potential outcome as "painful". Carol wanted clear documentation on how to get the new REPL on Windows. "Positioning [the new REPL] for teaching without clear Windows instructions is a recipe for disaster".</p><p style="text-align: left;">Pablo assured that the team wants to add support for Windows Terminal in time for the first 3.13 release candidate. Pablo could not make guarantees due to a lack of Windows expertise among the three, saying "the reason I'm not saying 100% is because none of us are Windows experts. We understand what needs to be done... but we need some help."</p><p style="text-align: left;">艁ukasz named Steve Dower, the Windows release expert for Python, who is "very motivated to help us get Windows Terminal support during sprints". 艁ukasz reiterated they're "not 100%, but we are very motivated to get it done".</p><p style="text-align: left;">Gregory Smith shared Carol's concern and framed the problem as one of communication strategy, proposing to "not promise too much until it works completely on Windows". By Python 3.14 the flashy features like syntax highlighting would have landed and the team would have a better understanding of what's needed for Windows. The team can revise the 3.13 "What's New in Python" depending on what gets implemented in the 3.13 timeline.</p><p style="text-align: left;">Ned Deily sought to clarify what the default experience would be for users of 3.13. Pablo said that "on Windows right now you will get the [same REPL] that you got before" and "on Linux and macOS, if your terminal supports the features which most of them do, you get the enhanced experience". "What we want in the sprints is to make Windows support the new one, if we get feature parity, then [Windows] will also get the new [REPL]".</p><p style="text-align: left;">Carol also asked to document how to opt-out of the new REPL in the case that support wasn't added in time for 3.13 to avoid differences between educational material and what students were seeing in their terminal. Kushal Das confirmed that differences across platforms is a source of problems for students, saying that "if all [students] have the same experience it's much better than just improving only macOS and Linux" to avoid students feeling bad just due to their operating system.<br /></p><p style="text-align: left;">Pablo said that the opt-out mechanism was already in place with an environment variable and will discuss other opt-out mechanisms if needed for educators.</p><p style="text-align: left;">Emily Morehouse, speaking as a Steering Council member added that the Steering Council has requested an informational PEP on the new REPL. "Hearing concerns about how [the new REPL] might be rolled out... it sounds like we might need something that's more compatible and an easier rollout", leaving the final discussions to the 3.13 release manager, Thomas Wouters. Carol replied that she believes "we could do it in documentation".</p> <div style='clear: both;'></div> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'> <span class='post-author vcard'> Posted by <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://www.blogger.com/profile/16555309043643874359' itemprop='url'/> <a class='g-profile' href='https://www.blogger.com/profile/16555309043643874359' rel='author' title='author profile'> <span itemprop='name'>Seth Michael Larson</span> </a> </span> </span> <span class='post-timestamp'> at <meta content='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-pyrepl-new-default-repl-for-python.html' itemprop='url'/> <a class='timestamp-link' href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-pyrepl-new-default-repl-for-python.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2024-06-14T09:26:00-04:00'>6/14/2024 09:26:00 AM</abbr></a> </span> <span class='post-comment-link'> </span> <span class='post-icons'> <span class='item-control blog-admin pid-743697153'> <a href='https://www.blogger.com/post-edit.g?blogID=8520&postID=6575827673653005818&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <div class='post-share-buttons goog-inline-block'> </div> </div> <div class='post-footer-line post-footer-line-2'> <span class='post-labels'> </span> </div> <div class='post-footer-line post-footer-line-3'> <span class='post-location'> Location: <a href='https://maps.google.com/maps?q=1000+Fort+Duquesne+Blvd,+Pittsburgh,+PA+15222,+USA@40.4455472,-79.9962844&z=10' target='_blank'>1000 Fort Duquesne Blvd, Pittsburgh, PA 15222, USA</a> </span> </div> </div> </div> </div> <div class='post-outer'> <div class='post hentry uncustomized-post-template' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6C2DnH9UGEb6_Pw9mqAfrYQ_BrwUoLz2NF9-A-yvI36ZV6Hzow2QVeO7sc8h3WWb7KKETX29AlHIspgfyyTfbTFaRK4_X0ssxxvgzHv5j3LlYFZYBJyJ_jmxgb6-NOOvqgCzCZXYHOEM3EL30PMyeDtO8zVF9t4D2smoInQ3zbrUHJDvXLw/s320/53746006345_2f7efd86d0_k.jpg' itemprop='image_url'/> <meta content='8520' itemprop='blogId'/> <meta content='5765322055155947103' itemprop='postId'/> <a name='5765322055155947103'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024.html'>The Python Language Summit 2024</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-5765322055155947103' itemprop='description articleBody'> <p>The Python Language Summit occurs every year just before PyCon US begins, this year occurring on May 15th, 2024 in Pittsburgh, Pennsylvania. The summit is attended by core developers, triagers, and Python implementation maintainers for a full day of talks and discussions on the future direction of Python.</p><p>This years summit included talks on the C API, free-threading, the security model of Python post-xz, and Python on mobile platforms.</p><p>This year's summit was attended by around 45 people and was covered by Seth Larson.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6C2DnH9UGEb6_Pw9mqAfrYQ_BrwUoLz2NF9-A-yvI36ZV6Hzow2QVeO7sc8h3WWb7KKETX29AlHIspgfyyTfbTFaRK4_X0ssxxvgzHv5j3LlYFZYBJyJ_jmxgb6-NOOvqgCzCZXYHOEM3EL30PMyeDtO8zVF9t4D2smoInQ3zbrUHJDvXLw/s2048/53746006345_2f7efd86d0_k.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1368" data-original-width="2048" height="214" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6C2DnH9UGEb6_Pw9mqAfrYQ_BrwUoLz2NF9-A-yvI36ZV6Hzow2QVeO7sc8h3WWb7KKETX29AlHIspgfyyTfbTFaRK4_X0ssxxvgzHv5j3LlYFZYBJyJ_jmxgb6-NOOvqgCzCZXYHOEM3EL30PMyeDtO8zVF9t4D2smoInQ3zbrUHJDvXLw/s320/53746006345_2f7efd86d0_k.jpg" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Attendees of the Python Language Summit 2024<br />(Photo credit: Kushal Das)<br /></td></tr></tbody></table><p></p><ul style="text-align: left;"><li><a href="https://pyfound.blogspot.com/2024/06/python-language-summit-2024-should-python-adopt-calver.html">Should Python adopt Calendar Versioning?</a>: talk by Hugo van Kemenade<br /></li><li><a href="https://pyfound.blogspot.com/2024/06/python-language-summit-2024-python-security-model-after-xz.html">Python's security model after the xz-utils backdoor</a>: talk by Pablo Galindo Salgado<br /></li><li><a href="https://pyfound.blogspot.com/2024/06/python-language-summit-2024-c-api.html">Native Interface and Limited C API</a>: talks by Petr Viktorin and Victor Stinner<br /></li><li><a href="https://pyfound.blogspot.com/2024/06/python-language-summit-2024-free-threading-ecosystems.html">Free-threading ecosystems</a>: talk by Daniele Parmeggiani<br /></li><li><a href="https://pyfound.blogspot.com/2024/06/python-language-summit-2024-python-on-mobile.html">Python on Mobile</a>: talk by Malcolm Smith</li><li><a href="https://pyfound.blogspot.com/2024/06/python-language-summit-2024-pyrepl-new-default-repl-for-python.html">PyREPL -- New default REPL written in Python</a>: talk by Pablo Galindo Salgado, 艁ukasz Langa, and Lysandros Nikolaou</li><li><a href="https://pyfound.blogspot.com/2024/06/python-language-summit-2024-pyrepl-new-pdb.html">Should we make pdb better?</a>: talk by Tian Gao<br /></li><li><a href="https://pyfound.blogspot.com/2024/06/python-language-summit-2024-limiting-yield-in-async-generators.html">Limiting yield in async generators</a>: talk by Zac Hatfield-Dodds</li><li><a href="https://pyfound.blogspot.com/2024/06/python-language-summit-2024-annotations-as-transforms.html">Annotations as Transforms</a>: talk by Jason R. Coombs <br /></li><li><a href="https://pyfound.blogspot.com/2024/06/python-language-summit-2024-lightning-talks.html">Lightning Talks</a>, featuring talks by Petr Viktorin, David Hewitt, Emily Morehouse, 艁ukasz Langa, Pablo Galindo Salgado, and Yury Selivanov</li></ul><p> </p><p> </p><p> </p><p> </p><p> </p> <div style='clear: both;'></div> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'> <span class='post-author vcard'> Posted by <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://www.blogger.com/profile/16555309043643874359' itemprop='url'/> <a class='g-profile' href='https://www.blogger.com/profile/16555309043643874359' rel='author' title='author profile'> <span itemprop='name'>Seth Michael Larson</span> </a> </span> </span> <span class='post-timestamp'> at <meta content='https://pyfound.blogspot.com/2024/06/python-language-summit-2024.html' itemprop='url'/> <a class='timestamp-link' href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2024-06-14T09:20:00-04:00'>6/14/2024 09:20:00 AM</abbr></a> </span> <span class='post-comment-link'> </span> <span class='post-icons'> <span class='item-control blog-admin pid-743697153'> <a href='https://www.blogger.com/post-edit.g?blogID=8520&postID=5765322055155947103&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <div class='post-share-buttons goog-inline-block'> </div> </div> <div class='post-footer-line post-footer-line-2'> <span class='post-labels'> </span> </div> <div class='post-footer-line post-footer-line-3'> <span class='post-location'> Location: <a href='https://maps.google.com/maps?q=1000+Fort+Duquesne+Blvd,+Pittsburgh,+PA+15222,+USA@40.4455472,-79.9962844&z=10' target='_blank'>1000 Fort Duquesne Blvd, Pittsburgh, PA 15222, USA</a> </span> </div> </div> </div> </div> <div class='post-outer'> <div class='post hentry uncustomized-post-template' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvkJZG2Hef6Q1_gBbnMa8D8-2vH2pZutdxA2dwMf0ptLy5hVYWPqlqVe6p5Bo1H-E2GL3G0ocf58y89LambQpnac-4Hl6SeGBkQj-4TO6wlqkmp5DCT_fJbzSTuN-4bo3ARtbIsw-C1sDrlOdkRkdGkzoeBZwYENDfZ2JqpWOI6bK9My_H-A/s320/20240515_163830.jpg' itemprop='image_url'/> <meta content='8520' itemprop='blogId'/> <meta content='1192046665792335868' itemprop='postId'/> <a name='1192046665792335868'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-lightning-talks.html'>The Python Language Summit 2024: Lightning Talks</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-1192046665792335868' itemprop='description articleBody'> <p>The Python Language Summit 2024 closed off with six lightning talks which were all submitted during the Language Summit. The talks were delivered by Petr Viktorin, David Hewitt, Emily Morehouse, 艁ukasz Langa, Pablo Galindo Salgado, and Yury Selivanov.</p><p></p><h2 style="text-align: left;">Petr Viktorin: Unsupported build warning</h2><p style="text-align: left;">Do you know what happens when you build Python on an unsupported platform?</p><p style="text-align: left;">"... <i>It works!</i>" -- Thomas Wouters<br></p><p style="text-align: left;">Petr gave a short presentation on a warning that many folks using Python (and even developing Python!) may have never seen before: the unsupported build warning. This warning appears when building on a platform that's <a href="https://peps.python.org/pep-0011/#support-tiers">not officially supported by CPython</a>, for example "riscv64-unknown-linux-gnu".</p><p style="text-align: left;"></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvkJZG2Hef6Q1_gBbnMa8D8-2vH2pZutdxA2dwMf0ptLy5hVYWPqlqVe6p5Bo1H-E2GL3G0ocf58y89LambQpnac-4Hl6SeGBkQj-4TO6wlqkmp5DCT_fJbzSTuN-4bo3ARtbIsw-C1sDrlOdkRkdGkzoeBZwYENDfZ2JqpWOI6bK9My_H-A/s4000/20240515_163830.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="3000" data-original-width="4000" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvkJZG2Hef6Q1_gBbnMa8D8-2vH2pZutdxA2dwMf0ptLy5hVYWPqlqVe6p5Bo1H-E2GL3G0ocf58y89LambQpnac-4Hl6SeGBkQj-4TO6wlqkmp5DCT_fJbzSTuN-4bo3ARtbIsw-C1sDrlOdkRkdGkzoeBZwYENDfZ2JqpWOI6bK9My_H-A/s320/20240515_163830.jpg" width="320"></a></td></tr><tr><td class="tr-caption" style="text-align: center;">"The platform is not supported, use at your own risk"<br>(Photo credit: Hugo van Kemenade)<br></td></tr></tbody></table> <p></p><p style="text-align: left;">Just because a platform isn't officially supported by CPython doesn't mean it won't work on that platform, and indeed it's likely that CPython may work fine on the platform or a subset of features may be subtly or not-so-subtly broken or unavailable.<br></p><p style="text-align: left;">Petr wanted to get a temperature check from the group on whether this warning could be further improved or changed, such as by hiding the warning after the user had executed the test suite or showing the number of tests that had failed.<br></p><p style="text-align: left;">The room seemed mostly uninterested in exploring this topic further and was in favor of keeping the warning as-is.<br></p><span></span> <div style='clear: both;'></div> </div> <div class='jump-link'> <a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-lightning-talks.html#more' title='The Python Language Summit 2024: Lightning Talks'>Read more »</a> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'> <span class='post-author vcard'> Posted by <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://www.blogger.com/profile/16555309043643874359' itemprop='url'/> <a class='g-profile' href='https://www.blogger.com/profile/16555309043643874359' rel='author' title='author profile'> <span itemprop='name'>Seth Michael Larson</span> </a> </span> </span> <span class='post-timestamp'> at <meta content='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-lightning-talks.html' itemprop='url'/> <a class='timestamp-link' href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-lightning-talks.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2024-06-14T09:13:00-04:00'>6/14/2024 09:13:00 AM</abbr></a> </span> <span class='post-comment-link'> </span> <span class='post-icons'> <span class='item-control blog-admin pid-743697153'> <a href='https://www.blogger.com/post-edit.g?blogID=8520&postID=1192046665792335868&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <div class='post-share-buttons goog-inline-block'> </div> </div> <div class='post-footer-line post-footer-line-2'> <span class='post-labels'> </span> </div> <div class='post-footer-line post-footer-line-3'> <span class='post-location'> Location: <a href='https://maps.google.com/maps?q=1000+Fort+Duquesne+Blvd,+Pittsburgh,+PA+15222,+USA@40.4455472,-79.9962844&z=10' target='_blank'>1000 Fort Duquesne Blvd, Pittsburgh, PA 15222, USA</a> </span> </div> </div> </div> </div> <div class='post-outer'> <div class='post hentry uncustomized-post-template' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8fHzQDwNx56z5HZwjnd1Ayl4pLCYhE5w36gzvc0mpEj2sMLzVi9U9Dp1XVdx_3wUAj0wtlG6quczL1nepB-lS1xMrPUfAYAlg1y7xjiNCeAZ3SlAx2kXKDUz_Bp8hki18lvXyRHIMHR1ggucHcMJaXMP_YfwXeARCo2PjsQQBGQl6E-7AvA/w400-h145/Screenshot%20from%202024-06-05%2017-09-59.png' itemprop='image_url'/> <meta content='8520' itemprop='blogId'/> <meta content='2299622449761716565' itemprop='postId'/> <a name='2299622449761716565'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-annotations-as-transforms.html'>The Python Language Summit 2024: Annotations as Transformers</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-2299622449761716565' itemprop='description articleBody'> <p>The final talk of the main schedule of the Python Language Summit was delivered by Jason R. Coombs on using annotations for transforms. The presentation was accompanied by a <a href="https://github.com/jaraco/transformers/blob/main/Annotations%20as%20transforms.md">GitHub repository</a> and <a href="https://github.com/jaraco/transformers/blob/main/Annotations%20as%20transforms.ipynb">Jupyter notebook</a> illustrating the problem and proposed solution.</p><p>Jason is interested in a method for users to "transform their parameters in a reusable way". The motivation was to avoid imperative methods of transforming parameters to "increase reusability, composition, and separation of concerns". Jason imagined transformers which could be "packaged up in a library or used across multiple functions" and would "be applied at the scope of individual parameters".</p><p>Python already has a language feature that's <i>similar</i> to this concept with <a href="https://docs.python.org/3/reference/compound_stmts.html#function-definitions">decorators</a>, which allow wrapping a function or class with another function in a syntactically concise way.</p><p>Jason noted that "return values can be handled by decorators fairly easily, so [the proposal] is more concerned with input parameters". For a decorator to affect parameters, the decorator "would have to inspect the parameters" and "entangle itself with the function signature".</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8fHzQDwNx56z5HZwjnd1Ayl4pLCYhE5w36gzvc0mpEj2sMLzVi9U9Dp1XVdx_3wUAj0wtlG6quczL1nepB-lS1xMrPUfAYAlg1y7xjiNCeAZ3SlAx2kXKDUz_Bp8hki18lvXyRHIMHR1ggucHcMJaXMP_YfwXeARCo2PjsQQBGQl6E-7AvA/s1479/Screenshot%20from%202024-06-05%2017-09-59.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="537" data-original-width="1479" height="145" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8fHzQDwNx56z5HZwjnd1Ayl4pLCYhE5w36gzvc0mpEj2sMLzVi9U9Dp1XVdx_3wUAj0wtlG6quczL1nepB-lS1xMrPUfAYAlg1y7xjiNCeAZ3SlAx2kXKDUz_Bp8hki18lvXyRHIMHR1ggucHcMJaXMP_YfwXeARCo2PjsQQBGQl6E-7AvA/w400-h145/Screenshot%20from%202024-06-05%2017-09-59.png" width="400" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Diagram from Jason's presentation showing transforms being applied to individual parameters of a function.<br /></td></tr></tbody></table><p></p><p>Jason's proposal would use type annotations due to type annotations already specifying the desired type, the proposal being to add behavior "this is the type I want to make this" and perform transforms. Below is some example code of the proposal:<br /><br /><span style="font-family: courier;">def transformer(val: float | None) -> float:<br /> return val if val is not None else 0<br /><br />def make_str(val: float) -> str:<br /> return str(val)<br /><br />def my_fn(<br /> p1: transformer,<br /> p2: transformer<br />) -> make_str:<br /><br /> return (p1 ** 2) + p2</span></p><p>Jason went on to show that <a href="https://docs.pydantic.dev">Pydantic</a> was offering something similar to his proposal by having functions called on parameters and return values using the <a href="https://docs.pydantic.dev/latest/concepts/validators/">pydantic.BeforeValidator</a> class in conjunction with typing.Annotated, though this use-case "wasn't being advertised by Pydantic":</p><p><span style="font-family: courier;">from typing import Annotated<br />import pydantic<br /><br />def transformer(val: float | None) -> float:<br /> return val if val is not None else 0<br /><br />@pydantic.validate_call(validate_return=True)<br />def my_fn(<br /> p1: Annotated[float, pydantic.BeforeValidator(transformer)],<br /> p2: Annotated[float, pydantic.BeforeValidator(transformer)]<br />) -> Annotated[str, pydantic.BeforeValidator(str)]:<br /><br /> return (p1 ** 2) + p2<br /></span><br /></p><p>Jason didn't like this approach though due to the verbosity, requiring to use a decorator and provide annotations, and needing an extra dependency.</p><p>Eric V. Smith asked if Jason had seen <a href="https://peps.python.org/pep-0712/">PEP 712</a>, which Eric is the sponsor of, that describes a "converter" mechanism for <a href="https://docs.python.org/3/library/dataclasses.html">dataclass</a> fields. This mechanism was similar in that "the type you annotated something with became different to the type you passed". Eric remarked it was "pretty common thing that people want to pass in different types when they're constructing something than the internal types of the class".<br /></p><p>Jason replied that he had seen the PEP but "hadn't incorporated it into a larger strategy yet". Steering council member Barry Warsaw noted that he "didn't know what the solution is, but it is interesting... that the problems are adjacent".</p><p>There was skepticism from the room, including from typing council member Guido van Rossum, on using type annotations as the mechanism for transformers. Type annotations today don't affect the runtime behavior of the code and this proposal would be a departure from that, Guido noting "process-wise, that's going to be a difficult hurdle".</p><p>If type annotations weren't the way forwards, Jason had also considered proposing new syntax or a new language feature and wanted feedback on whether "there's viability" in that approach and if so, "[he] could explore those options".</p><p>There were questions about why decorators weren't sufficient, citing <a href="https://peps.python.org/pep-0318/">PEP 318</a> motivation section containing examples similar to the ones Jason had presented. Transformers could be assigned to parameters by name, passing in the transformer as a key-value parameters into the decorator like so:</p><p><span style="font-family: courier;">def transformer(val: float | None) -> float:<br /> return val if val is not None else 0<br /></span></p><p><span style="font-family: courier;">@apply(p1=transformer, p2=transformer)<br />def my_fn(<br /> p1: float,<br /> p2: float<br />) -> float:<br /><br /> return (p1 ** 2) + p2</span></p><p>Jason found this pattern "discouraging" and "less elegant" because the variable name needs to mentioned in multiple places and that he was "hoping for something that was more integrated into the language, to not feel like a second-class feature".</p><p>艁ukasz Langa commented on the case for removing the "None" type from a union, could already be done with a <a href="https://typing.readthedocs.io/en/latest/spec/narrowing.html#typeguard">type guard</a> and drew attention to work being done to allow more complicated type guards. 艁ukasz was "sympathetic to conciseness, but type checkers already handle this".</p><p>Steering Council member Gregory Smith was hesitant to make any change in this area. He agreed that "as a language, we're missing something", but "wasn't sure if we've got a way forward that doesn't make the language more complicated".<br /></p> <div style='clear: both;'></div> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'> <span class='post-author vcard'> Posted by <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://www.blogger.com/profile/16555309043643874359' itemprop='url'/> <a class='g-profile' href='https://www.blogger.com/profile/16555309043643874359' rel='author' title='author profile'> <span itemprop='name'>Seth Michael Larson</span> </a> </span> </span> <span class='post-timestamp'> at <meta content='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-annotations-as-transforms.html' itemprop='url'/> <a class='timestamp-link' href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-annotations-as-transforms.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2024-06-14T09:13:00-04:00'>6/14/2024 09:13:00 AM</abbr></a> </span> <span class='post-comment-link'> </span> <span class='post-icons'> <span class='item-control blog-admin pid-743697153'> <a href='https://www.blogger.com/post-edit.g?blogID=8520&postID=2299622449761716565&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <div class='post-share-buttons goog-inline-block'> </div> </div> <div class='post-footer-line post-footer-line-2'> <span class='post-labels'> </span> </div> <div class='post-footer-line post-footer-line-3'> <span class='post-location'> Location: <a href='https://maps.google.com/maps?q=1000+Fort+Duquesne+Blvd,+Pittsburgh,+PA+15222,+USA@40.4455472,-79.9962844&z=10' target='_blank'>1000 Fort Duquesne Blvd, Pittsburgh, PA 15222, USA</a> </span> </div> </div> </div> </div> <div class='post-outer'> <div class='post hentry uncustomized-post-template' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWtaKiTPJWFEv1diOaZHdyHIGqG98xfoACzbLVRN_Yi7v-f789hOQDH_zEfdlGO19RGb1gCgcA__rtD46ocU7e6XrT4CjMdHSlWnosWERYTOUu5f8YJPi5mpGf0MgMMKUE0TsR7gdfmM_XBfWFQ78op7vRku1CcTkYygheYLPwEN0VNCs1iA/s320/20240515_155647.jpg' itemprop='image_url'/> <meta content='8520' itemprop='blogId'/> <meta content='926968286645162950' itemprop='postId'/> <a name='926968286645162950'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-limiting-yield-in-async-generators.html'>The Python Language Summit 2024: Limiting Yield in Async Generators</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-926968286645162950' itemprop='description articleBody'> <p></p><p>Zac Hatfield-Dodds came to the Language Summit to present on a fundamental incompatability between the popular async programming paradigm "<a href="https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful">structured concurrency</a>" and <a href="https://peps.python.org/pep-0525/">asynchronous generators</a>, specifically when it came to exception handling when the two were mixed together.</p><h3 style="text-align: left;">Structured Concurrency <br /></h3><p>Structured concurrency is becoming more popular for Python async programming like with <a href="https://trio.readthedocs.io">Trio</a> "nurseries" and in the Python standard library with the addition of <a href="https://docs.python.org/3/library/asyncio-task.html#task-groups">asyncio.TaskGroup</a> in Python 3.11.</p><p>When using structured concurrency, active tasks can be thought of as a tree-like structure where sub-tasks of a parent task have to exit before the parent task itself can proceed past a pre-defined scope. This exit can come through all the tasks completing successfully or from an exception being raised either internally or externally (for example, in the case of a timeout on time-bounded work).</p><p>The mechanism which allows a parent task and its sub-tasks to cooperate in this way is called a "cancel scope" which Trio makes a <a href="https://trio.readthedocs.io/en/stable/reference-core.html#trio.CancelScope">top-level concept</a> but is implicitly used in asyncio.TaskGroup and asyncio.timeout.</p><p>Async programs that are structured with this paradigm can rely on exceptions behaving in a much more recognizable way. There's no more danger of a spawned sub-task silently swallowing an exception because all sub-tasks are guaranteed to be checked for their status before the parent task can exit.<br /></p><h3 style="text-align: left;">The problem with yields <br /></h3><p>The fundamental issue is that <a href="https://docs.python.org/3/reference/expressions.html#yield-expressions">yields</a> suspend the current call frame, in effect "returning" a value, and then the generator needs to be "called" again for execution to be resumed. This suspension doesn't play well with structured concurrency because execution can't be suspended in the same call frame as a cancel scope, otherwise that scope can't process exceptions from its child tasks.</p><p><br /></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWtaKiTPJWFEv1diOaZHdyHIGqG98xfoACzbLVRN_Yi7v-f789hOQDH_zEfdlGO19RGb1gCgcA__rtD46ocU7e6XrT4CjMdHSlWnosWERYTOUu5f8YJPi5mpGf0MgMMKUE0TsR7gdfmM_XBfWFQ78op7vRku1CcTkYygheYLPwEN0VNCs1iA/s4000/20240515_155647.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="3000" data-original-width="4000" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWtaKiTPJWFEv1diOaZHdyHIGqG98xfoACzbLVRN_Yi7v-f789hOQDH_zEfdlGO19RGb1gCgcA__rtD46ocU7e6XrT4CjMdHSlWnosWERYTOUu5f8YJPi5mpGf0MgMMKUE0TsR7gdfmM_XBfWFQ78op7vRku1CcTkYygheYLPwEN0VNCs1iA/s320/20240515_155647.jpg" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Zac leading a "fun game of 'why is this code broken?'"<br />(Photo credit: Hugo van Kemenade)<br /></td></tr></tbody></table><p>Zac presented some innocuous looking code samples that suffered from the described issue:<br /></p><pre><span class="k" style="font-family: courier;">async</span><span style="font-family: courier;"> <span class="k">def</span> <span class="nf">iter_with_timeout</span><span class="p">(</span><span class="n">ait</span><span class="p">,</span> <span class="n">max_time</span><span class="p">):</span> <span class="k">try</span><span class="p">:</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="k">with</span> asyncio.<span class="n">timeout</span><span class="p">(</span><span class="n">max_time</span><span class="p">):</span> <span class="k">yield</span> <span class="k">await</span> <span class="n">anext</span><span class="p">(</span><span class="n">ait</span><span class="p">)</span> <span class="k">except</span> <span class="ne">StopAsyncIteration</span><span class="p">:</span> <span class="k">return</span> <span class="k">async</span> <span class="k">def</span> <span class="nf">fn</span><span class="p">():</span> <span class="k">async</span> <span class="k">for</span> <span class="n">elem</span> <span class="ow">in</span> <span class="n">iter_with_timeout</span><span class="p">(</span><span class="n">ait</span><span class="p">,</span> <span class="n">max_time</span><span class="o">=</span><span class="mf">1.0</span><span class="p">):</span> <span class="k">await</span> <span class="n">do_something_with</span><span class="p">(</span><span class="n">elem</span><span class="p">)</span></span></pre><p></p><p>In this example, asyncio.timeout() could expire while the yield had suspended the generator and before the generator was resumed. This scenario would result in the cancellation exception being raised <i>in the outer task</i> outside of the asyncio.timeout() cancel scope. If things had gone to plan and the generator wasn't suspended the cancellation would be caught by asyncio.timeout() instead and execution would proceed.</p><p>Zac presented the following fix to the iter_with_timeout() function:</p><pre><span class="k" style="font-family: courier;">async</span><span style="font-family: courier;"> <span class="k">def</span> <span class="nf">iter_with_timeout</span><span class="p">(</span><span class="n">ait</span><span class="p">,</span> <span class="n">max_time</span><span class="p">):</span> <span class="k">try</span><span class="p">:</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="k">with</span> asyncio.<span class="n">timeout</span><span class="p">(</span><span class="n">max_time</span><span class="p">):</span> tmp = <span class="k">await</span> <span class="n">anext</span><span class="p">(</span><span class="n">ait</span><span class="p">)</span> <br /> yield tmp # Move yield outside the cancel scope!<br /> <span class="k">except</span> <span class="ne">StopAsyncIteration</span><span class="p">:</span> <span class="k">return</span></span></pre><p></p><p>By moving the yield outside the cancellation scope it means that the suspension of the frame isn't happening when execution is inside a cancellation scope. This means that propagation of cancellation errors can't be subverted by a suspended call frame for this program.<br /></p><p>If you're still having trouble understanding the problem: you are not alone. There was a refrain of "still with me?" coming from Zac throughout this talk. I recommend looking at the <a href="https://peps.python.org/pep-0789/#problem-statement">problem statement</a> and <a href="https://peps.python.org/pep-0789/#motivating-examples">motivating examples</a> in the PEP for more information. <br /></p><h3 style="text-align: left;">Where to go from here<br /></h3><p></p><p>Zac and Nathaniel Smith have coauthored <a href="https://peps.python.org/pep-0789">PEP 789</a> with their proposed solution of disallowing yield statements within context managers that behave like cancel scopes. Attempting to yield within these scopes would instead raise a RuntimeError.<br /></p><p>The mechanism would be using a new function "<span style="font-family: courier;">sys.prevents_yields()</span>" which would be used by authors of async frameworks to annotate context managers which can't be suspended safely. Users of async frameworks wouldn't need to change their code unless it contained the unwanted behavior.</p><p>The language would need to support this feature by adding metadata to call frames to track whether the current frame should allow yields to occur.<br /></p><p>Mark Shannon was concerned that the solution was "lots of machinery to handle the exception being raised in the wrong place" and sought clarification that there would be overhead added to every call and return. Zac confirmed this would be the case, but that it could be done with "one integer [member on call frames] that you increment and decrement, but it would do some operation on every frame call and return".<br /></p><p>Irit Katriel asked why a "runtime error" was being used "instead of something static". Zac explained that users might define their own context managers which have a "cancel scope property" and the runtime "wouldn't know statically whether a given context manager should raise an error or not".</p><p>艁ukasz Langa asked whether adding a type annotation to context managers would be sufficient to avoid adding runtime overhead. Zac responded that "there are still many users that don't use static type checking", and that "there's no intention to make it required by default". 艁ukasz was concerned that the proposal "would be contentious for runtime performance" due to the impact being "non-trivial".</p><p>Pablo Galindo Salgado wanted to explore other big ideas to avoid the performance penalty like adding new syntax or language feature, such as "with noyield" to provide a static method of avoiding the issue. Zac agreed that changing the context manager protocol could also be a solution.<br /></p><p>Guido van Rossum lamented that this was "yet another demonstration that async generators were a bridge too far. Could we have a simpler PEP that proposes to deprecate and eventually remove from the language asynchronous generators, just because they're a pain and tend to spawn more complexity".</p><p>Zac had no objections to a PEP deprecating async generators¹. Zac continued, "while static analysis is helpful in some cases, there are inevitably cases that it misses which kept biting us... until we banned all async generators in our codebase".<br /><br />¹ Editors note: after the summit <a href="https://peps.python.org/pep-0789/#deprecate-async-generators-entirely">an update to PEP 789</a> would describe how the problem doesn't exist <i>solely</i> in async generators and thus removal of the feature wouldn't solve the problem, either.</p> <div style='clear: both;'></div> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'> <span class='post-author vcard'> Posted by <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://www.blogger.com/profile/16555309043643874359' itemprop='url'/> <a class='g-profile' href='https://www.blogger.com/profile/16555309043643874359' rel='author' title='author profile'> <span itemprop='name'>Seth Michael Larson</span> </a> </span> </span> <span class='post-timestamp'> at <meta content='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-limiting-yield-in-async-generators.html' itemprop='url'/> <a class='timestamp-link' href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-limiting-yield-in-async-generators.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2024-06-14T09:13:00-04:00'>6/14/2024 09:13:00 AM</abbr></a> </span> <span class='post-comment-link'> </span> <span class='post-icons'> <span class='item-control blog-admin pid-743697153'> <a href='https://www.blogger.com/post-edit.g?blogID=8520&postID=926968286645162950&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <div class='post-share-buttons goog-inline-block'> </div> </div> <div class='post-footer-line post-footer-line-2'> <span class='post-labels'> </span> </div> <div class='post-footer-line post-footer-line-3'> <span class='post-location'> Location: <a href='https://maps.google.com/maps?q=1000+Fort+Duquesne+Blvd,+Pittsburgh,+PA+15222,+USA@40.4455472,-79.9962844&z=10' target='_blank'>1000 Fort Duquesne Blvd, Pittsburgh, PA 15222, USA</a> </span> </div> </div> </div> </div> <div class='post-outer'> <div class='post hentry uncustomized-post-template' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcwAFtvp4eyohqY1hi5IaCGZhjtHli8UuGnZsbcxIXojkJa-jtv8tcbGcpKfKT0sj78TdARW7h6lm3McZu1lxcutLw6SQx45ME7UL0mt7mEqkQRIoJBdtowo1QmH8ffA9tMvJH1iGo_qHjkXvuDCFzaoHYl58080Hf_IUiIvJ1igj9-kNxlg/s320/20240515_144421.jpg' itemprop='image_url'/> <meta content='8520' itemprop='blogId'/> <meta content='8126192978921924850' itemprop='postId'/> <a name='8126192978921924850'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-pyrepl-new-pdb.html'>The Python Language Summit 2024: Should we make pdb better?</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-8126192978921924850' itemprop='description articleBody'> <p>Tian Gao came to the Language Summit 2024 to talk about improving <a href="https://docs.python.org/3/library/pdb.html">pdb</a>, short for "Python debugger", a module and command line tool for debugging Python.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcwAFtvp4eyohqY1hi5IaCGZhjtHli8UuGnZsbcxIXojkJa-jtv8tcbGcpKfKT0sj78TdARW7h6lm3McZu1lxcutLw6SQx45ME7UL0mt7mEqkQRIoJBdtowo1QmH8ffA9tMvJH1iGo_qHjkXvuDCFzaoHYl58080Hf_IUiIvJ1igj9-kNxlg/s4000/20240515_144421.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="3000" data-original-width="4000" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcwAFtvp4eyohqY1hi5IaCGZhjtHli8UuGnZsbcxIXojkJa-jtv8tcbGcpKfKT0sj78TdARW7h6lm3McZu1lxcutLw6SQx45ME7UL0mt7mEqkQRIoJBdtowo1QmH8ffA9tMvJH1iGo_qHjkXvuDCFzaoHYl58080Hf_IUiIvJ1igj9-kNxlg/s320/20240515_144421.jpg" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Tian Gao presenting on how to improve pdb<br /></td></tr></tbody></table><p>There are not many command-line debugger alternatives to pdb for Python. Tian mentioned a few, including PuDB, pdb++, and ipdb, but those alternatives are all themselves based on either <a href="https://docs.python.org/3/library/pdb.html">pdb</a> or another standard library module '<a href="https://docs.python.org/3/library/bdb.html">bdb</a>'.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4A1ZLNLEbvfhdtXCqgVcSpmgLAwTxzsJQLRe1pcRi17Cq2qbzDYX5VisUdHjl3oAsuuLT6-0LeVwHdw73iFfa6xOeOqSzep1bSVJdaiMOFcYec_vSqLJWO79rOF8rnV8mS9mi8mDImum7m-uSHGGAAVAweGiKMsBI3Cm0OlEIe40CIdh-5g/s1370/Screenshot%20from%202024-06-03%2017-27-48.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="773" data-original-width="1370" height="181" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4A1ZLNLEbvfhdtXCqgVcSpmgLAwTxzsJQLRe1pcRi17Cq2qbzDYX5VisUdHjl3oAsuuLT6-0LeVwHdw73iFfa6xOeOqSzep1bSVJdaiMOFcYec_vSqLJWO79rOF8rnV8mS9mi8mDImum7m-uSHGGAAVAweGiKMsBI3Cm0OlEIe40CIdh-5g/s320/Screenshot%20from%202024-06-03%2017-27-48.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">pdb is the only "standalone" command-line-based Python debugger<br /></td></tr></tbody></table><br /><p>Tian presented a laundry list of desirable new features that could be added to pdb, including:<br /></p><ul style="text-align: left;"><li>Showing more lines of code around the current breakpoint.</li><li>Colors in the terminal, syntax highlighting.</li><li>Customization, with defaults being safe.<br /></li><li>Handling of more scenarios (threads, asyncio, bytecode, remote debugging)<br /></li></ul><h3 style="text-align: left;">Performance and backwards compatibility <br /></h3><p>The biggest issue according to Tian, which he noted had been discussed in the past, was the performance of pdb. "pdb is slow because sys.trace is slow, which is something we cannot change", and the only way forward on making pdb faster is to switch to sys.monitoring to avoid triggering unnecessary events.</p><p>Switching to sys.monitoring would give a big boost to performance. According to Tian, "setting a breakpoint in your code in the worst case you get a 100x slowdown compared to almost zero overhead with sys.monitoring". Unfortunately, switching isn't so easy, Tian noted there are serious backwards compatibility concerns for the standard library module <a href="https://docs.python.org/3/library/bdb.html">bdb</a> if pdb were to start using sys.monitoring.<br /></p><p>"If we're not ready to [switch to sys.monitoring] yet, would we ever do this in the future?", Tian asked the group, noting that an alternative is to create a third-party library and encourage folks to use that library instead. </p><p>Thomas Wouters started off saying that "bdb is a standard library module and it cannot break user code" and cautioned that core developers don't know who is depending on modules. bdb's interface can't have backwards incompatible changes without long deprecation periods. In Thomas' mind, "the answer is obvious, leave pdb as it is and build something else".</p><p>Thomas also noted "in the long-term, a debugger in the standard library is important" but that development doesn't need to happen in the standard library. Thomas listed the benefits for developing a new debugger outside the standard library like being able to publish outside the Python release schedule and to use the debugger with older Python versions. Once a debugger reaches a certain level of stability it can be added to the standard library and potentially replace pdb.</p><p>Tian agreed with Thomas' proposal in theory, but was concerned that a third-party debugger on PyPI wouldn't see the same levels of adoption compared to being in the standard library and thus would struggle to meet a threshold of "stability" without a critical mass of users. Or worse yet, maintainers wouldn't be motivated to continue due to a lack of use, resulting in a "dead project". (Some foreshadowing, Steering Council member Emily Morehouse <a href="https://pyfound.blogspot.com/2024/06/python-language-summit-2024-lightning-talks.html">gave a lightning talk on this topic</a> later on in the Language Summit)</p><p>艁ukasz Langa noted that Python now has support for "<a href="https://docs.python.org/3/library/functions.html#breakpoint">breakpoint()</a>" and that "what breakpoint() actually does, we can change. We can run another debugger if we decide to", referencing if a better debugger was added in the future to CPython that it could be made into a new default for breakpoints.<br /></p><p>Russell Keith-Magee from BeeWare, was interested in what Tian had said about remote debugging, noting that "remote debugging is the only way you can debug [on mobile platforms]". Russell would be interested in pdb or a new debugger supporting this use-case. Tian noted that unfortunately remote debugging would be one of the more difficult features to implement.</p><p></p><p>Pablo Galindo Salgado, commenting on existing Python "attach-to-process" debuggers, said that the hacks in use today are "extremely unsafe". Pablo said that "we'd need something inside CPython [to be safe], but then you have another problem, you have to implement that feature on [all platforms]". Pablo also mentioned that attach-to-process debugging is usually a bad model because it can't be enabled by default for security reasons but "you won't know when you'll need to debug".</p><p>Anthony Shaw asked about the scope of the project and was interested in whether there could be a framework for debugging in CPython that pdb and others could build on. Anthony pointed out that many other debuggers "needed to do a bunch of hooks and tricks" to do debugging because it's "not provided out of the box by CPython".</p><p>Tian responded that "bdb is supposed to do that, but it was written 30 years ago so is too old to support new things that a debugger wants". Others mentioned that <a href="https://docs.python.org/3/library/sys.monitoring.html">sys.monitoring</a> (new in Python 3.12) was meant to be a framework for debuggers to build on.</p><p></p><p>Gregory Smith, Steering Council member, said he "wants all of these things" and agreed with Thomas to "develop this as much as you can... outside of the standard library", telling Tian that "you're going to end up in a better state that way". Greg's primary concern was whether CPython needed to do anything to enable Tian's proposal. He continued, "it sounds like we (CPython) have most of what we need, but if we don't let's get that planned so we can enable a successful separate project before we ship it with Python in the future".<br /></p> <div style='clear: both;'></div> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'> <span class='post-author vcard'> Posted by <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://www.blogger.com/profile/16555309043643874359' itemprop='url'/> <a class='g-profile' href='https://www.blogger.com/profile/16555309043643874359' rel='author' title='author profile'> <span itemprop='name'>Seth Michael Larson</span> </a> </span> </span> <span class='post-timestamp'> at <meta content='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-pyrepl-new-pdb.html' itemprop='url'/> <a class='timestamp-link' href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-pyrepl-new-pdb.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2024-06-14T09:13:00-04:00'>6/14/2024 09:13:00 AM</abbr></a> </span> <span class='post-comment-link'> </span> <span class='post-icons'> <span class='item-control blog-admin pid-743697153'> <a href='https://www.blogger.com/post-edit.g?blogID=8520&postID=8126192978921924850&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <div class='post-share-buttons goog-inline-block'> </div> </div> <div class='post-footer-line post-footer-line-2'> <span class='post-labels'> </span> </div> <div class='post-footer-line post-footer-line-3'> <span class='post-location'> Location: <a href='https://maps.google.com/maps?q=1000+Fort+Duquesne+Blvd,+Pittsburgh,+PA+15222,+USA@40.4455472,-79.9962844&z=10' target='_blank'>1000 Fort Duquesne Blvd, Pittsburgh, PA 15222, USA</a> </span> </div> </div> </div> </div> <div class='post-outer'> <div class='post hentry uncustomized-post-template' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixExyJnspFUzIv-sCDG25YGQznrvIlXm5E-McCWSTT9yi86L7-jUcjj432lFz52wLq80wkEgX6U_7hFiWY_VnZPiPVNRZBB4qDKFQuXYfFCGlDn2sQ6lgjuiMG0X6SryKr0gG11X6ebw8AfLjsj2F0LLGnynRph33i0xPbejo5HBTjGs5TFg/s320/Screenshot%20from%202024-05-28%2016-17-02.png' itemprop='image_url'/> <meta content='8520' itemprop='blogId'/> <meta content='7757275864788771480' itemprop='postId'/> <a name='7757275864788771480'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-python-on-mobile.html'>The Python Language Summit 2024: Python on Mobile</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-7757275864788771480' itemprop='description articleBody'> <p>Malcolm Smith from BeeWare presented on the status and direction of Python on mobile platforms like iOS and Android. BeeWare has been working on bringing Python to mobile for a few years now. Previously Russell Keith-Magee gave a talk at the <a href="https://pyfound.blogspot.com/2023/05/the-python-language-summit-2023-python.html">Language Summit in 2023 on BeeWare</a> to announce plans for <a href="https://peps.python.org/pep-0011/#tier-3">Tier 3 support</a> for Python on Android and iOS in Python 3.13 along with Anaconda's funded support for the project.<br /></p><p>Now we've arrived at Python 3.13 pre-releases, and things are going well! Malcolm reported that "the implementations are nearly complete" along with thank-yous to the core developers who helped with the project.<br /></p><p></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixExyJnspFUzIv-sCDG25YGQznrvIlXm5E-McCWSTT9yi86L7-jUcjj432lFz52wLq80wkEgX6U_7hFiWY_VnZPiPVNRZBB4qDKFQuXYfFCGlDn2sQ6lgjuiMG0X6SryKr0gG11X6ebw8AfLjsj2F0LLGnynRph33i0xPbejo5HBTjGs5TFg/s798/Screenshot%20from%202024-05-28%2016-17-02.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="456" data-original-width="798" height="183" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixExyJnspFUzIv-sCDG25YGQznrvIlXm5E-McCWSTT9yi86L7-jUcjj432lFz52wLq80wkEgX6U_7hFiWY_VnZPiPVNRZBB4qDKFQuXYfFCGlDn2sQ6lgjuiMG0X6SryKr0gG11X6ebw8AfLjsj2F0LLGnynRph33i0xPbejo5HBTjGs5TFg/s320/Screenshot%20from%202024-05-28%2016-17-02.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Overview of current Python mobile platform support<br /></td></tr></tbody></table><p>The other platforms listed in the table "iOS x86_64 and Android ARM32/x86", don't have any plans to be implemented. There aren't any actual physical devices for iOS on x86_64 as the architecture is only used for development simulators.<br /></p><p>For Android the ARM32 and x86 platforms are being phased out due to being 32-bit architectures and today represent less than 10% of devices. For these reasons, Malcolm and team have decided not to implement support for this architecture.<br /></p><p>Malcolm also reported that there is a buildbot for iOS and in the coming weeks there will be buildbots added for Android ARM64 and x86_64 platforms.</p><h3 style="text-align: left;">Let's talk packages!<br /></h3><p> Python is well-known for its rich package ecosystem, and the BeeWare team is working on bringing Python packages to mobile Python, too. "It's not enough just to have support for CPython", Malcolm said on this topic, "we also need to support the packaging ecosystem". As with many new platforms for Python, pure Python packages work without much issue and "the difficulty comes in with anything which contains native compiled components".</p><p> </p><p></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3dD8cimeWwy6C-5ZI1u9BIWXH9xavRe3bEsX39JrLTL8LICA2M71VKqWdwFJk-tZjAe9e_keVDm3b9uNHK5wjQvx5Bo4nlmRKDYwWd5eL-RtOUG1FOgHWVUOk4jDTKROaJb33JTWuBmedaw3MR4LupbmqCSRoF7vAhxv4Ls3g8-ORXTxgKw/s799/Screenshot%20from%202024-05-28%2016-20-10.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="314" data-original-width="799" height="126" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3dD8cimeWwy6C-5ZI1u9BIWXH9xavRe3bEsX39JrLTL8LICA2M71VKqWdwFJk-tZjAe9e_keVDm3b9uNHK5wjQvx5Bo4nlmRKDYwWd5eL-RtOUG1FOgHWVUOk4jDTKROaJb33JTWuBmedaw3MR4LupbmqCSRoF7vAhxv4Ls3g8-ORXTxgKw/s320/Screenshot%20from%202024-05-28%2016-20-10.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The current and future approach for mobile-friendly Python packages<br /></td></tr></tbody></table> <p></p><p>The BeeWare team's approach so far has been to bootstrap packages with native components on their own by creating tools and "building wheels for popular packages like numpy, cryptography, and Pillow". Malcolm reported that the current approach of rebuilding individual packages isn't scalable and the team would need to help upstream maintainers build their own mobile wheels. Malcolm said the team plans to focus this year on "making it as easy as possible to produce and release [mobile] wheels within existing workflows" and contributing to tools like cibuildwheel, setuptools, and PyO3.</p><p>Malcolm also hopes that "by the end of this year some of the major packages will be in position to start releasing mobile wheels to the Python Package Index". The team has already specified a format for the wheel tags for iOS (<a href="https://peps.python.org/pep-0730/#packaging">PEP 730</a>) and Android (<a href="https://peps.python.org/pep-0738/#packaging">PEP 738</a>). "The binary compatibility situation is pretty good", Malcolm noted that iOS and Android both come from a single source in Apple and Google respectively meaning "there's a fairly well-defined set of libraries available on each version".</p><p>Python today provides an embeddable package for the Windows platform. Malcolm requested from the group that more official Python embeddable packages be created for each of the mobile platforms with headers and libraries to ease building Python packages for those platforms. Having these artifacts available would provide a reference for binary compatibility on those platforms.</p><p>Ned Deily, the macOS release expert for CPython, agreed that having more binary releases for macOS and iOS is something we "should definitely do in the 3.14 timeframe".<br /></p><h3 style="text-align: left;">Challenges with keeping mobile buildbots green<br /></h3><p style="text-align: left;">Malcolm provided the core developer team some tips on writing Python code with these new and constrained platforms in mind. He warned that there is little to no support for spawning subprocesses, but "multi-threading on the other hand is perfectly fine on both of these platforms".</p><p style="text-align: left;">Mobile platforms also tend to be constrained in terms of security. iOS only allows loading libraries from specific folders and Android has restrictions like not being able to read the root directory or create hard links.</p><p style="text-align: left;">Given these differences, "it's reasonable to expect that mobile platforms will have more frequent failures as development proceeds, so how do we go about testing them?" The full CPython test suite is running on both mobile platforms with buildbots, but today there's no testing done before a pull request is merged. This situation leads to mobile buildbots starting to fail without the contributing developer necessarily noticing.</p><p style="text-align: left;">This problem is exacerbated by limited continuous integration (CI) resources in GitHub Actions, especially for macOS which limits virtualization on ARM64 processors. Malcolm suggested evaluating <a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue#about-merge-queues">GitHub's Merge Queue feature</a> as a potential way to solve this issue by requiring a small amount of testing on mobile platforms without blocking development of features.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizihyphenhyphen3scy_eGOswab7CI_de9nl5dHwj4S-HnT0neRp6ezuEbs6qgDPo4Rx3wH98P3Rt2RoxtZzsouB-afH7Hs82s1AQD_76hk-b8Bz33U8cEpc8BHKeBKptWzlRjs313VU9YvA9lb8VezFqn1UjNoPHC_9-H4ySshqZd-QhEyHqFKNNMllvg/s1198/Screenshot%20from%202024-05-29%2012-47-34.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="673" data-original-width="1198" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizihyphenhyphen3scy_eGOswab7CI_de9nl5dHwj4S-HnT0neRp6ezuEbs6qgDPo4Rx3wH98P3Rt2RoxtZzsouB-afH7Hs82s1AQD_76hk-b8Bz33U8cEpc8BHKeBKptWzlRjs313VU9YvA9lb8VezFqn1UjNoPHC_9-H4ySshqZd-QhEyHqFKNNMllvg/s320/Screenshot%20from%202024-05-29%2012-47-34.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Malcolm's proposal for better visibility of test failures for mobile<br /></td></tr></tbody></table><br /><p style="text-align: left;">艁ukasz Langa agreed that CI was an issue, one that he's actively looking improving, but wasn't convinced that using a merge queue would decrease the number of jobs required to run. Malcolm clarified that he is proposing only running a smaller subset of jobs per-commit in pull requests and the complete set, including some buildbots, as a part of pre-merge testing. <br /></p><p style="text-align: left;"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3" style="text-overflow: unset;"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3" style="text-overflow: unset;">Many folks expressed concern about adding buildbots as a part of pre-merge or per-commit checks, because buildbots have no high-availability SLA and often suffer occasional outages, some buildbots not being reliable and therefore preventing merging of commits, and concerns about security of unreviewed changes running on buildbots.<br /></span></span></p><p style="text-align: left;"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3" style="text-overflow: unset;"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3" style="text-overflow: unset;">Thomas Wouters, Python 3.13 release manager, was "unconvinced" on adding pre-merge testing for Tier 3 platforms, something that is <a href="https://peps.python.org/pep-0011/#support-tiers">usually reserved for Tier 1 platforms</a>.</span></span></p><p style="text-align: left;">Ned Deily recommended doing iOS builds as a part of existing macOS builds in GitHub Actions. This would catch build errors for the platform and would likely find some issues early without much additional investment.</p> <div style='clear: both;'></div> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'> <span class='post-author vcard'> Posted by <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://www.blogger.com/profile/16555309043643874359' itemprop='url'/> <a class='g-profile' href='https://www.blogger.com/profile/16555309043643874359' rel='author' title='author profile'> <span itemprop='name'>Seth Michael Larson</span> </a> </span> </span> <span class='post-timestamp'> at <meta content='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-python-on-mobile.html' itemprop='url'/> <a class='timestamp-link' href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-python-on-mobile.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2024-06-14T09:13:00-04:00'>6/14/2024 09:13:00 AM</abbr></a> </span> <span class='post-comment-link'> </span> <span class='post-icons'> <span class='item-control blog-admin pid-743697153'> <a href='https://www.blogger.com/post-edit.g?blogID=8520&postID=7757275864788771480&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <div class='post-share-buttons goog-inline-block'> </div> </div> <div class='post-footer-line post-footer-line-2'> <span class='post-labels'> </span> </div> <div class='post-footer-line post-footer-line-3'> <span class='post-location'> Location: <a href='https://maps.google.com/maps?q=1000+Fort+Duquesne+Blvd,+Pittsburgh,+PA+15222,+USA@40.4455472,-79.9962844&z=10' target='_blank'>1000 Fort Duquesne Blvd, Pittsburgh, PA 15222, USA</a> </span> </div> </div> </div> </div> <div class='post-outer'> <div class='post hentry uncustomized-post-template' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivKqZ5arkv_l8RhLPqmFmnl_v797VnOU0HBAilJQFK-VUktBrd5VjNg9p0gfqX5UjS9PTIjLmsN_wUx6Tbbjqbo_baffEOyqwcPazs224myHNsjyTERlbtZ2omHUOxMqAUSj7crhDzDa3CsTDtrRdCnepHwsqpEiCMnMXTQBd7AKAVjQd_LQ/s320/Screenshot%20from%202024-06-07%2010-27-54.png' itemprop='image_url'/> <meta content='8520' itemprop='blogId'/> <meta content='4948873243117449847' itemprop='postId'/> <a name='4948873243117449847'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-free-threading-ecosystems.html'>The Python Language Summit 2024: Free-threading ecosystems</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-4948873243117449847' itemprop='description articleBody'> <p>Following <a href="https://pyfound.blogspot.com/2023/05/the-python-language-summit-2023-making.html">years of excitement</a> around the removal of the Global Interpreter Lock (GIL), Python without the GIL is coming soon. <a href="https://www.python.org/downloads/release/python-3130b1/">Python 3.13 pre-releases</a> already have support for being built without the GIL using a new <span style="font-family: courier;">--disable-gil</span> compile-time option:</p><p><span style="font-family: courier;"># Download<br />wget https://www.python.org/ftp/python/3.13.0/Python-3.13.0b2.tgz<br />echo "c87c42aa8137230a15a02ed90a6600610ba680cb5b54c0fbc57581a0d032e0c4 ./Python-3.13.0b2.tgz" | sha256sum --check<br />tar -xzvf ./Python-3.13.0b2.tgz<br /><br /># Build<br />cd </span><span style="font-family: courier;">Python-3.13.0b2/</span><br /><span style="font-family: courier;">./configure --disable-gil<br />make<br /><br /># Run with no GIL!<br />./python -X nogil -c "import sys; print(sys._is_gil_enabled())"<br />False</span><br /><br />But simply having GIL-less Python is not enough, code needs to be written that is safe and performant without the GIL using both the C and Python APIs.<br /></p><p>This year at the Language Summit, Daniele Parmeggiani gave a talk about ways Python can enable safe and performant concurrent code without <i>locking</i> CPython into a specific implementation or <a href="https://en.wikipedia.org/wiki/Memory_model_(programming)">memory model</a>.</p><h3 style="text-align: left;">Don't leak the details<br /></h3><p>Daniele started his talk, like many Python users, with cautious enthusiasm about the prospect of free-threading in Python:<br /></p><p></p><blockquote><p>"Given the <a href="https://discuss.python.org/t/pep-703-making-the-global-interpreter-lock-optional-in-cpython-acceptance/37075">acceptance notes</a> to PEP 703, one should argue for caution in discussing the prospects of new multi-threading ecosystems after the release of Python 3.13 — with a hopeful spirit I will disregard this caution here."</p><p>-- Daniele Parmeggiani</p></blockquote><p>Daniele detailed a <a href="https://github.com/python/cpython/issues/113920">feature request</a> he had opened to create a public function for the private C API function "<span style="font-family: courier;">_Py_TRY_INCREF()</span>". Daniele wanted to use this function to increment an object's reference count safely in a truly multi-threaded Python where a reference count might be decremented concurrently to an increment.<br /></p><p></p><p>Daniele continued, "[Sam Gross] <a href="https://github.com/python/cpython/issues/113920#issuecomment-1995563002">responded</a> as thoughtfully and thoroughly as he usually does that the function shouldn't be public, and I agree with him".<br /></p><p>The semantics of <span style="font-family: courier;">_Py_TRY_INCREF()</span> today are tied to the specific implementation of free-threading and without a guarantee that the underlying implementation won't change Daniele does not think the function "should ever be made public".</p><p>But without this functionality Daniele's problem still stands, where do we go from here?<br /></p><h3 style="text-align: left;">Higher-level APIs to the rescue<br /></h3><p>"At a higher-level it's possible to write further guarantees without constraining what's under the hood". Daniele started a single step up in abstraction, detailing an atomic reference API:</p><p><span style="font-family: courier;">PyObject *AtomicRef_Get(AtomicRef *self)<br />{<br /> PyObject *reference;<br /> reference = self->reference;<br /> while (!_Py_TRY_INCREF(reference)) {<br /> reference = self->reference;<br /> }<br /> return reference;<br />}</span><br /></p><p>This would be "trivial to implement" with the new garbage collection scheme in Python 3.13 ("quiescent state-based reclamation" or QSBR), "but what if [Python 3.14] were to change this scheme radically? Or what if 3.15 decides to do away with it entirely?"</p><p>Daniele eschewed making guarantees about low-level APIs at this stage of development, but concluded that "an API for atomically updating a reference to a PyObject seems like a high-level use-case worth guaranteeing, regardless of any implementation of reference counting".<br /></p><h3 style="text-align: left;">Atomic data structures<br /></h3><p style="text-align: left;">Daniele continued exploring higher-level concepts that Python could provide at this stage of free-threading by looking to what other languages are doing.<br /></p><p>Java provides a <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/package-summary.html">java.util.concurrent</a> package containing some familiar faces for Python concurrency users like <a href="https://docs.python.org/3/library/threading.html#semaphore-objects">Semaphores</a>, <a href="https://docs.python.org/3/library/threading.html#lock-objects">Locks</a>, and <a href="https://docs.python.org/3/library/threading.html#barrier-objects">Barriers</a>, but also some other atomic primitives that map to Python classes like dicts, lists, booleans, and integers. Daniele asked whether Python should provide atomic variations for primitives like numbers and dictionaries.<br /></p><p>Daniele explained that many atomic data structures use the "compare-and-set" model to synchronize read and write access to the same space in memory. Compare-and-set requires the caller to specify an expected value, if the value in memory matches the expected value then the value is updated to the passed value, and the call returns whether the operation was successful or not.</p><p>Daniele explained that compare-and-set establishes a "<a href="https://jenkov.com/tutorials/java-concurrency/java-happens-before-guarantee.html">happens-before</a>" ordering between concurrent writes to the same memory location, joking that the phrase "happens-before" may spark thoughts of memory models which he wished to avoid.</p><p>Today Python doesn't have any method of reordering memory accesses which would require thinking about the memory model. Daniele noted that may come one day from the new <a href="https://docs.python.org/3.13/whatsnew/3.13.html#whatsnew313-jit-compiler">just-in-time compiler</a> (JIT).<br /></p><p>Daniele was already developing an <a href="https://github.com/dpdani/cereggii/tree/dev/src/cereggii/atomic_dict">atomic dictionary class</a> and had seen performance gains over the existing standard library dictionary with the GIL disabled (with lower single-threaded performance):</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivKqZ5arkv_l8RhLPqmFmnl_v797VnOU0HBAilJQFK-VUktBrd5VjNg9p0gfqX5UjS9PTIjLmsN_wUx6Tbbjqbo_baffEOyqwcPazs224myHNsjyTERlbtZ2omHUOxMqAUSj7crhDzDa3CsTDtrRdCnepHwsqpEiCMnMXTQBd7AKAVjQd_LQ/s1199/Screenshot%20from%202024-06-07%2010-27-54.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="672" data-original-width="1199" height="179" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivKqZ5arkv_l8RhLPqmFmnl_v797VnOU0HBAilJQFK-VUktBrd5VjNg9p0gfqX5UjS9PTIjLmsN_wUx6Tbbjqbo_baffEOyqwcPazs224myHNsjyTERlbtZ2omHUOxMqAUSj7crhDzDa3CsTDtrRdCnepHwsqpEiCMnMXTQBd7AKAVjQd_LQ/s320/Screenshot%20from%202024-06-07%2010-27-54.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Performance comparison of dict with and without the GIL and Daniele's AtomicDict<br /></td></tr></tbody></table><br /><p>Daniele observed that the free-threading changes actually <i>decreased</i> the performance for write-heavy workloads on builtin types like dictionaries because "Python programs will now actually be subject to memory contention". When multiple threads attempt to mutate a list or dictionary, "it will be as if the GIL is still there, [the threads] will all be contending for one lock", offering that "new concurrent data structures would alleviate this performance issue".<br /></p><p>Daniele wanted to know what primitives Python should offer for C extension developers targeting free-threaded builds, or asked if it's still too early to make guarantees: <br /></p><p>"As the writer of a <a href="https://github.com/dpdani/cereggii">C extension</a> looking to implement concurrent lock-free data structures for Python", Daniele asked of the room, "does CPython eventually wish to incorporate... either high-level atomics or low-level routines?"</p><p>Daniele continued, "if not the atomics, then new low-level APIs like <span style="font-family: courier;">_Py_TRY_INCREF()</span> will be necessary in order not to force the abuse of locks in external efforts towards new free-threading ecosystems".</p><h3 style="text-align: left;">Discussion<br /></h3><p>Thomas Wouters, channeling the Steering Council's past intent from accepting PEP 703 last October said, "we don't know yet what users will actually need" and the Steering Council didn't want to "prematurely optimize" and mandate features be implemented without that knowledge.<br /></p><p>Thomas recommended building solutions to "production use-cases" as PyPI packages or separate projects before the deciding to pull those solutions into Python, summarizing the sentiment with, "we need to take our time and make sure we're doing the right thing".<br /></p><p>Steering Council member Barry Warsaw agreed with Thomas on strategy, also adding that "[atomic references] might be something [Python] needs to make sure the interpreter doesn't crash with some of our own C code". Barry was interested in how to "ensure that the interpreter stays safe in the face of free-threading without necessarily thinking about the right APIs for the higher-level data structures".</p><p>Sam Gross, author and main implementer of PEP 703 to make the GIL optional in CPython, commented on making additional guarantees to standard library collections, saying "we're going to find situations that are ambiguous where no one's promised thread-safety or [the lack of thread-safety]".<br /></p><p>Sam would also like to see "scalable collections" on PyPI (and "would love to see in Python eventually too") that are "designed not just to be thread-safe, but to scale well with certain workloads". Sam noted that builtin data classes like dict and list "can only make so many trade-offs" and tend to "focus on single-threaded performance" or "multi-threaded read-only access".</p><p>Eric Snow wanted to see immutable data structures be considered, too, noting the benefits to performance and shareability that Yury Selivanov was seeing when using them with sub-interpreters.<br /></p><p>Gregory Smith sympathized with Daniele on wanting to avoid thinking about memory models, but "had a sneaking suspicion we kinda have to anyway". Greg was concerned about other stacks like data science and machine learning "re-interpreting Python code and transforming it into other things that run on other hardware". Without a clear definition, people "make their own assumptions" and get confused when code runs differently in different places.</p><p>Replying to Greg, Daniele offered that there's already a mechanism for determining whether an object is shared between threads "which might be a first-step", but that this "was a detail of the implementation, and not a part of the language".<br /></p><p>Guido van Rossum began by being "wary of looking to Java for examples", stating that many APIs that Python borrowed from Java were eventually deprecated and removed.<br /></p><p>Guido commented that "there will be other people with much higher-level ideas on concurrency" and recommended "to wait as long as we can before we build anything into the language explicitly or implicitly". Guido also felt it was "important that we have sub-interpreters as well as free-threading, so people can play with different models before we commit to anything".</p><p>Overall, the group seemed interested in Daniele's work on atomics but didn't seem willing to commit to exact answers for Python yet. It's clear that more experimentation will be needed in this area.<br /></p><p> </p><p> <br /></p> <div style='clear: both;'></div> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'> <span class='post-author vcard'> Posted by <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://www.blogger.com/profile/16555309043643874359' itemprop='url'/> <a class='g-profile' href='https://www.blogger.com/profile/16555309043643874359' rel='author' title='author profile'> <span itemprop='name'>Seth Michael Larson</span> </a> </span> </span> <span class='post-timestamp'> at <meta content='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-free-threading-ecosystems.html' itemprop='url'/> <a class='timestamp-link' href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-free-threading-ecosystems.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2024-06-14T09:13:00-04:00'>6/14/2024 09:13:00 AM</abbr></a> </span> <span class='post-comment-link'> </span> <span class='post-icons'> <span class='item-control blog-admin pid-743697153'> <a href='https://www.blogger.com/post-edit.g?blogID=8520&postID=4948873243117449847&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <div class='post-share-buttons goog-inline-block'> </div> </div> <div class='post-footer-line post-footer-line-2'> <span class='post-labels'> </span> </div> <div class='post-footer-line post-footer-line-3'> <span class='post-location'> Location: <a href='https://maps.google.com/maps?q=1000+Fort+Duquesne+Blvd,+Pittsburgh,+PA+15222,+USA@40.4455472,-79.9962844&z=10' target='_blank'>1000 Fort Duquesne Blvd, Pittsburgh, PA 15222, USA</a> </span> </div> </div> </div> </div> <div class='post-outer'> <div class='post hentry uncustomized-post-template' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgx0oL6BPriudhNKKC3MP6xvjLt8SZ3FigkSSpy1UeJ-9xIJKVqERnhegv6yFIUH24vA2LtC2tHk2F6EL_CsoEGDwHvoyF05A9T_f6bn_Hjz7-T-PrVpdAkRn7FjIrFXZGtbFdkNlRsddRoPKxtRdIMgjaN8v5j6_bezxq-IvQjGftIiWEeRA/s320/Screenshot%20from%202024-06-03%2009-27-32.png' itemprop='image_url'/> <meta content='8520' itemprop='blogId'/> <meta content='345653848632957355' itemprop='postId'/> <a name='345653848632957355'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-c-api.html'>The Python Language Summit 2024: Native Interface and Limited C API</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-345653848632957355' itemprop='description articleBody'> <p>Back in October 2023, <a href="https://peps.python.org/pep-0731/">PEP 731</a> proposed a new <a href="https://github.com/capi-workgroup">C API working group</a> <a href="https://peps.python.org/pep-0731/#motivation">charged</a> with overseeing and coordinating the development and maintenance of the Python C API. This working group spawned from a <a href="https://pyfound.blogspot.com/2023/05/the-python-language-summit-2023-three.html">series of discussions on the C API</a> from the Language Summit in 2023 and creation of an <a href="https://github.com/capi-working-group/problems">inventory of problems with the C API</a> at the 2023 core developer sprint.</p><p>Two inaugural C API working group members, Petr Viktorin and Victor Stinner, presented back-to-back talks on the C API and gave context on what's been happening in the past year.<br /></p><h3 style="text-align: left;">What does the C API working group do?</h3><p style="text-align: left;">The first of the two C API talks was given by Petr Viktorin on the "Native Interface" and some of the first steps towards an idealized C API.</p><p style="text-align: left;">Petr started off by explaining that the <a href="https://github.com/capi-workgroup">C API working group</a> makes two types of decisions: what functionality to expose via the C API and how to expose it. Petr also explained that the C API working group keeps two separate issue trackers, one for incremental "<a href="https://github.com/capi-workgroup/api-evolution/issues">evolution</a>" of the C API and another for "<a href="https://github.com/capi-workgroup/api-revolution/issues">revolution</a>", a place where more "radical" ideas are discussed.<br /></p><p style="text-align: left;">The existing C API wasn't designed with the knowledge, context, and needs of today (like free-threading), but there are many good parts of the C API. Petr explained that one of the more impactful things the working group has done is to formalize "guidelines to get consistency with the good parts of the existing API". <br /></p><p style="text-align: left;"> </p><p style="text-align: left;">Petr gave an example of what can go wrong with the PyLong_GetSign() function. This API has a baked-in type check that can't be avoided due to its function signature and thus incurs a performance penalty <i>even when the caller has already checked whether the object is the correct type</i>.</p><p style="text-align: left;">This extra performance penalty means that CPython itself uses its own private API which avoids the type check, but this extra private API only for CPython isn't a great experience. Other languages and projects want access to the more performant API, too.</p><p style="text-align: left;">Petr went on to reference Mark Shannon's <a href="https://github.com/markshannon/New-C-API-for-Python">proposal for a New C API</a> which Petr called "close to perfect" with caveats around not dropping existing APIs and the name, instead suggesting "Native Interface" for the name of the new C API.</p><p>"Unfortunately we need to keep the old API around. We can't just remove a chunk of the existing API just because it's old", Petr lamented. Petr also noted that not being able to remove parts of the existing API might mean that the <a href="https://github.com/faster-cpython/">Faster CPython</a> project loses some incentive to work on the new C API.</p><p>C API decisions are made on three axes: performance, safety, and convenience. Petr argued that of the three, "performance should be prioritized", because a convenient and safe layer can be built on top of a performant API with the right amount of context. </p><h3 style="text-align: left;">Annotating the existing C API<br /></h3><p>Petr noted that we have experience within Python for adding a safety layer on top of APIs in Python: <b>type hints</b>! Type hints in Python provide context into an API's inputs and outputs that can be checked using external tooling without incurring a performance penalty on runtime.<br /></p><p>Petr proposed adding annotations to C function signatures for function behaviors like "returns a null pointer on error" or "never returns a null pointer" which can then be used in other contexts like documentation or borrow checking. Among the proposed annotations were some about whether references were borrowed, stolen, or a new reference, which can be used to check consistency of references.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgx0oL6BPriudhNKKC3MP6xvjLt8SZ3FigkSSpy1UeJ-9xIJKVqERnhegv6yFIUH24vA2LtC2tHk2F6EL_CsoEGDwHvoyF05A9T_f6bn_Hjz7-T-PrVpdAkRn7FjIrFXZGtbFdkNlRsddRoPKxtRdIMgjaN8v5j6_bezxq-IvQjGftIiWEeRA/s1873/Screenshot%20from%202024-06-03%2009-27-32.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="961" data-original-width="1873" height="164" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgx0oL6BPriudhNKKC3MP6xvjLt8SZ3FigkSSpy1UeJ-9xIJKVqERnhegv6yFIUH24vA2LtC2tHk2F6EL_CsoEGDwHvoyF05A9T_f6bn_Hjz7-T-PrVpdAkRn7FjIrFXZGtbFdkNlRsddRoPKxtRdIMgjaN8v5j6_bezxq-IvQjGftIiWEeRA/s320/Screenshot%20from%202024-06-03%2009-27-32.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">List of possible annotations for C API functions<br /></td></tr></tbody></table><br /><p>Petr also noted that many of these annotations apply not only to new APIs but to existing APIs as well. Implementing these annotations as empty C macros means that behavior and performance isn't impacted but can be parsed from header files.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8eYzfRJu4zPbJScTMUyN6DVvu6ehjIFeC1vx-5dlegFNGUTfS_pzhMBSjk2NXcBBAH67iF8p41iSI1_pm_QJXFlC70l_nb8MWCP-oZNGMrNtV0ArkDa17rsVQK9w5f5zHny14LlN3rJgjxW_iSdqGz8i8Lzjy0hG4S16Kpu33iV62BDZbnw/s1141/Screenshot%20from%202024-06-03%2012-42-56.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="269" data-original-width="1141" height="75" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8eYzfRJu4zPbJScTMUyN6DVvu6ehjIFeC1vx-5dlegFNGUTfS_pzhMBSjk2NXcBBAH67iF8p41iSI1_pm_QJXFlC70l_nb8MWCP-oZNGMrNtV0ArkDa17rsVQK9w5f5zHny14LlN3rJgjxW_iSdqGz8i8Lzjy0hG4S16Kpu33iV62BDZbnw/s320/Screenshot%20from%202024-06-03%2012-42-56.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Petr's slides showing the annotations in use as C macros<br /></td></tr></tbody></table><br /><p>To go along with these new annotations, Petr proposed writing a tool similar to <a href="https://devguide.python.org/development-tools/clinic">Argument Clinic</a>. Argument Clinic is a tool maintained by the CPython team which automatically generates boilerplate code like function signatures and argument unpacking based on input instructions.</p><p>Mark Shannon asked to clarify whether the priority was to improve the C API or document existing behavior. Petr's plan was to add annotation information to the existing API and to wait on implementing the new Native Interface until later. This plan wouldn't change the behavior of any existing API, but APIs which aren't conforming would receive a new variant that conforms to the new C API standards.</p><p>Victor Stinner asked whether the annotation information would be stored in a separate file. Petr noted that a separate file is the plan to make it easier to wrap the API and to avoid needing to parse header files directly.<br /></p><p><a href="https://github.com/PyO3/pyo3">PyO3</a> maintainer David Hewitt asked whether the plan was to include variations that avoid type checks for all C API functions to dodge the performance penalty for C API wrappers. David noted that PyO3 implemented many C API function calls as methods on wrapped objects. This means that the type check was implicit and thus could avoid having types checked again by the C API function. David also clarified that these extra checks "aren't a major performance drag" but would be great to remove the inefficiencies if possible.</p><p>Petr answered that wrappers will need to wait for the Native Interface to be implemented to expose the underlying C API functions which don't include type checks.</p><p>There was enthusiastic agreement from the room about using annotation information for documentation and automatically generating boilerplate code and checks along with being able to do borrow checking using annotation information.<br /></p><h3 style="text-align: left;">Limited C API</h3><p style="text-align: left;">The second C API talk was given by Victor Stinner on the status of the <a href="https://docs.python.org/3/c-api/stable.html#limited-c-api">Limited C API</a>. The Limited C API is a subset of the Python C API that's consistent across different versions of Python. The Limited C API can be opted-in to using #define Py_LIMITED_API, by doing so only <b>public</b> functions of the limited C API can be used.</p><p style="text-align: left;">Victor started off by listing his long-term goals for the Python C API, which mostly focused on reducing friction both for maintainers of the Python C API and for third parties using the API or updating to support new Python versions. One possibility to achieve this would be to "move to using the Limited C API by default and use the <a href="https://docs.python.org/3/c-api/stable.html#stable-abi">Stable ABI</a> for everybody" but Victor noted this is a "very long term goal".</p><p style="text-align: left;">Getting to this goal is challenging because it's difficult to know how a given change will affect the ecosystem of Python projects, both for finding affected projects and how widespread breakage would be for users. Victor explained that each change typically only requires "1-10 lines of code changed per impacted project" to fix issues.</p><p style="text-align: left;"></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEHlKl8WNOLVBF-6ST8DW75_NL0xPNL-sE4OfXzVGO2mBDNNrAKkP5qdieotnS9zqNwrbJBJYijYWO_RuR48skh_vUK6c4fGFmRHME3WMuYa2HDjW2QEQwgVsiY3373STVXgZoxwZvLo_e9fh83Djfmd_W6Sra5TgSxL9EOu_i2qtc-4d_7w/s1022/Screenshot%20from%202024-05-28%2016-01-05.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="767" data-original-width="1022" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEHlKl8WNOLVBF-6ST8DW75_NL0xPNL-sE4OfXzVGO2mBDNNrAKkP5qdieotnS9zqNwrbJBJYijYWO_RuR48skh_vUK6c4fGFmRHME3WMuYa2HDjW2QEQwgVsiY3373STVXgZoxwZvLo_e9fh83Djfmd_W6Sra5TgSxL9EOu_i2qtc-4d_7w/s320/Screenshot%20from%202024-05-28%2016-01-05.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Trying to move all functions from private to either public or internal<br /></td></tr></tbody></table><br /><p></p><p style="text-align: left;">Victor's biggest project currently is to remove private functions from the C API, specifically functions which begin with an underscore "_" by convention. Victor explained that he removed all 300 private functions starting with "_Py" for 3.13.0-alpha1 to discover how and where private APIs are used by downstream projects. Victor and team anticipated that this mass-removal would cause breakages, so after the initial round of discovery the removed functions causing the most issues have been re-added in 3.13.0-alpha2.</p><p style="text-align: left;">As of 3.13.0-beta1, 264 functions of the over 300 functions are still removed. The functions which have been added back are not simply left as-is either: once a private function is discovered the C API working group gets a chance to design a <i>new public C API function</i> for projects to use instead.</p><p style="margin-left: 40px; text-align: left;">"The goal isn't to annoy people, the goal is to provide better functions for everybody" -- Victor Stinner<br /></p><p style="text-align: left;">These new public C API functions would have documentation, tests, backwards compatibility guarantees, and can benefit from the new C API working group guidelines around API design. Victor gave an example of the PyDict_Pop() API which previously required checking for an error condition using PyErr_Occurred() to disambiguate between a key not being in the dictionary or if any other error occurred.</p><p style="text-align: left;">The new PyDict_Pop() function returns -1, 0, and 1 for the "error", "not found", and "found" cases respectively in accordance with new C API guidelines meaning a call to PyErr_Ocurred() is avoided.<br /></p><p style="text-align: left;"></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGaZhdHinILOD5Dy1yMt2N0CTg3RIw9FxPOqlEm7-fGVX8L6SHzn0Fe32lsnqwKcSQpYODIJZpuIcFWVbiRBurMvlLTiIOECsFEPmRqhIVgdCQ8nP50R2122vjHPd2fVmWEkFx4AhtajudT0829jnsGin4yjyMnHXfYJUo7ULsI47mKsdQqw/s1022/Screenshot%20from%202024-05-28%2016-06-33.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="767" data-original-width="1022" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGaZhdHinILOD5Dy1yMt2N0CTg3RIw9FxPOqlEm7-fGVX8L6SHzn0Fe32lsnqwKcSQpYODIJZpuIcFWVbiRBurMvlLTiIOECsFEPmRqhIVgdCQ8nP50R2122vjHPd2fVmWEkFx4AhtajudT0829jnsGin4yjyMnHXfYJUo7ULsI47mKsdQqw/s320/Screenshot%20from%202024-05-28%2016-06-33.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">New PyDict_Pop() public function with improvements<br /></td></tr></tbody></table><p>The <a href="https://pythoncapi-compat.readthedocs.io">pythoncapi-compat project</a>, which Victor is a maintainer of, provides backfills for these new 3.13 APIs for Python 3.12 and older. This means that projects can immediately start taking advantage of new APIs which are better designed and return strong references. Victor highlighted in particular PyDict_GetItemRef() and others which are new in 3.13 and are important for free-threading due to PyDict_GetItem() returning a borrowed reference instead of a new strong reference.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZQNbEMsxXCjgzqQZvK8ujCqXQ38ySK_9yQDwdx4EHjGilQNos1TkMJgjjqa3RB8qCCXCZHGopM6oK5henJSch2c7lOgF_mrJic5isrqEEUaIuAO_azwyR95GcIgMi2LP0i5XEs1MlG4goJLr66_rQLKSBI9fYE7h7ytplVayNTKVsywp6Bg/s1023/Screenshot%20from%202024-06-03%2012-09-42.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="768" data-original-width="1023" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZQNbEMsxXCjgzqQZvK8ujCqXQ38ySK_9yQDwdx4EHjGilQNos1TkMJgjjqa3RB8qCCXCZHGopM6oK5henJSch2c7lOgF_mrJic5isrqEEUaIuAO_azwyR95GcIgMi2LP0i5XEs1MlG4goJLr66_rQLKSBI9fYE7h7ytplVayNTKVsywp6Bg/s320/Screenshot%20from%202024-06-03%2012-09-42.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Slide from Victor's presentation on current Limited C API adoption<br /></td></tr></tbody></table><br /><p style="text-align: left;">The biggest users of the Python C API like Cython, PyO3, pybind, and more are at various stages of supporting the Limited C API, most of which require an opt-in for builds.</p><p style="text-align: left;">Victor's top project in coming months and years will be to move the C API away from using structures ("C structs") like PyFrameObject, PyThreadState, and PyTypeObject. Victor noted that projects like Cython, greenlet, gevent, and more have to access directly into structure members which can cause breakages when upgrading to new Python versions. Victor explained that there is no way to handle this with the Limited C API today. "We already provide many helper functions like getters and setters, but we need to provide even more" said Victor as a way forwards on this issue.</p><p style="text-align: left;">Petr questioned the approach of "breaking current projects so that future Python versions don't break them", saying that it'd be better to warn projects about using private API functions that aren't supported and wait to introduce breaking changes when it's necessary to progress the C API.</p><p style="text-align: left;">Victor replied that he'd already started work on a PEP to opt-in for build errors when a project is using deprecated functions, "like a strict mode for the C API". Victor agreed that the current plan isn't great in this way, "we ask people to update their code and the timeline is very short, we expect people to update in one years time" noting the circumstances where this can be difficult such as unmaintained projects or solo-maintainers.</p><p style="text-align: left;">Petr also added here that the opt-in would need to be versioned per Python version, so users can have control over when they want to do the work to move to new C API functions. <br /></p><p style="text-align: left;">Eric Snow and Mark Shannon remarked on a more incremental strategy. This strategy would see deprecated functions moved structurally into a separate file ("legacy.c" and "legacy.h") but with the behavior preserved to have a clearer idea of what functions Python developers want to remove. After being moved the functions would be implemented using newly designed APIs where possible. Others noted that this would only be a convenience for core developers and projects that are interested in internals like PyO3 and Cython.</p><p style="text-align: left;">David Hewitt commented on the long feedback cycles, as downstream projects of the Stable ABI are still using Python 3.7 as a target, so any changes to the Stable ABI may not receive feedback until many years later. Victor responded that he's working on a new project that implements new functions of Python for old Python versions.<br /></p><p style="text-align: left;">Overall, the work and proposals presented by both Petr and Victor were well-received by the room. It's clear that the Python C API is in good hands with the C API working group and is moving in the right direction to solve tomorrow's problems.</p> <div style='clear: both;'></div> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'> <span class='post-author vcard'> Posted by <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://www.blogger.com/profile/16555309043643874359' itemprop='url'/> <a class='g-profile' href='https://www.blogger.com/profile/16555309043643874359' rel='author' title='author profile'> <span itemprop='name'>Seth Michael Larson</span> </a> </span> </span> <span class='post-timestamp'> at <meta content='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-c-api.html' itemprop='url'/> <a class='timestamp-link' href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-c-api.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2024-06-14T09:13:00-04:00'>6/14/2024 09:13:00 AM</abbr></a> </span> <span class='post-comment-link'> </span> <span class='post-icons'> <span class='item-control blog-admin pid-743697153'> <a href='https://www.blogger.com/post-edit.g?blogID=8520&postID=345653848632957355&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <div class='post-share-buttons goog-inline-block'> </div> </div> <div class='post-footer-line post-footer-line-2'> <span class='post-labels'> </span> </div> <div class='post-footer-line post-footer-line-3'> <span class='post-location'> Location: <a href='https://maps.google.com/maps?q=1000+Fort+Duquesne+Blvd,+Pittsburgh,+PA+15222,+USA@40.4455472,-79.9962844&z=10' target='_blank'>1000 Fort Duquesne Blvd, Pittsburgh, PA 15222, USA</a> </span> </div> </div> </div> </div> </div></div> </div> <div class='blog-pager' id='blog-pager'> <span id='blog-pager-newer-link'> <a class='blog-pager-newer-link' href='https://pyfound.blogspot.com/search?updated-max=2024-07-12T13:12:00-04:00&max-results=2&reverse-paginate=true' id='Blog1_blog-pager-newer-link' title='Newer Posts'>Newer Posts</a> </span> <span id='blog-pager-older-link'> <a class='blog-pager-older-link' href='https://pyfound.blogspot.com/search?updated-max=2024-06-14T09:13:00-04:00&max-results=2' id='Blog1_blog-pager-older-link' title='Older Posts'>Older Posts</a> </span> <a class='home-link' href='https://pyfound.blogspot.com/'>Home</a> </div> <div class='clear'></div> <div class='blog-feeds'> <div class='feed-links'> Subscribe to: <a class='feed-link' href='https://pyfound.blogspot.com/feeds/posts/default' target='_blank' type='application/atom+xml'>Posts (Atom)</a> </div> </div> </div></div> </div> </div> <div class='column-left-outer'> <div class='column-left-inner'> <aside> </aside> </div> </div> <div class='column-right-outer'> <div class='column-right-inner'> <aside> <div class='sidebar section' id='sidebar-right-1'><div class='widget Text' data-version='1' id='Text1'> <h2 class='title'>Mission</h2> <div class='widget-content'> <span style="font-size: 125%;font-size:125%;"><i>The mission of the <a href="https://www.python.org/psf/">Python Software Foundation</a> is to promote, protect, and advance the Python programming language, and to support and facilitate the growth of a diverse and international community of Python programmers.</i></span> </div> <div class='clear'></div> </div><div class='widget LinkList' data-version='1' id='LinkList1'> <div class='widget-content'> <ul> <li><a href='https://www.python.org/psf-landing/'>Python Software Foundation</a></li> <li><a href='https://www.python.org/psf/grants/'>Grants Program</a></li> <li><a href='https://www.python.org/psf/membership/'>Membership</a></li> <li><a href='https://www.python.org/community/awards/'>Awards</a></li> <li><a href='https://www.python.org/psf/records/board/minutes/'>Meeting Minutes</a></li> </ul> <div class='clear'></div> </div> </div><div class='widget Image' data-version='1' id='Image1'> <h2>PSF Sponsors</h2> <div class='widget-content'> <a href='https://www.python.org/psf/sponsors/'> <img alt='PSF Sponsors' height='113' id='Image1_img' src='https://blogger.googleusercontent.com/img/a/AVvXsEinkl3WkxN3nTJap16aR2WhbP1k4BJw92phSFrhsrCWHOWl2raf6mjtgC7pt4NxSkiugearJ82UJRuepNqcZCEnB2Adm9c1EEv32nHzQf79hx-B2ZOqdk_M6Y6sWkGD7SMsq5l1ZE1xYyxGwrz5U1wnBHlqDOlDfiWHNtFwc_69mS7pRTH6=s250' width='250'/> </a> <br/> <span class='caption'>A big thank you to the above PSF sponsors for supporting our mission!</span> </div> <div class='clear'></div> </div><div class='widget HTML' data-version='1' id='HTML1'> <div class='widget-content'> <a class="twitter-timeline" data-width="256" data-height="512" data-dnt="true" href="https://twitter.com/ThePSF?ref_src=twsrc%5Etfw">Tweets by ThePSF</a> <script async="async" src="https://platform.twitter.com/widgets.js" charset="utf-8" ></script> </div> <div class='clear'></div> </div><div class='widget BlogArchive' data-version='1' id='BlogArchive1'> <h2>Blog Archive</h2> <div class='widget-content'> <div id='ArchiveList'> <div id='BlogArchive1_ArchiveList'> <ul class='hierarchy'> <li class='archivedate expanded'> <a class='toggle' href='javascript:void(0)'> <span class='zippy toggle-open'> ▼  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2024/'> 2024 </a> <span class='post-count' dir='ltr'>(51)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2024/11/'> November </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2024/10/'> October </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2024/09/'> September </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2024/08/'> August </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2024/07/'> July </a> <span class='post-count' dir='ltr'>(7)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate expanded'> <a class='toggle' href='javascript:void(0)'> <span class='zippy toggle-open'> ▼  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2024/06/'> June </a> <span class='post-count' dir='ltr'>(16)</span> <ul class='posts'> <li><a href='https://pyfound.blogspot.com/2024/06/announcing-psf-board-candidates-for-2024.html'>Announcing the PSF Board Candidates for 2024!</a></li> <li><a href='https://pyfound.blogspot.com/2024/06/faq-for-proposed-changes-to-psf-bylaws.html'>FAQ for Proposed Changes to PSF Bylaws</a></li> <li><a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-pyrepl-new-default-repl-for-python.html'>The Python Language Summit 2024: PyREPL -- New def...</a></li> <li><a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024.html'>The Python Language Summit 2024</a></li> <li><a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-lightning-talks.html'>The Python Language Summit 2024: Lightning Talks</a></li> <li><a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-annotations-as-transforms.html'>The Python Language Summit 2024: Annotations as Tr...</a></li> <li><a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-limiting-yield-in-async-generators.html'>The Python Language Summit 2024: Limiting Yield in...</a></li> <li><a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-pyrepl-new-pdb.html'>The Python Language Summit 2024: Should we make pd...</a></li> <li><a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-python-on-mobile.html'>The Python Language Summit 2024: Python on Mobile</a></li> <li><a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-free-threading-ecosystems.html'>The Python Language Summit 2024: Free-threading ec...</a></li> <li><a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-c-api.html'>The Python Language Summit 2024: Native Interface ...</a></li> <li><a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-python-security-model-after-xz.html'>The Python Language Summit 2024: Python's security...</a></li> <li><a href='https://pyfound.blogspot.com/2024/06/python-language-summit-2024-should-python-adopt-calver.html'>The Python Language Summit 2024: Should Python ado...</a></li> <li><a href='https://pyfound.blogspot.com/2024/06/for-your-consideration-proposed-bylaws.html'>For your consideration: Proposed bylaws changes to...</a></li> <li><a href='https://pyfound.blogspot.com/2024/06/its-time-to-make-nominations-for-psf.html'>It’s time to make nominations for the PSF Board El...</a></li> <li><a href='https://pyfound.blogspot.com/2024/06/affirm-your-psf-membership-voting-status.html'>Affirm your PSF Membership Voting Status</a></li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2024/05/'> May </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2024/04/'> April </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2024/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2024/02/'> February </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2024/01/'> January </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2023/'> 2023 </a> <span class='post-count' dir='ltr'>(37)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2023/12/'> December </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2023/11/'> November </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2023/10/'> October </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2023/09/'> September </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2023/08/'> August </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2023/06/'> June </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2023/05/'> May </a> <span class='post-count' dir='ltr'>(12)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2023/04/'> April </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2023/03/'> March </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2023/02/'> February </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2023/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2022/'> 2022 </a> <span class='post-count' dir='ltr'>(35)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2022/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2022/11/'> November </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2022/10/'> October </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2022/07/'> July </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2022/06/'> June </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2022/05/'> May </a> <span class='post-count' dir='ltr'>(12)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2022/04/'> April </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2022/03/'> March </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2022/02/'> February </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2022/01/'> January </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2021/'> 2021 </a> <span class='post-count' dir='ltr'>(42)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2021/12/'> December </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2021/11/'> November </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2021/10/'> October </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2021/09/'> September </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2021/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2021/07/'> July </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2021/06/'> June </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2021/05/'> May </a> <span class='post-count' dir='ltr'>(12)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2021/04/'> April </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2021/03/'> March </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2021/02/'> February </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2021/01/'> January </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2020/'> 2020 </a> <span class='post-count' dir='ltr'>(51)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2020/12/'> December </a> <span class='post-count' dir='ltr'>(8)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2020/11/'> November </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2020/10/'> October </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2020/09/'> September </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2020/07/'> July </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2020/06/'> June </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2020/05/'> May </a> <span class='post-count' dir='ltr'>(10)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2020/04/'> April </a> <span class='post-count' dir='ltr'>(11)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2020/03/'> March </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2020/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2019/'> 2019 </a> <span class='post-count' dir='ltr'>(45)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2019/12/'> December </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2019/11/'> November </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2019/10/'> October </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2019/09/'> September </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2019/08/'> August </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2019/07/'> July </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2019/06/'> June </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2019/05/'> May </a> <span class='post-count' dir='ltr'>(11)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2019/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2019/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2019/02/'> February </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2019/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2018/'> 2018 </a> <span class='post-count' dir='ltr'>(31)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2018/12/'> December </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2018/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2018/10/'> October </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2018/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2018/08/'> August </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2018/07/'> July </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2018/06/'> June </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2018/05/'> May </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2018/04/'> April </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2018/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2018/02/'> February </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2018/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2017/'> 2017 </a> <span class='post-count' dir='ltr'>(32)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2017/12/'> December </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2017/11/'> November </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2017/10/'> October </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2017/09/'> September </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2017/08/'> August </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2017/07/'> July </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2017/05/'> May </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2017/04/'> April </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2017/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2017/02/'> February </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2017/01/'> January </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2016/'> 2016 </a> <span class='post-count' dir='ltr'>(27)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2016/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2016/10/'> October </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2016/08/'> August </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2016/07/'> July </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2016/06/'> June </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2016/05/'> May </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2016/04/'> April </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2016/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2016/01/'> January </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2015/'> 2015 </a> <span class='post-count' dir='ltr'>(67)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2015/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2015/11/'> November </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2015/10/'> October </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2015/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2015/08/'> August </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2015/07/'> July </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2015/06/'> June </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2015/05/'> May </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2015/04/'> April </a> <span class='post-count' dir='ltr'>(13)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2015/03/'> March </a> <span class='post-count' dir='ltr'>(14)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2015/02/'> February </a> <span class='post-count' dir='ltr'>(9)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2015/01/'> January </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2014/'> 2014 </a> <span class='post-count' dir='ltr'>(14)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2014/10/'> October </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2014/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2014/08/'> August </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2014/07/'> July </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2014/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2014/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2014/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2014/02/'> February </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2014/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2013/'> 2013 </a> <span class='post-count' dir='ltr'>(18)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2013/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2013/09/'> September </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2013/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2013/07/'> July </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2013/06/'> June </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2013/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2013/03/'> March </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2013/02/'> February </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2013/01/'> January </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2012/'> 2012 </a> <span class='post-count' dir='ltr'>(21)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2012/12/'> December </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2012/11/'> November </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2012/10/'> October </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2012/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2012/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2012/07/'> July </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2012/06/'> June </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2012/05/'> May </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2012/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2012/03/'> March </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2012/01/'> January </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2011/'> 2011 </a> <span class='post-count' dir='ltr'>(55)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2011/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2011/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2011/10/'> October </a> <span class='post-count' dir='ltr'>(7)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2011/09/'> September </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2011/08/'> August </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2011/07/'> July </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2011/06/'> June </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2011/05/'> May </a> <span class='post-count' dir='ltr'>(8)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2011/04/'> April </a> <span class='post-count' dir='ltr'>(8)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2011/03/'> March </a> <span class='post-count' dir='ltr'>(13)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2011/02/'> February </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2011/01/'> January </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2010/'> 2010 </a> <span class='post-count' dir='ltr'>(35)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2010/12/'> December </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2010/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2010/10/'> October </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2010/09/'> September </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2010/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2010/07/'> July </a> <span class='post-count' dir='ltr'>(8)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2010/06/'> June </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2010/05/'> May </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2010/04/'> April </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2010/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2010/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2009/'> 2009 </a> <span class='post-count' dir='ltr'>(21)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2009/12/'> December </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2009/10/'> October </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2009/09/'> September </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2009/08/'> August </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2009/07/'> July </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2009/06/'> June </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2009/05/'> May </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2009/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2009/02/'> February </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2008/'> 2008 </a> <span class='post-count' dir='ltr'>(23)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2008/12/'> December </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2008/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2008/10/'> October </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2008/08/'> August </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2008/07/'> July </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2008/05/'> May </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2008/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2008/03/'> March </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2008/02/'> February </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2008/01/'> January </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2007/'> 2007 </a> <span class='post-count' dir='ltr'>(26)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2007/12/'> December </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2007/11/'> November </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2007/10/'> October </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2007/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2007/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2007/07/'> July </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2007/06/'> June </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2007/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2007/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2007/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2007/02/'> February </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2007/01/'> January </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2006/'> 2006 </a> <span class='post-count' dir='ltr'>(39)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2006/12/'> December </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2006/11/'> November </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2006/10/'> October </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2006/09/'> September </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2006/08/'> August </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2006/07/'> July </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2006/05/'> May </a> <span class='post-count' dir='ltr'>(7)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2006/04/'> April </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://pyfound.blogspot.com/2006/03/'> March </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> </li> </ul> </div> </div> <div class='clear'></div> </div> </div></div> <table border='0' cellpadding='0' cellspacing='0' class='section-columns columns-2'> <tbody> <tr> <td class='first columns-cell'> <div class='sidebar no-items section' id='sidebar-right-2-1'></div> </td> <td class='columns-cell'> <div class='sidebar no-items section' id='sidebar-right-2-2'></div> </td> </tr> </tbody> </table> <div class='sidebar no-items section' id='sidebar-right-3'></div> </aside> </div> </div> </div> <div style='clear: both'></div> <!-- columns --> </div> <!-- main --> </div> </div> <div class='main-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <footer> <div class='footer-outer'> <div class='footer-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left footer-fauxborder-left'> <div class='fauxborder-right footer-fauxborder-right'></div> <div class='region-inner footer-inner'> <div class='foot no-items section' id='footer-1'></div> <table border='0' cellpadding='0' cellspacing='0' class='section-columns columns-2'> <tbody> <tr> <td class='first columns-cell'> <div class='foot no-items section' id='footer-2-1'></div> </td> <td class='columns-cell'> <div class='foot no-items section' id='footer-2-2'></div> </td> </tr> </tbody> </table> <!-- outside of the include in order to lock Attribution widget --> <div class='foot section' id='footer-3' name='Footer'><div class='widget Attribution' data-version='1' id='Attribution1'> <div class='widget-content' style='text-align: center;'> Powered by <a href='https://www.blogger.com' target='_blank'>Blogger</a>. </div> <div class='clear'></div> </div></div> </div> </div> <div class='footer-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </footer> <!-- content --> </div> </div> <div class='content-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <script type='text/javascript'> window.setTimeout(function() { document.body.className = document.body.className.replace('loading', ''); }, 10); </script> <script type="text/javascript" src="https://www.blogger.com/static/v1/widgets/984859869-widgets.js"></script> <script type='text/javascript'> window['__wavt'] = 'AOuZoY4E972y8J321S8-jU4Miw15AC33uQ:1732451125347';_WidgetManager._Init('//www.blogger.com/rearrange?blogID\x3d8520','//pyfound.blogspot.com/2024/06/','8520'); _WidgetManager._SetDataContext([{'name': 'blog', 'data': {'blogId': '8520', 'title': 'Python Software Foundation News', 'url': 'https://pyfound.blogspot.com/2024/06/', 'canonicalUrl': 'https://pyfound.blogspot.com/2024/06/', 'homepageUrl': 'https://pyfound.blogspot.com/', 'searchUrl': 'https://pyfound.blogspot.com/search', 'canonicalHomepageUrl': 'https://pyfound.blogspot.com/', 'blogspotFaviconUrl': 'https://pyfound.blogspot.com/favicon.ico', 'bloggerUrl': 'https://www.blogger.com', 'hasCustomDomain': false, 'httpsEnabled': true, 'enabledCommentProfileImages': true, 'gPlusViewType': 'FILTERED_POSTMOD', 'adultContent': false, 'analyticsAccountNumber': 'UA-55961911-3', 'encoding': 'UTF-8', 'locale': 'en', 'localeUnderscoreDelimited': 'en', 'languageDirection': 'ltr', 'isPrivate': false, 'isMobile': false, 'isMobileRequest': false, 'mobileClass': '', 'isPrivateBlog': false, 'isDynamicViewsAvailable': false, 'feedLinks': '\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Python Software Foundation News - Atom\x22 href\x3d\x22https://pyfound.blogspot.com/feeds/posts/default\x22 /\x3e\n\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/rss+xml\x22 title\x3d\x22Python Software Foundation News - RSS\x22 href\x3d\x22https://pyfound.blogspot.com/feeds/posts/default?alt\x3drss\x22 /\x3e\n\x3clink rel\x3d\x22service.post\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Python Software Foundation News - Atom\x22 href\x3d\x22https://www.blogger.com/feeds/8520/posts/default\x22 /\x3e\n', 'meTag': '', 'adsenseHostId': 'ca-host-pub-1556223355139109', 'adsenseHasAds': false, 'adsenseAutoAds': false, 'boqCommentIframeForm': true, 'loginRedirectParam': '', 'view': '', 'dynamicViewsCommentsSrc': '//www.blogblog.com/dynamicviews/4224c15c4e7c9321/js/comments.js', 'dynamicViewsScriptSrc': '//www.blogblog.com/dynamicviews/d78375fb222d99b3', 'plusOneApiSrc': 'https://apis.google.com/js/platform.js', 'disableGComments': true, 'interstitialAccepted': false, 'sharing': {'platforms': [{'name': 'Get link', 'key': 'link', 'shareMessage': 'Get link', 'target': ''}, {'name': 'Facebook', 'key': 'facebook', 'shareMessage': 'Share to Facebook', 'target': 'facebook'}, {'name': 'BlogThis!', 'key': 'blogThis', 'shareMessage': 'BlogThis!', 'target': 'blog'}, {'name': 'X', 'key': 'twitter', 'shareMessage': 'Share to X', 'target': 'twitter'}, {'name': 'Pinterest', 'key': 'pinterest', 'shareMessage': 'Share to Pinterest', 'target': 'pinterest'}, {'name': 'Email', 'key': 'email', 'shareMessage': 'Email', 'target': 'email'}], 'disableGooglePlus': true, 'googlePlusShareButtonWidth': 0, 'googlePlusBootstrap': '\x3cscript type\x3d\x22text/javascript\x22\x3ewindow.___gcfg \x3d {\x27lang\x27: \x27en\x27};\x3c/script\x3e'}, 'hasCustomJumpLinkMessage': false, 'jumpLinkMessage': 'Read more', 'pageType': 'archive', 'pageName': '06/01/2024 - 07/01/2024', 'pageTitle': 'Python Software Foundation News: 06/01/2024 - 07/01/2024'}}, {'name': 'features', 'data': {}}, {'name': 'messages', 'data': {'edit': 'Edit', 'linkCopiedToClipboard': 'Link copied to clipboard!', 'ok': 'Ok', 'postLink': 'Post Link'}}, {'name': 'template', 'data': {'name': 'custom', 'localizedName': 'Custom', 'isResponsive': false, 'isAlternateRendering': false, 'isCustom': true}}, {'name': 'view', 'data': {'classic': {'name': 'classic', 'url': '?view\x3dclassic'}, 'flipcard': {'name': 'flipcard', 'url': '?view\x3dflipcard'}, 'magazine': {'name': 'magazine', 'url': '?view\x3dmagazine'}, 'mosaic': {'name': 'mosaic', 'url': '?view\x3dmosaic'}, 'sidebar': {'name': 'sidebar', 'url': '?view\x3dsidebar'}, 'snapshot': {'name': 'snapshot', 'url': '?view\x3dsnapshot'}, 'timeslide': {'name': 'timeslide', 'url': '?view\x3dtimeslide'}, 'isMobile': false, 'title': 'Python Software Foundation News', 'description': '\xa0\xa0News from the Python Software Foundation', 'url': 'https://pyfound.blogspot.com/2024/06/', 'type': 'feed', 'isSingleItem': false, 'isMultipleItems': true, 'isError': false, 'isPage': false, 'isPost': false, 'isHomepage': false, 'isArchive': true, 'isLabelSearch': false, 'archive': {'year': 2024, 'month': 6, 'rangeMessage': 'Showing posts from June, 2024'}}}]); _WidgetManager._RegisterWidget('_NavbarView', new _WidgetInfo('Navbar1', 'navbar', document.getElementById('Navbar1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_HeaderView', new _WidgetInfo('Header1', 'header', document.getElementById('Header1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogView', new _WidgetInfo('Blog1', 'main', document.getElementById('Blog1'), {'cmtInteractionsEnabled': false, 'lightboxEnabled': true, 'lightboxModuleUrl': 'https://www.blogger.com/static/v1/jsbin/2646514562-lbx.js', 'lightboxCssUrl': 'https://www.blogger.com/static/v1/v-css/1964470060-lightbox_bundle.css'}, 'displayModeFull')); _WidgetManager._RegisterWidget('_TextView', new _WidgetInfo('Text1', 'sidebar-right-1', document.getElementById('Text1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_LinkListView', new _WidgetInfo('LinkList1', 'sidebar-right-1', document.getElementById('LinkList1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_ImageView', new _WidgetInfo('Image1', 'sidebar-right-1', document.getElementById('Image1'), {'resize': true}, 'displayModeFull')); _WidgetManager._RegisterWidget('_HTMLView', new _WidgetInfo('HTML1', 'sidebar-right-1', document.getElementById('HTML1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogArchiveView', new _WidgetInfo('BlogArchive1', 'sidebar-right-1', document.getElementById('BlogArchive1'), {'languageDirection': 'ltr', 'loadingMessage': 'Loading\x26hellip;'}, 'displayModeFull')); _WidgetManager._RegisterWidget('_AttributionView', new _WidgetInfo('Attribution1', 'footer-3', document.getElementById('Attribution1'), {}, 'displayModeFull')); </script> </body> </html>