From af33d311fc5d58658f8c99084f3b19371c25e919 Mon Sep 17 00:00:00 2001 From: Ee Durbin Date: Mon, 16 Sep 2024 09:02:04 -0400 Subject: [PATCH 1/6] Remove peps app. (#2552) all peps were moved to peps.python.org, so this machinery is no longer necessary --- peps/__init__.py | 0 peps/apps.py | 6 - peps/converters.py | 262 ----- peps/management/__init__.py | 0 peps/management/commands/__init__.py | 0 peps/management/commands/dump_pep_pages.py | 21 - .../management/commands/generate_pep_pages.py | 143 --- peps/models.py | 1 - peps/templatetags/__init__.py | 0 peps/templatetags/peps.py | 16 - peps/tests/__init__.py | 6 - peps/tests/peps.tar.gz | Bin 46010 -> 0 bytes peps/tests/peps/pep-0000.html | 1030 ----------------- peps/tests/peps/pep-0012.html | 53 - peps/tests/peps/pep-0012.rst | 33 - peps/tests/peps/pep-0525.html | 595 ---------- peps/tests/peps/pep-3001-1.png | Bin 14117 -> 0 bytes peps/tests/peps/pep-3001.html | 140 --- peps/tests/test_commands.py | 56 - peps/tests/test_converters.py | 64 - pydotorg/settings/base.py | 5 - templates/components/pep-widget.html | 19 - templates/python/documentation.html | 3 - templates/python/index.html | 2 - 24 files changed, 2455 deletions(-) delete mode 100644 peps/__init__.py delete mode 100644 peps/apps.py delete mode 100644 peps/converters.py delete mode 100644 peps/management/__init__.py delete mode 100644 peps/management/commands/__init__.py delete mode 100644 peps/management/commands/dump_pep_pages.py delete mode 100644 peps/management/commands/generate_pep_pages.py delete mode 100644 peps/models.py delete mode 100644 peps/templatetags/__init__.py delete mode 100644 peps/templatetags/peps.py delete mode 100644 peps/tests/__init__.py delete mode 100644 peps/tests/peps.tar.gz delete mode 100644 peps/tests/peps/pep-0000.html delete mode 100644 peps/tests/peps/pep-0012.html delete mode 100644 peps/tests/peps/pep-0012.rst delete mode 100644 peps/tests/peps/pep-0525.html delete mode 100644 peps/tests/peps/pep-3001-1.png delete mode 100644 peps/tests/peps/pep-3001.html delete mode 100644 peps/tests/test_commands.py delete mode 100644 peps/tests/test_converters.py delete mode 100644 templates/components/pep-widget.html diff --git a/peps/__init__.py b/peps/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/peps/apps.py b/peps/apps.py deleted file mode 100644 index a59996f2a..000000000 --- a/peps/apps.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.apps import AppConfig - - -class PepsAppConfig(AppConfig): - - name = 'peps' diff --git a/peps/converters.py b/peps/converters.py deleted file mode 100644 index 1d63d7438..000000000 --- a/peps/converters.py +++ /dev/null @@ -1,262 +0,0 @@ -import functools -import datetime -import re -import os - -from bs4 import BeautifulSoup - -from django.conf import settings -from django.core.exceptions import ImproperlyConfigured -from django.core.files import File -from django.db.models import Max - -from pages.models import Page, Image - -PEP_TEMPLATE = 'pages/pep-page.html' -pep_url = lambda num: f'dev/peps/pep-{num}/' - - -def get_peps_last_updated(): - last_update = Page.objects.filter( - path__startswith='dev/peps', - ).aggregate(Max('updated')).get('updated__max') - if last_update is None: - return datetime.datetime( - 1970, 1, 1, tzinfo=datetime.timezone( - datetime.timedelta(0) - ) - ) - return last_update - - -def convert_pep0(artifact_path): - """ - Take existing generated pep-0000.html and convert to something suitable - for a Python.org Page returns the core body HTML necessary only - """ - pep0_path = os.path.join(artifact_path, 'pep-0000.html') - pep0_content = open(pep0_path).read() - data = convert_pep_page(0, pep0_content) - if data is None: - return - return data['content'] - - -def get_pep0_page(artifact_path, commit=True): - """ - Using convert_pep0 above, create a CMS ready pep0 page and return it - - pep0 is used as the directory index, but it's also an actual pep, so we - return both Page objects. - """ - pep0_content = convert_pep0(artifact_path) - if pep0_content is None: - return None, None - pep0_page, _ = Page.objects.get_or_create(path='dev/peps/') - pep0000_page, _ = Page.objects.get_or_create(path='dev/peps/pep-0000/') - for page in [pep0_page, pep0000_page]: - page.content = pep0_content - page.content_markup_type = 'html' - page.title = "PEP 0 -- Index of Python Enhancement Proposals (PEPs)" - page.template_name = PEP_TEMPLATE - - if commit: - page.save() - - return pep0_page, pep0000_page - - -def fix_headers(soup, data): - """ Remove empty or unwanted headers and find our title """ - header_rows = soup.find_all('th') - for t in header_rows: - if 'Version:' in t.text: - if t.next_sibling.text == '$Revision$': - t.parent.extract() - if t.next_sibling.text == '': - t.parent.extract() - if 'Last-Modified:' in t.text: - if '$Date$'in t.next_sibling.text: - t.parent.extract() - if t.next_sibling.text == '': - t.parent.extract() - if t.text == 'Title:': - data['title'] = t.next_sibling.text - if t.text == 'Content-Type:': - t.parent.extract() - if 'Version:' in t.text and 'N/A' in t.next_sibling.text: - t.parent.extract() - - return soup, data - - -def convert_pep_page(pep_number, content): - """ - Handle different formats that pep2html.py outputs - """ - data = { - 'title': None, - } - # Remove leading zeros from PEP number for display purposes - pep_number_humanize = re.sub(r'^0+', '', str(pep_number)) - - if '' in content: - soup = BeautifulSoup(content, 'lxml') - data['title'] = soup.title.text - - if not re.search(r'PEP \d+', data['title']): - data['title'] = 'PEP {} -- {}'.format( - pep_number_humanize, - soup.title.text, - ) - - header = soup.body.find('div', class_="header") - header, data = fix_headers(header, data) - data['header'] = str(header) - - main_content = soup.body.find('div', class_="content") - - data['main_content'] = str(main_content) - data['content'] = ''.join([ - data['header'], - data['main_content'] - ]) - - else: - soup = BeautifulSoup(content, 'lxml') - - soup, data = fix_headers(soup, data) - if not data['title']: - data['title'] = f"PEP {pep_number_humanize} -- " - else: - if not re.search(r'PEP \d+', data['title']): - data['title'] = "PEP {} -- {}".format( - pep_number_humanize, - data['title'], - ) - - data['content'] = str(soup) - - # Fix PEP links - pep_content = BeautifulSoup(data['content'], 'lxml') - body_links = pep_content.find_all("a") - - pep_href_re = re.compile(r'pep-(\d+)\.html') - - for b in body_links: - m = pep_href_re.search(b.attrs['href']) - - # Skip anything not matching 'pep-XXXX.html' - if not m: - continue - - b.attrs['href'] = f'/dev/peps/pep-{m.group(1)}/' - - # Return early if 'html' or 'body' return None. - if pep_content.html is None or pep_content.body is None: - return - - # Strip and tags. - pep_content.html.unwrap() - pep_content.body.unwrap() - - data['content'] = str(pep_content) - return data - - -def get_pep_page(artifact_path, pep_number, commit=True): - """ - Given a pep_number retrieve original PEP source text, rst, or html. - Get or create the associated Page and return it - """ - pep_path = os.path.join(artifact_path, f'pep-{pep_number}.html') - if not os.path.exists(pep_path): - print(f"PEP Path '{pep_path}' does not exist, skipping") - return - - pep_content = convert_pep_page(pep_number, open(pep_path).read()) - if pep_content is None: - return None - pep_rst_source = os.path.join( - artifact_path, f'pep-{pep_number}.rst', - ) - pep_ext = '.rst' if os.path.exists(pep_rst_source) else '.txt' - source_link = 'https://github.com/python/peps/blob/master/pep-{}{}'.format( - pep_number, pep_ext) - pep_content['content'] += """Source: {0}""".format(source_link) - - pep_page, _ = Page.objects.get_or_create(path=pep_url(pep_number)) - - pep_page.title = pep_content['title'] - - pep_page.content = pep_content['content'] - pep_page.content_markup_type = 'html' - pep_page.template_name = PEP_TEMPLATE - - if commit: - pep_page.save() - - return pep_page - - -def add_pep_image(artifact_path, pep_number, path): - image_path = os.path.join(artifact_path, path) - if not os.path.exists(image_path): - print(f"Image Path '{image_path}' does not exist, skipping") - return - - try: - page = Page.objects.get(path=pep_url(pep_number)) - except Page.DoesNotExist: - print(f"Could not find backing PEP {pep_number}") - return - - # Find existing images, we have to loop here as we can't use the ORM - # to query against image__path - existing_images = Image.objects.filter(page=page) - - FOUND = False - for image in existing_images: - if image.image.name.endswith(path): - FOUND = True - break - - if not FOUND: - image = Image(page=page) - - with open(image_path, 'rb') as image_obj: - image.image.save(path, File(image_obj)) - image.save() - - # Old images used to live alongside html, but now they're in different - # places, so update the page accordingly. - soup = BeautifulSoup(page.content.raw, 'lxml') - for img_tag in soup.findAll('img'): - if img_tag['src'] == path: - img_tag['src'] = image.image.url - - page.content.raw = str(soup) - page.save() - - return image - - -def get_peps_rss(artifact_path): - rss_feed = os.path.join(artifact_path, 'peps.rss') - if not os.path.exists(rss_feed): - return - - page, _ = Page.objects.get_or_create( - path="dev/peps/peps.rss", - template_name="pages/raw.html", - ) - - with open(rss_feed) as rss_content: - content = rss_content.read() - - page.content = content - page.is_published = True - page.content_type = "application/rss+xml" - page.save() - - return page diff --git a/peps/management/__init__.py b/peps/management/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/peps/management/commands/__init__.py b/peps/management/commands/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/peps/management/commands/dump_pep_pages.py b/peps/management/commands/dump_pep_pages.py deleted file mode 100644 index 549b8faa4..000000000 --- a/peps/management/commands/dump_pep_pages.py +++ /dev/null @@ -1,21 +0,0 @@ -from django.core import serializers -from django.core.management import BaseCommand - -from pages.models import Page - - -class Command(BaseCommand): - """ - Dump PEP related Pages as indented JSON - """ - help = "Dump PEP related Pages as indented JSON" - - def handle(self, **options): - qs = Page.objects.filter(path__startswith='dev/peps/') - - serializers.serialize( - format='json', - queryset=qs, - indent=4, - stream=self.stdout, - ) diff --git a/peps/management/commands/generate_pep_pages.py b/peps/management/commands/generate_pep_pages.py deleted file mode 100644 index 9f9010584..000000000 --- a/peps/management/commands/generate_pep_pages.py +++ /dev/null @@ -1,143 +0,0 @@ -import re -import os - -from contextlib import ExitStack -from tarfile import TarFile -from tempfile import TemporaryDirectory, TemporaryFile - -import requests - -from django.core.management import BaseCommand -from django.conf import settings - -from dateutil.parser import parse as parsedate - -from peps.converters import ( - get_pep0_page, get_pep_page, add_pep_image, get_peps_rss, get_peps_last_updated -) - -pep_number_re = re.compile(r'pep-(\d+)') - - -class Command(BaseCommand): - """ - Generate CMS Pages from flat file PEP data. - - Run this command AFTER normal RST -> HTML PEP transformation from the PEP - repository has happened. This works on the HTML files created during that - process. - - For verbose output run this with: - - ./manage.py generate_pep_pages --verbosity=2 - """ - help = "Generate PEP Page objects from rendered HTML" - - def is_pep_page(self, path): - return path.startswith('pep-') and path.endswith('.html') - - def is_image(self, path): - # All images are pngs - return path.endswith('.png') - - def handle(self, **options): - verbosity = int(options['verbosity']) - - def verbose(msg): - """ Output wrapper """ - if verbosity > 1: - print(msg) - - verbose("== Starting PEP page generation") - - with ExitStack() as stack: - if settings.PEP_REPO_PATH is not None: - artifacts_path = settings.PEP_REPO_PATH - else: - verbose(f"== Fetching PEP artifact from {settings.PEP_ARTIFACT_URL}") - temp_file = self.get_artifact_tarball(stack) - if not temp_file: - verbose("== No update to artifacts, we're done here!") - return - temp_dir = stack.enter_context(TemporaryDirectory()) - tar_ball = stack.enter_context(TarFile.open(fileobj=temp_file, mode='r:gz')) - tar_ball.extractall(path=temp_dir, numeric_owner=False) - - artifacts_path = os.path.join(temp_dir, 'peps') - - verbose("Generating RSS Feed") - peps_rss = get_peps_rss(artifacts_path) - if not peps_rss: - verbose("Could not find generated RSS feed. Skipping.") - - verbose("Generating PEP0 index page") - pep0_page, _ = get_pep0_page(artifacts_path) - if pep0_page is None: - verbose("HTML version of PEP 0 cannot be generated.") - return - - image_paths = set() - - # Find pep pages - for f in os.listdir(artifacts_path): - - if self.is_image(f): - verbose(f"- Deferring import of image '{f}'") - image_paths.add(f) - continue - - # Skip files we aren't looking for - if not self.is_pep_page(f): - verbose(f"- Skipping non-PEP file '{f}'") - continue - - if 'pep-0000.html' in f: - verbose("- Skipping duplicate PEP0 index") - continue - - verbose(f"Generating PEP Page from '{f}'") - pep_match = pep_number_re.match(f) - if pep_match: - pep_number = pep_match.groups(1)[0] - p = get_pep_page(artifacts_path, pep_number) - if p is None: - verbose( - "- HTML version PEP {!r} cannot be generated.".format( - pep_number - ) - ) - verbose(f"====== Title: '{p.title}'") - else: - verbose(f"- Skipping invalid '{f}'") - - # Find pep images. This needs to happen afterwards, because we need - for img in image_paths: - pep_match = pep_number_re.match(img) - if pep_match: - pep_number = pep_match.groups(1)[0] - verbose("Generating image for PEP {} at '{}'".format( - pep_number, img)) - add_pep_image(artifacts_path, pep_number, img) - else: - verbose(f"- Skipping non-PEP related image '{img}'") - - verbose("== Finished") - - def get_artifact_tarball(self, stack): - artifact_url = settings.PEP_ARTIFACT_URL - if not artifact_url.startswith(('http://', 'https://')): - return stack.enter_context(open(artifact_url, 'rb')) - - peps_last_updated = get_peps_last_updated() - with requests.get(artifact_url, stream=True) as r: - artifact_last_modified = parsedate(r.headers['last-modified']) - if peps_last_updated > artifact_last_modified: - return - - temp_file = stack.enter_context(TemporaryFile()) - for chunk in r.iter_content(chunk_size=8192): - if chunk: - temp_file.write(chunk) - - temp_file.seek(0) - return temp_file diff --git a/peps/models.py b/peps/models.py deleted file mode 100644 index e45cd9cd1..000000000 --- a/peps/models.py +++ /dev/null @@ -1 +0,0 @@ -# Intentially left blank diff --git a/peps/templatetags/__init__.py b/peps/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/peps/templatetags/peps.py b/peps/templatetags/peps.py deleted file mode 100644 index 9d90afe24..000000000 --- a/peps/templatetags/peps.py +++ /dev/null @@ -1,16 +0,0 @@ -from django import template - -from pages.models import Page - -register = template.Library() - - -@register.simple_tag -def get_newest_pep_pages(limit=5): - """ Retrieve the most recently added PEPs """ - latest_peps = Page.objects.filter( - path__startswith='dev/peps/', - is_published=True, - ).order_by('-created')[:limit] - - return latest_peps diff --git a/peps/tests/__init__.py b/peps/tests/__init__.py deleted file mode 100644 index 944cc90aa..000000000 --- a/peps/tests/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -import os - -from django.conf import settings - -FAKE_PEP_REPO = os.path.join(settings.BASE, 'peps/tests/peps/') -FAKE_PEP_ARTIFACT = os.path.join(settings.BASE, 'peps/tests/peps.tar.gz') diff --git a/peps/tests/peps.tar.gz b/peps/tests/peps.tar.gz deleted file mode 100644 index edaa86b79a67945ecf960d06657e6453f6657916..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46010 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 K3_LUdSONgDYY=k) diff --git a/peps/tests/peps/pep-0000.html b/peps/tests/peps/pep-0000.html deleted file mode 100644 index c11c027c9..000000000 --- a/peps/tests/peps/pep-0000.html +++ /dev/null @@ -1,1030 +0,0 @@ - - - - - PEP 0 -- Index of Python Enhancement Proposals (PEPs) - - - - - - -
- - - - - - - - - -
PEP: 0
Title: Index of Python Enhancement Proposals (PEPs)
Version: N/A
Last-Modified: 2014-09-26
Author: David Goodger <goodger at python.org>, Barry Warsaw <barry at python.org>
Status: Active
Type: Informational
Created: 13-Jul-2000
-
-
-
-

Introduction

-
-    This PEP contains the index of all Python Enhancement Proposals,
-    known as PEPs.  PEP numbers are assigned by the PEP editors, and
-    once assigned are never changed[1].  The Mercurial history[2] of
-    the PEP texts represent their historical record.
-
-
-
-

Index by Category

-
-     num  title                                                   owner
-     ---  -----                                                   -----
-
- Meta-PEPs (PEPs about PEPs or Processes)
-
- P     1  PEP Purpose and Guidelines                              Warsaw, Hylton, Goodger, Coghlan
- P     4  Deprecation of Standard Modules                         von Löwis
- P     5  Guidelines for Language Evolution                       Prescod
- P     6  Bug Fix Releases                                        Aahz, Baxter
- P     7  Style Guide for C Code                                  GvR
- P     8  Style Guide for Python Code                             GvR, Warsaw, Coghlan
- P     9  Sample Plaintext PEP Template                           Warsaw
- P    10  Voting Guidelines                                       Warsaw
- P    11  Removing support for little used platforms              von Löwis
- P    12  Sample reStructuredText PEP Template                    Goodger, Warsaw
-
- Other Informational PEPs
-
- I    20  The Zen of Python                                       Peters
- I   101  Doing Python Releases 101                               Warsaw, GvR
- IF  247  API for Cryptographic Hash Functions                    Kuchling
- IF  248  Python Database API Specification v1.0                  Lemburg
- IF  249  Python Database API Specification v2.0                  Lemburg
- I   257  Docstring Conventions                                   Goodger, GvR
- IF  272  API for Block Encryption Algorithms v1.0                Kuchling
- I   287  reStructuredText Docstring Format                       Goodger
- I   290  Code Migration and Modernization                        Hettinger
- IF  291  Backward Compatibility for the Python 2 Standard ...    Norwitz
- IF  333  Python Web Server Gateway Interface v1.0                Eby
- I   373  Python 2.7 Release Schedule                             Peterson
- I   392  Python 3.2 Release Schedule                             Brandl
- I   394  The "python" Command on Unix-Like Systems               Staley, Coghlan
- I   398  Python 3.3 Release Schedule                             Brandl
- IF  399  Pure Python/C Accelerator Module Compatibility ...      Cannon
- IF  404  Python 2.8 Un-release Schedule                          Warsaw
- IA  411  Provisional packages in the Python standard library     Coghlan, Bendersky
- I   429  Python 3.4 Release Schedule                             Hastings
- IF  430  Migrating to Python 3 as the default online ...         Coghlan
- I   434  IDLE Enhancement Exception for All Branches             Rovito, Reedy
- IA  440  Version Identification and Dependency Specification     Coghlan, Stufft
- I   478  Python 3.5 Release Schedule                             Hastings
- IF 3333  Python Web Server Gateway Interface v1.0.1              Eby
-
- Accepted PEPs (accepted; may not be implemented yet)
-
- SA  345  Metadata for Python Software Packages 1.2               Jones
- SA  376  Database of Installed Python Distributions              Ziadé
- SA  425  Compatibility Tags for Built Distributions              Holth
- SA  427  The Wheel Binary Package Format 1.0                     Holth
- SA  461  Adding % formatting to bytes and bytearray              Furman
- SA  471  os.scandir() function -- a better and faster ...        Hoyt
- SA  477  Backport ensurepip (PEP 453) to Python 2.7              Stufft, Coghlan
- SA 3121  Extension Module Initialization and Finalization        von Löwis
-
- Open PEPs (under consideration)
-
- S   381  Mirroring infrastructure for PyPI                       Ziadé, v. Löwis
- P   387  Backwards Compatibility Policy                          Peterson
- S   426  Metadata for Python Software Packages 2.0               Coghlan, Holth, Stufft
- S   431  Time zone support improvements                          Regebro
- S   432  Simplifying the CPython startup sequence                Coghlan
- S   436  The Argument Clinic DSL                                 Hastings
- S   441  Improving Python ZIP Application Support                Holth
- S   447  Add __getdescriptor__ method to metaclass               Oussoren
- S   448  Additional Unpacking Generalizations                    Landau
- I   452  API for Cryptographic Hash Functions v2.0               Kuchling, Heimes
- S   455  Adding a key-transforming dictionary to collections     Pitrou
- I   457  Syntax For Positional-Only Parameters                   Hastings
- S   458  Surviving a Compromise of PyPI                          Kuppusamy, Stufft, Cappos
- S   459  Standard Metadata Extensions for Python Software ...    Coghlan
- S   463  Exception-catching expressions                          Angelico
- S   467  Minor API improvements for binary sequences             Coghlan
- S   468  Preserving the order of \*\*kwargs in a function.       Snow
- P   470  Using Multi Index Support for External to PyPI ...      Stufft
- S   472  Support for indexing with keyword arguments             Borini, Martinot-Lagarde
- S   473  Adding structured data to built-in exceptions           Kreft
- S   475  Retry system calls failing with EINTR                   Natali, Stinner
- S   476  Enabling certificate verification by default for ...    Gaynor
-
- Finished PEPs (done, implemented in code repository)
-
- SF  100  Python Unicode Integration                              Lemburg
- SF  201  Lockstep Iteration                                      Warsaw
- SF  202  List Comprehensions                                     Warsaw
- SF  203  Augmented Assignments                                   Wouters
- SF  205  Weak References                                         Drake
- SF  207  Rich Comparisons                                        GvR, Ascher
- SF  208  Reworking the Coercion Model                            Schemenauer, Lemburg
- SF  214  Extended Print Statement                                Warsaw
- SF  217  Display Hook for Interactive Use                        Zadka
- SF  218  Adding a Built-In Set Object Type                       Wilson, Hettinger
- SF  221  Import As                                               Wouters
- SF  223  Change the Meaning of \x Escapes                        Peters
- SF  227  Statically Nested Scopes                                Hylton
- SF  229  Using Distutils to Build Python                         Kuchling
- SF  230  Warning Framework                                       GvR
- SF  232  Function Attributes                                     Warsaw
- SF  234  Iterators                                               Yee, GvR
- SF  235  Import on Case-Insensitive Platforms                    Peters
- SF  236  Back to the __future__                                  Peters
- SF  237  Unifying Long Integers and Integers                     Zadka, GvR
- SF  238  Changing the Division Operator                          Zadka, GvR
- SF  241  Metadata for Python Software Packages                   Kuchling
- SF  250  Using site-packages on Windows                          Moore
- SF  252  Making Types Look More Like Classes                     GvR
- SF  253  Subtyping Built-in Types                                GvR
- SF  255  Simple Generators                                       Schemenauer, Peters, Hetland
- SF  260  Simplify xrange()                                       GvR
- SF  261  Support for "wide" Unicode characters                   Prescod
- SF  263  Defining Python Source Code Encodings                   Lemburg, von Löwis
- SF  264  Future statements in simulated shells                   Hudson
- SF  273  Import Modules from Zip Archives                        Ahlstrom
- SF  274  Dict Comprehensions                                     Warsaw
- SF  277  Unicode file name support for Windows NT                Hodgson
- SF  278  Universal Newline Support                               Jansen
- SF  279  The enumerate() built-in function                       Hettinger
- SF  282  A Logging System                                        Sajip, Mick
- SF  285  Adding a bool type                                      GvR
- SF  289  Generator Expressions                                   Hettinger
- SF  292  Simpler String Substitutions                            Warsaw
- SF  293  Codec Error Handling Callbacks                          Dörwald
- SF  301  Package Index and Metadata for Distutils                Jones
- SF  302  New Import Hooks                                        JvR, Moore
- SF  305  CSV File API                                            Altis, Cole, McNamara, Montanaro, Wells
- SF  307  Extensions to the pickle protocol                       GvR, Peters
- SF  308  Conditional Expressions                                 GvR, Hettinger
- SF  309  Partial Function Application                            Harris
- SF  311  Simplified Global Interpreter Lock Acquisition for ...  Hammond
- SF  314  Metadata for Python Software Packages v1.1              Kuchling, Jones
- SF  318  Decorators for Functions and Methods                    Smith
- SF  322  Reverse Iteration                                       Hettinger
- SF  324  subprocess - New process module                         Astrand
- SF  327  Decimal Data Type                                       Batista
- SF  328  Imports: Multi-Line and Absolute/Relative               Aahz
- SF  331  Locale-Independent Float/String Conversions             Reis
- SF  338  Executing modules as scripts                            Coghlan
- SF  341  Unifying try-except and try-finally                     Brandl
- SF  342  Coroutines via Enhanced Generators                      GvR, Eby
- SF  343  The "with" Statement                                    GvR, Coghlan
- SF  352  Required Superclass for Exceptions                      Cannon, GvR
- SF  353  Using ssize_t as the index type                         von Löwis
- SF  357  Allowing Any Object to be Used for Slicing              Oliphant
- SF  358  The "bytes" Object                                      Schemenauer, GvR
- SF  362  Function Signature Object                               Cannon, Seo, Selivanov, Hastings
- SF  366  Main module explicit relative imports                   Coghlan
- SF  370  Per user site-packages directory                        Heimes
- SF  371  Addition of the multiprocessing package to the ...      Noller, Oudkerk
- SF  372  Adding an ordered dictionary to collections             Ronacher, Hettinger
- SF  378  Format Specifier for Thousands Separator                Hettinger
- SF  380  Syntax for Delegating to a Subgenerator                 Ewing
- SF  383  Non-decodable Bytes in System Character Interfaces      v. Löwis
- SF  384  Defining a Stable ABI                                   v. Löwis
- SF  389  argparse - New Command Line Parsing Module              Bethard
- SF  391  Dictionary-Based Configuration For Logging              Sajip
- SF  393  Flexible String Representation                          v. Löwis
- SF  397  Python launcher for Windows                             Hammond, v. Löwis
- SF  405  Python Virtual Environments                             Meyer
- SF  409  Suppressing exception context                           Furman
- SF  412  Key-Sharing Dictionary                                  Shannon
- SF  414  Explicit Unicode Literal for Python 3.3                 Ronacher, Coghlan
- SF  415  Implement context suppression with exception attributes Peterson
- SF  417  Including mock in the Standard Library                  Foord
- SF  418  Add monotonic time, performance counter, and ...        Simpson, Jewett, Turnbull, Stinner
- SF  420  Implicit Namespace Packages                             Smith
- SF  421  Adding sys.implementation                               Snow
- SF  424  A method for exposing a length hint                     Gaynor
- SF  428  The pathlib module -- object-oriented filesystem paths  Pitrou
- SF  435  Adding an Enum type to the Python standard library      Warsaw, Bendersky, Furman
- SF  442  Safe object finalization                                Pitrou
- SF  443  Single-dispatch generic functions                       Langa
- SF  445  Add new APIs to customize Python memory allocators      Stinner
- SF  446  Make newly created file descriptors non-inheritable     Stinner
- SF  450  Adding A Statistics Module To The Standard Library      D'Aprano
- SF  451  A ModuleSpec Type for the Import System                 Snow
- SF  453  Explicit bootstrapping of pip in Python installations   Stufft, Coghlan
- SF  454  Add a new tracemalloc module to trace Python memory ... Stinner
- SF  456  Secure and interchangeable hash algorithm               Heimes
- SF  465  A dedicated infix operator for matrix multiplication    Smith
- SF  466  Network Security Enhancements for Python 2.7.x          Coghlan
- SF 3101  Advanced String Formatting                              Talin
- SF 3102  Keyword-Only Arguments                                  Talin
- SF 3104  Access to Names in Outer Scopes                         Yee
- SF 3105  Make print a function                                   Brandl
- SF 3106  Revamping dict.keys(), .values() and .items()           GvR
- SF 3107  Function Annotations                                    Winter, Lownds
- SF 3108  Standard Library Reorganization                         Cannon
- SF 3109  Raising Exceptions in Python 3000                       Winter
- SF 3110  Catching Exceptions in Python 3000                      Winter
- SF 3111  Simple input built-in in Python 3000                    Roberge
- SF 3112  Bytes literals in Python 3000                           Orendorff
- SF 3113  Removal of Tuple Parameter Unpacking                    Cannon
- SF 3114  Renaming iterator.next() to iterator.__next__()         Yee
- SF 3115  Metaclasses in Python 3000                              Talin
- SF 3116  New I/O                                                 Stutzbach, GvR, Verdone
- SF 3118  Revising the buffer protocol                            Oliphant, Banks
- SF 3119  Introducing Abstract Base Classes                       GvR, Talin
- SF 3120  Using UTF-8 as the default source encoding              von Löwis
- SF 3123  Making PyObject_HEAD conform to standard C              von Löwis
- SF 3127  Integer Literal Support and Syntax                      Maupin
- SF 3129  Class Decorators                                        Winter
- SF 3131  Supporting Non-ASCII Identifiers                        von Löwis
- SF 3132  Extended Iterable Unpacking                             Brandl
- SF 3134  Exception Chaining and Embedded Tracebacks              Yee
- SF 3135  New Super                                               Spealman, Delaney, Ryan
- SF 3137  Immutable Bytes and Mutable Buffer                      GvR
- SF 3138  String representation in Python 3000                    Ishimoto
- SF 3141  A Type Hierarchy for Numbers                            Yasskin
- SF 3144  IP Address Manipulation Library for the Python ...      Moody
- SF 3147  PYC Repository Directories                              Warsaw
- SF 3148  futures - execute computations asynchronously           Quinlan
- SF 3149  ABI version tagged .so files                            Warsaw
- SF 3151  Reworking the OS and IO exception hierarchy             Pitrou
- SF 3154  Pickle protocol version 4                               Pitrou
- SF 3155  Qualified name for classes and functions                Pitrou
- SF 3156  Asynchronous IO Support Rebooted: the "asyncio" Module  GvR
-
- Historical Meta-PEPs and Informational PEPs
-
- PF    2  Procedure for Adding New Modules                        Faassen
- PF   42  Feature Requests                                        Hylton
- IF  160  Python 1.6 Release Schedule                             Drake
- IF  200  Python 2.0 Release Schedule                             Hylton
- IF  226  Python 2.1 Release Schedule                             Hylton
- IF  251  Python 2.2 Release Schedule                             Warsaw, GvR
- IF  283  Python 2.3 Release Schedule                             GvR
- IF  320  Python 2.4 Release Schedule                             Warsaw, Hettinger, Baxter
- PF  347  Migrating the Python CVS to Subversion                  von Löwis
- IF  356  Python 2.5 Release Schedule                             Norwitz, GvR, Baxter
- PF  360  Externally Maintained Packages                          Cannon
- IF  361  Python 2.6 and 3.0 Release Schedule                     Norwitz, Warsaw
- PF  374  Choosing a distributed VCS for the Python project       Cannon, Turnbull, Vassalotti, Warsaw, Ochtman
- IF  375  Python 3.1 Release Schedule                             Peterson
- PF  385  Migrating from Subversion to Mercurial                  Ochtman, Pitrou, Brandl
- PA  438  Transitioning to release-file hosting on PyPI           Krekel, Meyer
- PA  449  Removal of the PyPI Mirror Auto Discovery and ...       Stufft
- PA  464  Removal of the PyPI Mirror Authenticity API             Stufft
- PF 3000  Python 3000                                             GvR
- PF 3002  Procedure for Backwards-Incompatible Changes            Bethard
- PF 3003  Python Language Moratorium                              Cannon, Noller, GvR
- PF 3099  Things that will Not Change in Python 3000              Brandl
- PF 3100  Miscellaneous Python 3.0 Plans                          Cannon
-
- Deferred PEPs
-
- SD  211  Adding A New Outer Product Operator                     Wilson
- SD  212  Loop Counter Iteration                                  Schneider-Kamp
- SD  213  Attribute Access Handlers                               Prescod
- SD  219  Stackless Python                                        McMillan
- SD  222  Web Library Enhancements                                Kuchling
- SD  225  Elementwise/Objectwise Operators                        Zhu, Lielens
- SD  233  Python Online Help                                      Prescod
- SD  262  A Database of Installed Python Packages                 Kuchling
- SD  267  Optimized Access to Module Namespaces                   Hylton
- SD  269  Pgen Module for Python                                  Riehl
- SD  280  Optimizing access to globals                            GvR
- SD  286  Enhanced Argument Tuples                                von Löwis
- SD  312  Simple Implicit Lambda                                  Suzi, Martelli
- SD  316  Programming by Contract for Python                      Way
- SD  323  Copyable Iterators                                      Martelli
- SD  337  Logging Usage in the Standard Library                   Dubner
- SD  349  Allow str() to return unicode strings                   Schemenauer
- SD  368  Standard image protocol and class                       Mastrodomenico
- ID  396  Module Version Numbers                                  Warsaw
- SD  400  Deprecate codecs.StreamReader and codecs.StreamWriter   Stinner
- SD  403  General purpose decorator clause (aka "@in" clause)     Coghlan
- PD  407  New release cycle and introducing long-term support ... Pitrou, Brandl, Warsaw
- SD  419  Protecting cleanup statements from interruptions        Colomiets
- SD  422  Simpler customisation of class creation                 Coghlan, Urban
- ID  423  Naming conventions and recipes related to packaging     Bryon
- ID  444  Python Web3 Interface                                   McDonough, Ronacher
- PD  462  Core development workflow automation for CPython        Coghlan
- PD  474  Creating forge.python.org                               Coghlan
- SD  628  Add ``math.tau``                                        Coghlan
- SD 3124  Overloading, Generic Functions, Interfaces, and ...     Eby
- SD 3143  Standard daemon process library                         Finney
- SD 3150  Statement local namespaces (aka "given" clause)         Coghlan
- SD 3152  Cofunctions                                             Ewing
-
- Abandoned, Withdrawn, and Rejected PEPs
-
- PW    3  Guidelines for Handling Bug Reports                     Hylton
- IS  102  Doing Python Micro Releases                             Baxter, Warsaw, GvR
- SR  204  Range Literals                                          Wouters
- IW  206  Python Advanced Library                                 Kuchling
- SW  209  Multi-dimensional Arrays                                Barrett, Oliphant
- SR  210  Decoupling the Interpreter Loop                         Ascher
- SS  215  String Interpolation                                    Yee
- IR  216  Docstring Format                                        Zadka
- IR  220  Coroutines, Generators, Continuations                   McMillan
- SR  224  Attribute Docstrings                                    Lemburg
- SW  228  Reworking Python's Numeric Model                        Zadka, GvR
- SR  231  __findattr__()                                          Warsaw
- SR  239  Adding a Rational Type to Python                        Craig, Zadka
- SR  240  Adding a Rational Literal to Python                     Craig, Zadka
- SR  242  Numeric Kinds                                           Dubois
- SW  243  Module Repository Upload Mechanism                      Reifschneider
- SR  244  The `directive' statement                               von Löwis
- SR  245  Python Interface Syntax                                 Pelletier
- SR  246  Object Adaptation                                       Martelli, Evans
- SR  254  Making Classes Look More Like Types                     GvR
- SR  256  Docstring Processing System Framework                   Goodger
- SR  258  Docutils Design Specification                           Goodger
- SR  259  Omit printing newline after newline                     GvR
- SR  265  Sorting Dictionaries by Value                           Griffin
- SW  266  Optimizing Global Variable/Attribute Access             Montanaro
- SR  268  Extended HTTP functionality and WebDAV                  Stein
- SR  270  uniq method for list objects                            Petrone
- SR  271  Prefixing sys.path by command line option               Giacometti
- SR  275  Switching on Multiple Values                            Lemburg
- SR  276  Simple Iterator for ints                                Althoff
- SR  281  Loop Counter Iteration with range and xrange            Hetland
- SR  284  Integer for-loops                                       Eppstein, Ewing
- SW  288  Generators Attributes and Exceptions                    Hettinger
- SR  294  Type Names in the types Module                          Tirosh
- SR  295  Interpretation of multiline string constants            Koltsov
- SW  296  Adding a bytes Object Type                              Gilbert
- SR  297  Support for System Upgrades                             Lemburg
- SW  298  The Locked Buffer Interface                             Heller
- SR  299  Special __main__() function in modules                  Epler
- SR  303  Extend divmod() for Multiple Divisors                   Bellman
- SW  304  Controlling Generation of Bytecode Files                Montanaro
- IW  306  How to Change Python's Grammar                          Hudson, Diederich, Coghlan, Peterson
- SR  310  Reliable Acquisition/Release Pairs                      Hudson, Moore
- SR  313  Adding Roman Numeral Literals to Python                 Meyer
- SR  315  Enhanced While Loop                                     Hettinger, Carroll
- SR  317  Eliminate Implicit Exception Instantiation              Taschuk
- SR  319  Python Synchronize/Asynchronize Block                   Pelletier
- SW  321  Date/Time Parsing and Formatting                        Kuchling
- SR  325  Resource-Release Support for Generators                 Pedroni
- SR  326  A Case for Top and Bottom Values                        Carlson, Reedy
- SR  329  Treating Builtins as Constants in the Standard Library  Hettinger
- SR  330  Python Bytecode Verification                            Pelletier
- SR  332  Byte vectors and String/Unicode Unification             Montanaro
- SW  334  Simple Coroutines via SuspendIteration                  Evans
- SR  335  Overloadable Boolean Operators                          Ewing
- SR  336  Make None Callable                                      McClelland
- IW  339  Design of the CPython Compiler                          Cannon
- SR  340  Anonymous Block Statements                              GvR
- SS  344  Exception Chaining and Embedded Tracebacks              Yee
- SW  346  User Defined ("``with``") Statements                    Coghlan
- SR  348  Exception Reorganization for Python 3.0                 Cannon
- IR  350  Codetags                                                Elliott
- SR  351  The freeze protocol                                     Warsaw
- SS  354  Enumerations in Python                                  Finney
- SR  355  Path - Object oriented filesystem paths                 Lindqvist
- SW  359  The "make" Statement                                    Bethard
- SR  363  Syntax For Dynamic Attribute Access                     North
- SW  364  Transitioning to the Py3K Standard Library              Warsaw
- SR  365  Adding the pkg_resources module                         Eby
- SS  367  New Super                                               Spealman, Delaney
- SW  369  Post import hooks                                       Heimes
- SR  377  Allow __enter__() methods to skip the statement body    Coghlan
- SW  379  Adding an Assignment Expression                         Whitley
- SR  382  Namespace Packages                                      v. Löwis
- SS  386  Changing the version comparison module in Distutils     Ziadé
- SR  390  Static metadata for Distutils                           Ziadé
- SW  395  Qualified Names for Modules                             Coghlan
- PR  401  BDFL Retirement                                         Warsaw, Cannon
- SR  402  Simplified Package Layout and Partitioning              Eby
- SW  406  Improved Encapsulation of Import State                  Coghlan, Slodkowicz
- SR  408  Standard library __preview__ package                    Coghlan, Bendersky
- SR  410  Use decimal.Decimal type for timestamps                 Stinner
- PW  413  Faster evolution of the Python Standard Library         Coghlan
- SR  416  Add a frozendict builtin type                           Stinner
- SS  433  Easier suppression of file descriptor inheritance       Stinner
- SR  437  A DSL for specifying signatures, annotations and ...    Krah
- SR  439  Inclusion of implicit pip bootstrap in Python ...       Jones
- SW  460  Add binary interpolation and formatting                 Pitrou
- SW  469  Migration of dict iteration code to Python 3            Coghlan
- SR  666  Reject Foolish Indentation                              Creighton
- SR  754  IEEE 754 Floating Point Special Values                  Warnes
- PW 3001  Procedure for reviewing and improving standard ...      Brandl
- SR 3103  A Switch/Case Statement                                 GvR
- SR 3117  Postfix type declarations                               Brandl
- SR 3122  Delineation of the main module                          Cannon
- SR 3125  Remove Backslash Continuation                           Jewett
- SR 3126  Remove Implicit String Concatenation                    Jewett, Hettinger
- SR 3128  BList: A Faster List-like Type                          Stutzbach
- SR 3130  Access to Current Module/Class/Function                 Jewett
- SR 3133  Introducing Roles                                       Winter
- SR 3136  Labeled break and continue                              Chisholm
- SR 3139  Cleaning out sys and the "interpreter" module           Peterson
- SR 3140  str(container) should call str(item), not repr(item)    Broytmann, Jewett
- SR 3142  Add a "while" clause to generator expressions           Britton
- SW 3145  Asynchronous I/O For subprocess.Popen                   Pruitt, McCreary, Carlson
- SW 3146  Merging Unladen Swallow into CPython                    Winter, Yasskin, Kleckner
- SS 3153  Asynchronous IO support                                 Houtven
-
-
-
-

Numerical Index

-
-     num  title                                                   owner
-     ---  -----                                                   -----
- P     1  PEP Purpose and Guidelines                              Warsaw, Hylton, Goodger, Coghlan
- PF    2  Procedure for Adding New Modules                        Faassen
- PW    3  Guidelines for Handling Bug Reports                     Hylton
- P     4  Deprecation of Standard Modules                         von Löwis
- P     5  Guidelines for Language Evolution                       Prescod
- P     6  Bug Fix Releases                                        Aahz, Baxter
- P     7  Style Guide for C Code                                  GvR
- P     8  Style Guide for Python Code                             GvR, Warsaw, Coghlan
- P     9  Sample Plaintext PEP Template                           Warsaw
- P    10  Voting Guidelines                                       Warsaw
- P    11  Removing support for little used platforms              von Löwis
- P    12  Sample reStructuredText PEP Template                    Goodger, Warsaw
-
- I    20  The Zen of Python                                       Peters
-
- PF   42  Feature Requests                                        Hylton
-
- SF  100  Python Unicode Integration                              Lemburg
- I   101  Doing Python Releases 101                               Warsaw, GvR
- IS  102  Doing Python Micro Releases                             Baxter, Warsaw, GvR
-
- IF  160  Python 1.6 Release Schedule                             Drake
-
- IF  200  Python 2.0 Release Schedule                             Hylton
- SF  201  Lockstep Iteration                                      Warsaw
- SF  202  List Comprehensions                                     Warsaw
- SF  203  Augmented Assignments                                   Wouters
- SR  204  Range Literals                                          Wouters
- SF  205  Weak References                                         Drake
- IW  206  Python Advanced Library                                 Kuchling
- SF  207  Rich Comparisons                                        GvR, Ascher
- SF  208  Reworking the Coercion Model                            Schemenauer, Lemburg
- SW  209  Multi-dimensional Arrays                                Barrett, Oliphant
- SR  210  Decoupling the Interpreter Loop                         Ascher
- SD  211  Adding A New Outer Product Operator                     Wilson
- SD  212  Loop Counter Iteration                                  Schneider-Kamp
- SD  213  Attribute Access Handlers                               Prescod
- SF  214  Extended Print Statement                                Warsaw
- SS  215  String Interpolation                                    Yee
- IR  216  Docstring Format                                        Zadka
- SF  217  Display Hook for Interactive Use                        Zadka
- SF  218  Adding a Built-In Set Object Type                       Wilson, Hettinger
- SD  219  Stackless Python                                        McMillan
- IR  220  Coroutines, Generators, Continuations                   McMillan
- SF  221  Import As                                               Wouters
- SD  222  Web Library Enhancements                                Kuchling
- SF  223  Change the Meaning of \x Escapes                        Peters
- SR  224  Attribute Docstrings                                    Lemburg
- SD  225  Elementwise/Objectwise Operators                        Zhu, Lielens
- IF  226  Python 2.1 Release Schedule                             Hylton
- SF  227  Statically Nested Scopes                                Hylton
- SW  228  Reworking Python's Numeric Model                        Zadka, GvR
- SF  229  Using Distutils to Build Python                         Kuchling
- SF  230  Warning Framework                                       GvR
- SR  231  __findattr__()                                          Warsaw
- SF  232  Function Attributes                                     Warsaw
- SD  233  Python Online Help                                      Prescod
- SF  234  Iterators                                               Yee, GvR
- SF  235  Import on Case-Insensitive Platforms                    Peters
- SF  236  Back to the __future__                                  Peters
- SF  237  Unifying Long Integers and Integers                     Zadka, GvR
- SF  238  Changing the Division Operator                          Zadka, GvR
- SR  239  Adding a Rational Type to Python                        Craig, Zadka
- SR  240  Adding a Rational Literal to Python                     Craig, Zadka
- SF  241  Metadata for Python Software Packages                   Kuchling
- SR  242  Numeric Kinds                                           Dubois
- SW  243  Module Repository Upload Mechanism                      Reifschneider
- SR  244  The `directive' statement                               von Löwis
- SR  245  Python Interface Syntax                                 Pelletier
- SR  246  Object Adaptation                                       Martelli, Evans
- IF  247  API for Cryptographic Hash Functions                    Kuchling
- IF  248  Python Database API Specification v1.0                  Lemburg
- IF  249  Python Database API Specification v2.0                  Lemburg
- SF  250  Using site-packages on Windows                          Moore
- IF  251  Python 2.2 Release Schedule                             Warsaw, GvR
- SF  252  Making Types Look More Like Classes                     GvR
- SF  253  Subtyping Built-in Types                                GvR
- SR  254  Making Classes Look More Like Types                     GvR
- SF  255  Simple Generators                                       Schemenauer, Peters, Hetland
- SR  256  Docstring Processing System Framework                   Goodger
- I   257  Docstring Conventions                                   Goodger, GvR
- SR  258  Docutils Design Specification                           Goodger
- SR  259  Omit printing newline after newline                     GvR
- SF  260  Simplify xrange()                                       GvR
- SF  261  Support for "wide" Unicode characters                   Prescod
- SD  262  A Database of Installed Python Packages                 Kuchling
- SF  263  Defining Python Source Code Encodings                   Lemburg, von Löwis
- SF  264  Future statements in simulated shells                   Hudson
- SR  265  Sorting Dictionaries by Value                           Griffin
- SW  266  Optimizing Global Variable/Attribute Access             Montanaro
- SD  267  Optimized Access to Module Namespaces                   Hylton
- SR  268  Extended HTTP functionality and WebDAV                  Stein
- SD  269  Pgen Module for Python                                  Riehl
- SR  270  uniq method for list objects                            Petrone
- SR  271  Prefixing sys.path by command line option               Giacometti
- IF  272  API for Block Encryption Algorithms v1.0                Kuchling
- SF  273  Import Modules from Zip Archives                        Ahlstrom
- SF  274  Dict Comprehensions                                     Warsaw
- SR  275  Switching on Multiple Values                            Lemburg
- SR  276  Simple Iterator for ints                                Althoff
- SF  277  Unicode file name support for Windows NT                Hodgson
- SF  278  Universal Newline Support                               Jansen
- SF  279  The enumerate() built-in function                       Hettinger
- SD  280  Optimizing access to globals                            GvR
- SR  281  Loop Counter Iteration with range and xrange            Hetland
- SF  282  A Logging System                                        Sajip, Mick
- IF  283  Python 2.3 Release Schedule                             GvR
- SR  284  Integer for-loops                                       Eppstein, Ewing
- SF  285  Adding a bool type                                      GvR
- SD  286  Enhanced Argument Tuples                                von Löwis
- I   287  reStructuredText Docstring Format                       Goodger
- SW  288  Generators Attributes and Exceptions                    Hettinger
- SF  289  Generator Expressions                                   Hettinger
- I   290  Code Migration and Modernization                        Hettinger
- IF  291  Backward Compatibility for the Python 2 Standard ...    Norwitz
- SF  292  Simpler String Substitutions                            Warsaw
- SF  293  Codec Error Handling Callbacks                          Dörwald
- SR  294  Type Names in the types Module                          Tirosh
- SR  295  Interpretation of multiline string constants            Koltsov
- SW  296  Adding a bytes Object Type                              Gilbert
- SR  297  Support for System Upgrades                             Lemburg
- SW  298  The Locked Buffer Interface                             Heller
- SR  299  Special __main__() function in modules                  Epler
-
- SF  301  Package Index and Metadata for Distutils                Jones
- SF  302  New Import Hooks                                        JvR, Moore
- SR  303  Extend divmod() for Multiple Divisors                   Bellman
- SW  304  Controlling Generation of Bytecode Files                Montanaro
- SF  305  CSV File API                                            Altis, Cole, McNamara, Montanaro, Wells
- IW  306  How to Change Python's Grammar                          Hudson, Diederich, Coghlan, Peterson
- SF  307  Extensions to the pickle protocol                       GvR, Peters
- SF  308  Conditional Expressions                                 GvR, Hettinger
- SF  309  Partial Function Application                            Harris
- SR  310  Reliable Acquisition/Release Pairs                      Hudson, Moore
- SF  311  Simplified Global Interpreter Lock Acquisition for ...  Hammond
- SD  312  Simple Implicit Lambda                                  Suzi, Martelli
- SR  313  Adding Roman Numeral Literals to Python                 Meyer
- SF  314  Metadata for Python Software Packages v1.1              Kuchling, Jones
- SR  315  Enhanced While Loop                                     Hettinger, Carroll
- SD  316  Programming by Contract for Python                      Way
- SR  317  Eliminate Implicit Exception Instantiation              Taschuk
- SF  318  Decorators for Functions and Methods                    Smith
- SR  319  Python Synchronize/Asynchronize Block                   Pelletier
- IF  320  Python 2.4 Release Schedule                             Warsaw, Hettinger, Baxter
- SW  321  Date/Time Parsing and Formatting                        Kuchling
- SF  322  Reverse Iteration                                       Hettinger
- SD  323  Copyable Iterators                                      Martelli
- SF  324  subprocess - New process module                         Astrand
- SR  325  Resource-Release Support for Generators                 Pedroni
- SR  326  A Case for Top and Bottom Values                        Carlson, Reedy
- SF  327  Decimal Data Type                                       Batista
- SF  328  Imports: Multi-Line and Absolute/Relative               Aahz
- SR  329  Treating Builtins as Constants in the Standard Library  Hettinger
- SR  330  Python Bytecode Verification                            Pelletier
- SF  331  Locale-Independent Float/String Conversions             Reis
- SR  332  Byte vectors and String/Unicode Unification             Montanaro
- IF  333  Python Web Server Gateway Interface v1.0                Eby
- SW  334  Simple Coroutines via SuspendIteration                  Evans
- SR  335  Overloadable Boolean Operators                          Ewing
- SR  336  Make None Callable                                      McClelland
- SD  337  Logging Usage in the Standard Library                   Dubner
- SF  338  Executing modules as scripts                            Coghlan
- IW  339  Design of the CPython Compiler                          Cannon
- SR  340  Anonymous Block Statements                              GvR
- SF  341  Unifying try-except and try-finally                     Brandl
- SF  342  Coroutines via Enhanced Generators                      GvR, Eby
- SF  343  The "with" Statement                                    GvR, Coghlan
- SS  344  Exception Chaining and Embedded Tracebacks              Yee
- SA  345  Metadata for Python Software Packages 1.2               Jones
- SW  346  User Defined ("``with``") Statements                    Coghlan
- PF  347  Migrating the Python CVS to Subversion                  von Löwis
- SR  348  Exception Reorganization for Python 3.0                 Cannon
- SD  349  Allow str() to return unicode strings                   Schemenauer
- IR  350  Codetags                                                Elliott
- SR  351  The freeze protocol                                     Warsaw
- SF  352  Required Superclass for Exceptions                      Cannon, GvR
- SF  353  Using ssize_t as the index type                         von Löwis
- SS  354  Enumerations in Python                                  Finney
- SR  355  Path - Object oriented filesystem paths                 Lindqvist
- IF  356  Python 2.5 Release Schedule                             Norwitz, GvR, Baxter
- SF  357  Allowing Any Object to be Used for Slicing              Oliphant
- SF  358  The "bytes" Object                                      Schemenauer, GvR
- SW  359  The "make" Statement                                    Bethard
- PF  360  Externally Maintained Packages                          Cannon
- IF  361  Python 2.6 and 3.0 Release Schedule                     Norwitz, Warsaw
- SF  362  Function Signature Object                               Cannon, Seo, Selivanov, Hastings
- SR  363  Syntax For Dynamic Attribute Access                     North
- SW  364  Transitioning to the Py3K Standard Library              Warsaw
- SR  365  Adding the pkg_resources module                         Eby
- SF  366  Main module explicit relative imports                   Coghlan
- SS  367  New Super                                               Spealman, Delaney
- SD  368  Standard image protocol and class                       Mastrodomenico
- SW  369  Post import hooks                                       Heimes
- SF  370  Per user site-packages directory                        Heimes
- SF  371  Addition of the multiprocessing package to the ...      Noller, Oudkerk
- SF  372  Adding an ordered dictionary to collections             Ronacher, Hettinger
- I   373  Python 2.7 Release Schedule                             Peterson
- PF  374  Choosing a distributed VCS for the Python project       Cannon, Turnbull, Vassalotti, Warsaw, Ochtman
- IF  375  Python 3.1 Release Schedule                             Peterson
- SA  376  Database of Installed Python Distributions              Ziadé
- SR  377  Allow __enter__() methods to skip the statement body    Coghlan
- SF  378  Format Specifier for Thousands Separator                Hettinger
- SW  379  Adding an Assignment Expression                         Whitley
- SF  380  Syntax for Delegating to a Subgenerator                 Ewing
- S   381  Mirroring infrastructure for PyPI                       Ziadé, v. Löwis
- SR  382  Namespace Packages                                      v. Löwis
- SF  383  Non-decodable Bytes in System Character Interfaces      v. Löwis
- SF  384  Defining a Stable ABI                                   v. Löwis
- PF  385  Migrating from Subversion to Mercurial                  Ochtman, Pitrou, Brandl
- SS  386  Changing the version comparison module in Distutils     Ziadé
- P   387  Backwards Compatibility Policy                          Peterson
-
- SF  389  argparse - New Command Line Parsing Module              Bethard
- SR  390  Static metadata for Distutils                           Ziadé
- SF  391  Dictionary-Based Configuration For Logging              Sajip
- I   392  Python 3.2 Release Schedule                             Brandl
- SF  393  Flexible String Representation                          v. Löwis
- I   394  The "python" Command on Unix-Like Systems               Staley, Coghlan
- SW  395  Qualified Names for Modules                             Coghlan
- ID  396  Module Version Numbers                                  Warsaw
- SF  397  Python launcher for Windows                             Hammond, v. Löwis
- I   398  Python 3.3 Release Schedule                             Brandl
- IF  399  Pure Python/C Accelerator Module Compatibility ...      Cannon
- SD  400  Deprecate codecs.StreamReader and codecs.StreamWriter   Stinner
- PR  401  BDFL Retirement                                         Warsaw, Cannon
- SR  402  Simplified Package Layout and Partitioning              Eby
- SD  403  General purpose decorator clause (aka "@in" clause)     Coghlan
- IF  404  Python 2.8 Un-release Schedule                          Warsaw
- SF  405  Python Virtual Environments                             Meyer
- SW  406  Improved Encapsulation of Import State                  Coghlan, Slodkowicz
- PD  407  New release cycle and introducing long-term support ... Pitrou, Brandl, Warsaw
- SR  408  Standard library __preview__ package                    Coghlan, Bendersky
- SF  409  Suppressing exception context                           Furman
- SR  410  Use decimal.Decimal type for timestamps                 Stinner
- IA  411  Provisional packages in the Python standard library     Coghlan, Bendersky
- SF  412  Key-Sharing Dictionary                                  Shannon
- PW  413  Faster evolution of the Python Standard Library         Coghlan
- SF  414  Explicit Unicode Literal for Python 3.3                 Ronacher, Coghlan
- SF  415  Implement context suppression with exception attributes Peterson
- SR  416  Add a frozendict builtin type                           Stinner
- SF  417  Including mock in the Standard Library                  Foord
- SF  418  Add monotonic time, performance counter, and ...        Simpson, Jewett, Turnbull, Stinner
- SD  419  Protecting cleanup statements from interruptions        Colomiets
- SF  420  Implicit Namespace Packages                             Smith
- SF  421  Adding sys.implementation                               Snow
- SD  422  Simpler customisation of class creation                 Coghlan, Urban
- ID  423  Naming conventions and recipes related to packaging     Bryon
- SF  424  A method for exposing a length hint                     Gaynor
- SA  425  Compatibility Tags for Built Distributions              Holth
- S   426  Metadata for Python Software Packages 2.0               Coghlan, Holth, Stufft
- SA  427  The Wheel Binary Package Format 1.0                     Holth
- SF  428  The pathlib module -- object-oriented filesystem paths  Pitrou
- I   429  Python 3.4 Release Schedule                             Hastings
- IF  430  Migrating to Python 3 as the default online ...         Coghlan
- S   431  Time zone support improvements                          Regebro
- S   432  Simplifying the CPython startup sequence                Coghlan
- SS  433  Easier suppression of file descriptor inheritance       Stinner
- I   434  IDLE Enhancement Exception for All Branches             Rovito, Reedy
- SF  435  Adding an Enum type to the Python standard library      Warsaw, Bendersky, Furman
- S   436  The Argument Clinic DSL                                 Hastings
- SR  437  A DSL for specifying signatures, annotations and ...    Krah
- PA  438  Transitioning to release-file hosting on PyPI           Krekel, Meyer
- SR  439  Inclusion of implicit pip bootstrap in Python ...       Jones
- IA  440  Version Identification and Dependency Specification     Coghlan, Stufft
- S   441  Improving Python ZIP Application Support                Holth
- SF  442  Safe object finalization                                Pitrou
- SF  443  Single-dispatch generic functions                       Langa
- ID  444  Python Web3 Interface                                   McDonough, Ronacher
- SF  445  Add new APIs to customize Python memory allocators      Stinner
- SF  446  Make newly created file descriptors non-inheritable     Stinner
- S   447  Add __getdescriptor__ method to metaclass               Oussoren
- S   448  Additional Unpacking Generalizations                    Landau
- PA  449  Removal of the PyPI Mirror Auto Discovery and ...       Stufft
- SF  450  Adding A Statistics Module To The Standard Library      D'Aprano
- SF  451  A ModuleSpec Type for the Import System                 Snow
- I   452  API for Cryptographic Hash Functions v2.0               Kuchling, Heimes
- SF  453  Explicit bootstrapping of pip in Python installations   Stufft, Coghlan
- SF  454  Add a new tracemalloc module to trace Python memory ... Stinner
- S   455  Adding a key-transforming dictionary to collections     Pitrou
- SF  456  Secure and interchangeable hash algorithm               Heimes
- I   457  Syntax For Positional-Only Parameters                   Hastings
- S   458  Surviving a Compromise of PyPI                          Kuppusamy, Stufft, Cappos
- S   459  Standard Metadata Extensions for Python Software ...    Coghlan
- SW  460  Add binary interpolation and formatting                 Pitrou
- SA  461  Adding % formatting to bytes and bytearray              Furman
- PD  462  Core development workflow automation for CPython        Coghlan
- S   463  Exception-catching expressions                          Angelico
- PA  464  Removal of the PyPI Mirror Authenticity API             Stufft
- SF  465  A dedicated infix operator for matrix multiplication    Smith
- SF  466  Network Security Enhancements for Python 2.7.x          Coghlan
- S   467  Minor API improvements for binary sequences             Coghlan
- S   468  Preserving the order of \*\*kwargs in a function.       Snow
- SW  469  Migration of dict iteration code to Python 3            Coghlan
- P   470  Using Multi Index Support for External to PyPI ...      Stufft
- SA  471  os.scandir() function -- a better and faster ...        Hoyt
- S   472  Support for indexing with keyword arguments             Borini, Martinot-Lagarde
- S   473  Adding structured data to built-in exceptions           Kreft
- PD  474  Creating forge.python.org                               Coghlan
- S   475  Retry system calls failing with EINTR                   Natali, Stinner
- S   476  Enabling certificate verification by default for ...    Gaynor
- SA  477  Backport ensurepip (PEP 453) to Python 2.7              Stufft, Coghlan
- I   478  Python 3.5 Release Schedule                             Hastings
-
- SD  628  Add ``math.tau``                                        Coghlan
-
- SR  666  Reject Foolish Indentation                              Creighton
-
- SR  754  IEEE 754 Floating Point Special Values                  Warnes
-
- PF 3000  Python 3000                                             GvR
- PW 3001  Procedure for reviewing and improving standard ...      Brandl
- PF 3002  Procedure for Backwards-Incompatible Changes            Bethard
- PF 3003  Python Language Moratorium                              Cannon, Noller, GvR
-
- PF 3099  Things that will Not Change in Python 3000              Brandl
- PF 3100  Miscellaneous Python 3.0 Plans                          Cannon
- SF 3101  Advanced String Formatting                              Talin
- SF 3102  Keyword-Only Arguments                                  Talin
- SR 3103  A Switch/Case Statement                                 GvR
- SF 3104  Access to Names in Outer Scopes                         Yee
- SF 3105  Make print a function                                   Brandl
- SF 3106  Revamping dict.keys(), .values() and .items()           GvR
- SF 3107  Function Annotations                                    Winter, Lownds
- SF 3108  Standard Library Reorganization                         Cannon
- SF 3109  Raising Exceptions in Python 3000                       Winter
- SF 3110  Catching Exceptions in Python 3000                      Winter
- SF 3111  Simple input built-in in Python 3000                    Roberge
- SF 3112  Bytes literals in Python 3000                           Orendorff
- SF 3113  Removal of Tuple Parameter Unpacking                    Cannon
- SF 3114  Renaming iterator.next() to iterator.__next__()         Yee
- SF 3115  Metaclasses in Python 3000                              Talin
- SF 3116  New I/O                                                 Stutzbach, GvR, Verdone
- SR 3117  Postfix type declarations                               Brandl
- SF 3118  Revising the buffer protocol                            Oliphant, Banks
- SF 3119  Introducing Abstract Base Classes                       GvR, Talin
- SF 3120  Using UTF-8 as the default source encoding              von Löwis
- SA 3121  Extension Module Initialization and Finalization        von Löwis
- SR 3122  Delineation of the main module                          Cannon
- SF 3123  Making PyObject_HEAD conform to standard C              von Löwis
- SD 3124  Overloading, Generic Functions, Interfaces, and ...     Eby
- SR 3125  Remove Backslash Continuation                           Jewett
- SR 3126  Remove Implicit String Concatenation                    Jewett, Hettinger
- SF 3127  Integer Literal Support and Syntax                      Maupin
- SR 3128  BList: A Faster List-like Type                          Stutzbach
- SF 3129  Class Decorators                                        Winter
- SR 3130  Access to Current Module/Class/Function                 Jewett
- SF 3131  Supporting Non-ASCII Identifiers                        von Löwis
- SF 3132  Extended Iterable Unpacking                             Brandl
- SR 3133  Introducing Roles                                       Winter
- SF 3134  Exception Chaining and Embedded Tracebacks              Yee
- SF 3135  New Super                                               Spealman, Delaney, Ryan
- SR 3136  Labeled break and continue                              Chisholm
- SF 3137  Immutable Bytes and Mutable Buffer                      GvR
- SF 3138  String representation in Python 3000                    Ishimoto
- SR 3139  Cleaning out sys and the "interpreter" module           Peterson
- SR 3140  str(container) should call str(item), not repr(item)    Broytmann, Jewett
- SF 3141  A Type Hierarchy for Numbers                            Yasskin
- SR 3142  Add a "while" clause to generator expressions           Britton
- SD 3143  Standard daemon process library                         Finney
- SF 3144  IP Address Manipulation Library for the Python ...      Moody
- SW 3145  Asynchronous I/O For subprocess.Popen                   Pruitt, McCreary, Carlson
- SW 3146  Merging Unladen Swallow into CPython                    Winter, Yasskin, Kleckner
- SF 3147  PYC Repository Directories                              Warsaw
- SF 3148  futures - execute computations asynchronously           Quinlan
- SF 3149  ABI version tagged .so files                            Warsaw
- SD 3150  Statement local namespaces (aka "given" clause)         Coghlan
- SF 3151  Reworking the OS and IO exception hierarchy             Pitrou
- SD 3152  Cofunctions                                             Ewing
- SS 3153  Asynchronous IO support                                 Houtven
- SF 3154  Pickle protocol version 4                               Pitrou
- SF 3155  Qualified name for classes and functions                Pitrou
- SF 3156  Asynchronous IO Support Rebooted: the "asyncio" Module  GvR
-
- IF 3333  Python Web Server Gateway Interface v1.0.1              Eby
-
-
-
-

Reserved PEP Numbers

-
-     num  title                                                   owner
-     ---  -----                                                   -----
-     801  RESERVED                                                Warsaw
-
-
-
-

Key

-
-    S - Standards Track PEP
-    I - Informational PEP
-    P - Process PEP
-
-    A - Accepted proposal
-    R - Rejected proposal
-    W - Withdrawn proposal
-    D - Deferred proposal
-    F - Final proposal
-    A - Active proposal
-    D - Draft proposal
-    S - Superseded proposal
-
-
-
-

Owners

-
-    name                         email address
-    ----                         -------------
-    Aahz                         aahz at pythoncraft.com
-    Ahlstrom, James C.           jim at interet.com
-    Althoff, Jim                 james_althoff at i2.com
-    Altis, Kevin                 altis at semi-retired.com
-    Angelico, Chris              rosuav at gmail.com
-    Ascher, David                davida at activestate.com
-    Astrand, Peter               astrand at lysator.liu.se
-    Banks, Carl                  pythondev at aerojockey.com
-    Barrett, Paul                barrett at stsci.edu
-    Batista, Facundo             facundo at taniquetil.com.ar
-    Baxter, Anthony              anthony at interlink.com.au
-    Bellman, Thomas              bellman+pep-divmod@lysator.liu.se
-    Bendersky, Eli               eliben at gmail.com
-    Bethard, Steven              steven.bethard at gmail.com
-    Borini, Stefano              
-    Brandl, Georg                georg at python.org
-    Britton, Gerald              gerald.britton at gmail.com
-    Broytmann, Oleg              phd at phd.pp.ru
-    Bryon, Benoit                benoit at marmelune.net
-    Cannon, Brett                brett at python.org
-    Cappos, Justin               jcappos at poly.edu
-    Carlson, Josiah              jcarlson at uci.edu
-    Carroll,         W Isaac     icarroll at pobox.com
-    Chisholm, Matt               matt-python at theory.org
-    Coghlan, Nick                ncoghlan at gmail.com
-    Cole, Dave                   djc at object-craft.com.au
-    Colomiets, Paul              paul at colomiets.name
-    Craig, Christopher A.        python-pep at ccraig.org
-    Creighton, Laura             lac at strakt.com
-    D'Aprano, Steven             steve at pearwood.info
-    Delaney, Tim                 timothy.c.delaney at gmail.com
-    Diederich, Jack              jackdied at gmail.com
-    Dörwald, Walter              walter at livinglogic.de
-    Drake, Fred L., Jr.          fdrake at acm.org
-    Dubner, Michael P.           dubnerm at mindless.com
-    Dubois, Paul F.              paul at pfdubois.com
-    Eby, P.J.                    pje at telecommunity.com
-    Eby, Phillip J.              pje at telecommunity.com
-    Elliott, Micah               mde at tracos.org
-    Epler, Jeff                  jepler at unpythonic.net
-    Eppstein, David              eppstein at ics.uci.edu
-    Evans, Clark C.              cce at clarkevans.com
-    Ewing, Gregory               greg.ewing at canterbury.ac.nz
-    Ewing, Greg                  greg.ewing at canterbury.ac.nz
-    Faassen, Martijn             faassen at infrae.com
-    Finney, Ben                  ben+python@benfinney.id.au
-    Foord, Michael               michael at python.org
-    Furman, Ethan                ethan at stoneleaf.us
-    Gaynor, Alex                 alex.gaynor at gmail.com
-    Giacometti, Frédéric B.      fred at arakne.com
-    Gilbert, Scott               xscottg at yahoo.com
-    Goodger, David               goodger at python.org
-    Griffin, Grant               g2 at iowegian.com
-    Hammond, Mark                mhammond at skippinet.com.au
-    Harris, Peter                scav at blueyonder.co.uk
-    Hastings, Larry              larry at hastings.org
-    Heimes, Christian            christian at python.org
-    Heller, Thomas               theller at python.net
-    Hetland, Magnus Lie          magnus at hetland.org
-    Hettinger, Raymond           python at rcn.com
-    Hodgson, Neil                neilh at scintilla.org
-    Holth, Daniel                dholth at gmail.com
-    Houtven, Laurens Van         _ at lvh.cc
-    Hoyt, Ben                    benhoyt at gmail.com
-    Hudson, Michael              mwh at python.net
-    Hylton, Jeremy               jeremy at alum.mit.edu
-    Ishimoto, Atsuo              ishimoto--at--gembook.org
-    Jansen, Jack                 jack at cwi.nl
-    Jewett, Jim J.               jimjjewett at gmail.com
-    Jewett, Jim                  jimjjewett at gmail.com
-    Jones, Richard               richard at python.org
-    Kleckner, Reid               rnk at mit.edu
-    Koltsov, Stepan              yozh at mx1.ru
-    Krah, Stefan                 skrah at bytereef.org
-    Kreft, Sebastian             skreft at deezer.com
-    Krekel, Holger               holger at merlinux.eu
-    Kuchling, A.M.               amk at amk.ca
-    Kuppusamy, Trishank Karthik  tk47 at students.poly.edu
-    Landau, Joshua               joshua at landau.ws
-    Langa, Łukasz                lukasz at langa.pl
-    Lemburg, Marc-André          mal at lemburg.com
-    Lielens, Gregory             gregory.lielens at fft.be
-    Lindqvist, Björn             bjourne at gmail.com
-    von Löwis, Martin            martin at v.loewis.de
-    v. Löwis, Martin             martin at v.loewis.de
-    Lownds, Tony                 tony at lownds.com
-    Martelli, Alex               aleaxit at gmail.com
-    Martinot-Lagarde, Joseph     
-    Mastrodomenico, Lino         l.mastrodomenico at gmail.com
-    Maupin, Patrick              pmaupin at gmail.com
-    McClelland, Andrew           eternalsquire at comcast.net
-    McCreary, Charles R.         
-    McDonough, Chris             chrism at plope.com
-    McMillan, Gordon             gmcm at hypernet.com
-    McNamara, Andrew             andrewm at object-craft.com.au
-    Meyer, Mike                  mwm at mired.org
-    Meyer, Carl                  carl at oddbird.net
-    Mick, Trent                  trentm at activestate.com
-    Montanaro, Skip              skip at pobox.com
-    Moody, Peter                 pmoody at google.com
-    Moore, Paul                  gustav at morpheus.demon.co.uk
-    Natali, Charles-François     cf.natali at gmail.com
-    Noller, Jesse                jnoller at gmail.com
-    North, Ben                   ben at redfrontdoor.org
-    Norwitz, Neal                nnorwitz at gmail.com
-    Ochtman, Dirkjan             dirkjan at ochtman.nl
-    Oliphant, Travis             oliphant at ee.byu.edu
-    Orendorff, Jason             jason.orendorff at gmail.com
-    Oudkerk, Richard             r.m.oudkerk at googlemail.com
-    Oussoren, Ronald             ronaldoussoren at mac.com
-    Pedroni, Samuele             pedronis at python.org
-    Pelletier, Michel            michel at users.sourceforge.net
-    Peters, Tim                  tim at zope.com
-    Peterson, Benjamin           benjamin at python.org
-    Petrone, Jason               jp at demonseed.net
-    Pitrou, Antoine              solipsis at pitrou.net
-    Prescod, Paul                paul at prescod.net
-    Pruitt, (James) Eric         
-    Quinlan, Brian               brian at sweetapp.com
-    Reedy, Terry                 tjreedy at udel.edu
-    Regebro, Lennart             regebro at gmail.com
-    Reifschneider, Sean          jafo-pep at tummy.com
-    Reis, Christian R.           kiko at async.com.br
-    Riehl, Jonathan              jriehl at spaceship.com
-    Roberge, Andre               andre.roberge at gmail.com 
-    Ronacher, Armin              armin.ronacher at active-4.com
-    van Rossum, Guido (GvR)      guido at python.org
-    van Rossum, Just (JvR)       just at letterror.com
-    Rovito, Todd                 rovitotv at gmail.com
-    Ryan, Lie                    lie.1296 at gmail.com
-    Sajip, Vinay                 vinay_sajip at red-dove.com
-    Schemenauer, Neil            nas at arctrix.com
-    Schneider-Kamp, Peter        nowonder at nowonder.de
-    Selivanov, Yury              yselivanov at sprymix.com
-    Seo, Jiwon                   seojiwon at gmail.com
-    Shannon, Mark                mark at hotpy.org
-    Simpson, Cameron             cs at zip.com.au
-    Slodkowicz, Greg             jergosh at gmail.com
-    Smith, Nathaniel J.          njs at pobox.com
-    Smith, Kevin D.              kevin.smith at themorgue.org
-    Smith, Eric V.               eric at trueblade.com
-    Snow, Eric                   ericsnowcurrently at gmail.com
-    Spealman, Calvin             ironfroggy at gmail.com
-    Staley, Kerrick              mail at kerrickstaley.com
-    Stein, Greg                  gstein at lyra.org
-    Stinner, Victor              victor.stinner at gmail.com
-    Stufft, Donald               donald at stufft.io
-    Stutzbach, Daniel            daniel at stutzbachenterprises.com
-    Suzi, Roman                  rnd at onego.ru
-    Talin                        talin at acm.org
-    Taschuk, Steven              staschuk at telusplanet.net
-    Tirosh, Oren                 oren at hishome.net
-    Turnbull, Stephen J.         stephen at xemacs.org
-    Urban, Daniel                urban.dani+py@gmail.com
-    Vassalotti, Alexandre        alexandre at peadrop.com
-    Verdone, Mike                mike.verdone at gmail.com
-    Warnes, Gregory R.           gregory_r_warnes at groton.pfizer.com
-    Warsaw, Barry                barry at python.org
-    Way, Terence                 terry at wayforward.net
-    Wells, Cliff                 logiplexsoftware at earthlink.net
-    Whitley, Jervis              jervisau at gmail.com
-    Wilson, Greg                 gvwilson at ddj.com
-    Winter, Collin               collinwinter at google.com
-    Wouters, Thomas              thomas at python.org
-    Yasskin, Jeffrey             jyasskin at google.com
-    Yee, Ka-Ping                 ping at zesty.ca
-    Zadka, Moshe                 moshez at zadka.site.co.il
-    Zhu, Huaiyu                  hzhu at users.sourceforge.net
-    Ziadé, Tarek                 tarek at ziade.org
-
-
-
-

References

-
-    [1] PEP 1: PEP Purpose and Guidelines
-    [2] View PEP history online
-        http://hg.python.org/peps/
-
-
-
- - diff --git a/peps/tests/peps/pep-0012.html b/peps/tests/peps/pep-0012.html deleted file mode 100644 index e341e82f5..000000000 --- a/peps/tests/peps/pep-0012.html +++ /dev/null @@ -1,53 +0,0 @@ - - --- - - - - - - - - - - - - - - - - - -
PEP:12
Title:Sample reStructuredText PEP Template
Author:David Goodger <goodger at python.org>, -Barry Warsaw <barry at python.org>
Status:Active
Type:Process
Content-Type:text/x-rst
Created:05-Aug-2002
Post-History:30-Aug-2002
-
-
-

Contents

- -
-
-

Abstract

-

This PEP provides a boilerplate or sample template for creating your -own reStructuredText PEPs.

-
- - diff --git a/peps/tests/peps/pep-0012.rst b/peps/tests/peps/pep-0012.rst deleted file mode 100644 index 92a90835e..000000000 --- a/peps/tests/peps/pep-0012.rst +++ /dev/null @@ -1,33 +0,0 @@ -PEP: 12 -Title: Sample reStructuredText PEP Template -Author: David Goodger , - Barry Warsaw -Status: Active -Type: Process -Content-Type: text/x-rst -Created: 05-Aug-2002 -Post-History: 30-Aug-2002 - - -Abstract -======== - -This PEP provides a boilerplate or sample template for creating your -own reStructuredText PEPs. - - -Copyright -========= - -This document has been placed in the public domain. - - - -.. - Local Variables: - mode: indented-text - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 70 - coding: utf-8 - End: diff --git a/peps/tests/peps/pep-0525.html b/peps/tests/peps/pep-0525.html deleted file mode 100644 index 55a756e0d..000000000 --- a/peps/tests/peps/pep-0525.html +++ /dev/null @@ -1,595 +0,0 @@ - - --- - - - - - - - - - - - - - - - - - - - - - - - - - -
PEP:525
Title:Asynchronous Generators
Version:$Revision$
Last-Modified:$Date$
Author:Yury Selivanov <yury at magic.io>
Discussions-To:<python-dev at python.org>
Status:Draft
Type:Standards Track
Content-Type:text/x-rst
Created:28-Jul-2016
Python-Version:3.6
Post-History:02-Aug-2016
-
- -
-

Abstract

-

PEP 492 introduced support for native coroutines and async/await -syntax to Python 3.5. It is proposed here to extend Python's -asynchronous capabilities by adding support for -asynchronous generators.

-
-
-

Rationale and Goals

-

Regular generators (introduced in PEP 255) enabled an elegant way of -writing complex data producers and have them behave like an iterator.

-

However, currently there is no equivalent concept for the asynchronous -iteration protocol (async for). This makes writing asynchronous -data producers unnecessarily complex, as one must define a class that -implements __aiter__ and __anext__ to be able to use it in -an async for statement.

-

Essentially, the goals and rationale for PEP 255, applied to the -asynchronous execution case, hold true for this proposal as well.

-

Performance is an additional point for this proposal: in our testing of -the reference implementation, asynchronous generators are 2x faster -than an equivalent implemented as an asynchronous iterator.

-

As an illustration of the code quality improvement, consider the -following class that prints numbers with a given delay once iterated:

-
-class Ticker:
-    """Yield numbers from 0 to `to` every `delay` seconds."""
-
-    def __init__(self, delay, to):
-        self.delay = delay
-        self.i = 0
-        self.to = to
-
-    def __aiter__(self):
-        return self
-
-    async def __anext__(self):
-        i = self.i
-        if i >= self.to:
-            raise StopAsyncIteration
-        self.i += 1
-        if i:
-            await asyncio.sleep(self.delay)
-        return i
-
-

The same can be implemented as a much simpler asynchronous generator:

-
-async def ticker(delay, to):
-    """Yield numbers from 0 to `to` every `delay` seconds."""
-    for i in range(to):
-        yield i
-        await asyncio.sleep(delay)
-
-
-
-

Specification

-

This proposal introduces the concept of asynchronous generators to -Python.

-

This specification presumes knowledge of the implementation of -generators and coroutines in Python (PEP 342, PEP 380 and PEP 492).

-
-

Asynchronous Generators

-

A Python generator is any function containing one or more yield -expressions:

-
-def func():            # a function
-    return
-
-def genfunc():         # a generator function
-    yield
-
-

We propose to use the same approach to define -asynchronous generators:

-
-async def coro():      # a coroutine function
-    await smth()
-
-async def asyncgen():  # an asynchronous generator function
-    await smth()
-    yield 42
-
-

The result of calling an asynchronous generator function is -an asynchronous generator object, which implements the asynchronous -iteration protocol defined in PEP 492.

-

It is a SyntaxError to have a non-empty return statement in an -asynchronous generator.

-
-
-

Support for Asynchronous Iteration Protocol

-

The protocol requires two special methods to be implemented:

-
    -
  1. An __aiter__ method returning an asynchronous iterator.
  2. -
  3. An __anext__ method returning an awaitable object, which uses -StopIteration exception to "yield" values, and -StopAsyncIteration exception to signal the end of the iteration.
  4. -
-

Asynchronous generators define both of these methods. Let's manually -iterate over a simple asynchronous generator:

-
-async def genfunc():
-    yield 1
-    yield 2
-
-gen = genfunc()
-
-assert gen.__aiter__() is gen
-
-assert await gen.__anext__() == 1
-assert await gen.__anext__() == 2
-
-await gen.__anext__()  # This line will raise StopAsyncIteration.
-
-
-
-

Finalization

-

PEP 492 requires an event loop or a scheduler to run coroutines. -Because asynchronous generators are meant to be used from coroutines, -they also require an event loop to run and finalize them.

-

Asynchronous generators can have try..finally blocks, as well as -async with. It is important to provide a guarantee that, even -when partially iterated, and then garbage collected, generators can -be safely finalized. For example:

-
-async def square_series(con, to):
-    async with con.transaction():
-        cursor = con.cursor(
-            'SELECT generate_series(0, $1) AS i', to)
-        async for row in cursor:
-            yield row['i'] ** 2
-
-async for i in square_series(con, 1000):
-    if i == 100:
-        break
-
-

The above code defines an asynchronous generator that uses -async with to iterate over a database cursor in a transaction. -The generator is then iterated over with async for, which interrupts -the iteration at some point.

-

The square_series() generator will then be garbage collected, -and without a mechanism to asynchronously close the generator, Python -interpreter would not be able to do anything.

-

To solve this problem we propose to do the following:

-
    -
  1. Implement an aclose method on asynchronous generators -returning a special awaitable. When awaited it -throws a GeneratorExit into the suspended generator and -iterates over it until either a GeneratorExit or -a StopAsyncIteration occur.

    -

    This is very similar to what the close() method does to regular -Python generators, except that an event loop is required to execute -aclose().

    -
  2. -
  3. Raise a RuntimeError, when an asynchronous generator executes -a yield expression in its finally block (using await -is fine, though):

    -
    -async def gen():
    -    try:
    -        yield
    -    finally:
    -        await asyncio.sleep(1)   # Can use 'await'.
    -
    -        yield                    # Cannot use 'yield',
    -                                 # this line will trigger a
    -                                 # RuntimeError.
    -
    -
  4. -
  5. Add two new methods to the sys module: -set_asyncgen_finalizer() and get_asyncgen_finalizer().

    -
  6. -
-

The idea behind sys.set_asyncgen_finalizer() is to allow event -loops to handle generators finalization, so that the end user -does not need to care about the finalization problem, and it just -works.

-

When an asynchronous generator is iterated for the first time, -it stores a reference to the current finalizer. If there is none, -a RuntimeError is raised. This provides a strong guarantee that -every asynchronous generator object will always have a finalizer -installed by the correct event loop.

-

When an asynchronous generator is about to be garbage collected, -it calls its cached finalizer. The assumption is that the finalizer -will schedule an aclose() call with the loop that was active -when the iteration started.

-

For instance, here is how asyncio is modified to allow safe -finalization of asynchronous generators:

-
-# asyncio/base_events.py
-
-class BaseEventLoop:
-
-    def run_forever(self):
-        ...
-        old_finalizer = sys.get_asyncgen_finalizer()
-        sys.set_asyncgen_finalizer(self._finalize_asyncgen)
-        try:
-            ...
-        finally:
-            sys.set_asyncgen_finalizer(old_finalizer)
-            ...
-
-    def _finalize_asyncgen(self, gen):
-        self.create_task(gen.aclose())
-
-

sys.set_asyncgen_finalizer() is thread-specific, so several event -loops running in parallel threads can use it safely.

-
-
-

Asynchronous Generator Object

-

The object is modeled after the standard Python generator object. -Essentially, the behaviour of asynchronous generators is designed -to replicate the behaviour of synchronous generators, with the only -difference in that the API is asynchronous.

-

The following methods and properties are defined:

-
    -
  1. agen.__aiter__(): Returns agen.

    -
  2. -
  3. agen.__anext__(): Returns an awaitable, that performs one -asynchronous generator iteration when awaited.

    -
  4. -
  5. agen.asend(val): Returns an awaitable, that pushes the -val object in the agen generator. When the agen has -not yet been iterated, val must be None.

    -

    Example:

    -
    -async def gen():
    -    await asyncio.sleep(0.1)
    -    v = yield 42
    -    print(v)
    -    await asyncio.sleep(0.2)
    -
    -g = gen()
    -
    -await g.asend(None)      # Will return 42 after sleeping
    -                         # for 0.1 seconds.
    -
    -await g.asend('hello')   # Will print 'hello' and
    -                         # raise StopAsyncIteration
    -                         # (after sleeping for 0.2 seconds.)
    -
    -
  6. -
  7. agen.athrow(typ, [val, [tb]]): Returns an awaitable, that -throws an exception into the agen generator.

    -

    Example:

    -
    -async def gen():
    -    try:
    -        await asyncio.sleep(0.1)
    -        yield 'hello'
    -    except ZeroDivisionError:
    -        await asyncio.sleep(0.2)
    -        yield 'world'
    -
    -g = gen()
    -v = await g.asend(None)
    -print(v)                # Will print 'hello' after
    -                        # sleeping for 0.1 seconds.
    -
    -v = await g.athrow(ZeroDivisionError)
    -print(v)                # Will print 'world' after
    -                        $ sleeping 0.2 seconds.
    -
    -
  8. -
  9. agen.aclose(): Returns an awaitable, that throws a -GeneratorExit exception into the generator. The awaitable can -either return a yielded value, if agen handled the exception, -or agen will be closed and the exception will propagate back -to the caller.

    -
  10. -
  11. agen.__name__ and agen.__qualname__: readable and writable -name and qualified name attributes.

    -
  12. -
  13. agen.ag_await: The object that agen is currently awaiting -on, or None. This is similar to the currently available -gi_yieldfrom for generators and cr_await for coroutines.

    -
  14. -
  15. agen.ag_frame, agen.ag_running, and agen.ag_code: -defined in the same way as similar attributes of standard generators.

    -
  16. -
-

StopIteration and StopAsyncIteration are not propagated out of -asynchronous generators, and are replaced with a RuntimeError.

-
-
-

Implementation Details

-

Asynchronous generator object (PyAsyncGenObject) shares the -struct layout with PyGenObject. In addition to that, the -reference implementation introduces three new objects:

-
    -
  1. PyAsyncGenASend: the awaitable object that implements -__anext__ and asend() methods.
  2. -
  3. PyAsyncGenAThrow: the awaitable object that implements -athrow() and aclose() methods.
  4. -
  5. _PyAsyncGenWrappedValue: every directly yielded object from an -asynchronous generator is implicitly boxed into this structure. This -is how the generator implementation can separate objects that are -yielded using regular iteration protocol from objects that are -yielded using asynchronous iteration protocol.
  6. -
-

PyAsyncGenASend and PyAsyncGenAThrow are awaitables (they have -__await__ methods returning self) and are coroutine-like objects -(implementing __iter__, __next__, send() and throw() -methods). Essentially, they control how asynchronous generators are -iterated:

-pep-0525-1.png -
-

PyAsyncGenASend and PyAsyncGenAThrow

-

PyAsyncGenASend is a coroutine-like object that drives __anext__ -and asend() methods and implements the asynchronous iteration -protocol.

-

agen.asend(val) and agen.__anext__() return instances of -PyAsyncGenASend (which hold references back to the parent -agen object.)

-

The data flow is defined as follows:

-
    -
  1. When PyAsyncGenASend.send(val) is called for the first time, -val is pushed to the parent agen object (using existing -facilities of PyGenObject.)

    -

    Subsequent iterations over the PyAsyncGenASend objects, push -None to agen.

    -

    When a _PyAsyncGenWrappedValue object is yielded, it -is unboxed, and a StopIteration exception is raised with the -unwrapped value as an argument.

    -
  2. -
  3. When PyAsyncGenASend.throw(*exc) is called for the first time, -*exc is throwed into the parent agen object.

    -

    Subsequent iterations over the PyAsyncGenASend objects, push -None to agen.

    -

    When a _PyAsyncGenWrappedValue object is yielded, it -is unboxed, and a StopIteration exception is raised with the -unwrapped value as an argument.

    -
  4. -
  5. return statements in asynchronous generators raise -StopAsyncIteration exception, which is propagated through -PyAsyncGenASend.send() and PyAsyncGenASend.throw() methods.

    -
  6. -
-

PyAsyncGenAThrow is very similar to PyAsyncGenASend. The only -difference is that PyAsyncGenAThrow.send(), when called first time, -throws an exception into the parent agen object (instead of pushing -a value into it.)

-
-
-
-

New Standard Library Functions and Types

-
    -
  1. types.AsyncGeneratorType -- type of asynchronous generator -object.
  2. -
  3. sys.set_asyncgen_finalizer() and sys.get_asyncgen_finalizer() -methods to set up asynchronous generators finalizers in event loops.
  4. -
  5. inspect.isasyncgen() and inspect.isasyncgenfunction() -introspection functions.
  6. -
-
-
-

Backwards Compatibility

-

The proposal is fully backwards compatible.

-

In Python 3.5 it is a SyntaxError to define an async def -function with a yield expression inside, therefore it's safe to -introduce asynchronous generators in 3.6.

-
-
-
-

Performance

-
-

Regular Generators

-

There is no performance degradation for regular generators. -The following micro benchmark runs at the same speed on CPython with -and without asynchronous generators:

-
-def gen():
-    i = 0
-    while i < 100000000:
-        yield i
-        i += 1
-
-list(gen())
-
-
-
-

Improvements over asynchronous iterators

-

The following micro-benchmark shows that asynchronous generators -are about 2.3x faster than asynchronous iterators implemented in -pure Python:

-
-N = 10 ** 7
-
-async def agen():
-    for i in range(N):
-        yield i
-
-class AIter:
-    def __init__(self):
-        self.i = 0
-
-    def __aiter__(self):
-        return self
-
-    async def __anext__(self):
-        i = self.i
-        if i >= N:
-            raise StopAsyncIteration
-        self.i += 1
-        return i
-
-
-
-
-

Design Considerations

-
-

aiter() and anext() builtins

-

Originally, PEP 492 defined __aiter__ as a method that should -return an awaitable object, resulting in an asynchronous iterator.

-

However, in CPython 3.5.2, __aiter__ was redefined to return -asynchronous iterators directly. To avoid breaking backwards -compatibility, it was decided that Python 3.6 will support both -ways: __aiter__ can still return an awaitable with -a DeprecationWarning being issued.

-

Because of this dual nature of __aiter__ in Python 3.6, we cannot -add a synchronous implementation of aiter() built-in. Therefore, -it is proposed to wait until Python 3.7.

-
-
-

Asynchronous list/dict/set comprehensions

-

Syntax for asynchronous comprehensions is unrelated to the asynchronous -generators machinery, and should be considered in a separate PEP.

-
-
-

Asynchronous yield from

-

While it is theoretically possible to implement yield from support -for asynchronous generators, it would require a serious redesign of the -generators implementation.

-

yield from is also less critical for asynchronous generators, since -there is no need provide a mechanism of implementing another coroutines -protocol on top of coroutines. And to compose asynchronous generators a -simple async for loop can be used:

-
-async def g1():
-    yield 1
-    yield 2
-
-async def g2():
-    async for v in g1():
-        yield v
-
-
-
-

Why the asend() and athrow() methods are necessary

-

They make it possible to implement concepts similar to -contextlib.contextmanager using asynchronous generators. -For instance, with the proposed design, it is possible to implement -the following pattern:

-
-@async_context_manager
-async def ctx():
-    await open()
-    try:
-        yield
-    finally:
-        await close()
-
-async with ctx():
-    await ...
-
-

Another reason is that it is possible to push data and throw exceptions -into asynchronous generators using the object returned from -__anext__ object, but it is hard to do that correctly. Adding -explicit asend() and athrow() will pave a safe way to -accomplish that.

-

In terms of implementation, asend() is a slightly more generic -version of __anext__, and athrow() is very similar to -aclose(). Therefore having these methods defined for asynchronous -generators does not add any extra complexity.

-
-
-
-

Example

-

A working example with the current reference implementation (will -print numbers from 0 to 9 with one second delay):

-
-async def ticker(delay, to):
-    for i in range(to):
-        yield i
-        await asyncio.sleep(delay)
-
-
-async def run():
-    async for i in ticker(1, 10):
-        print(i)
-
-
-import asyncio
-loop = asyncio.get_event_loop()
-try:
-    loop.run_until_complete(run())
-finally:
-    loop.close()
-
-
-
-

Implementation

-

The complete reference implementation is available at [1].

-
- - - diff --git a/peps/tests/peps/pep-3001-1.png b/peps/tests/peps/pep-3001-1.png deleted file mode 100644 index 7f63aea5041e88f26907237a7131f989a0c975e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14117 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 ngzX&X`Tj - --- - - - - - - - - - - - - - - - - - - - - - -
PEP:3001
Title:Procedure for reviewing and improving standard library modules
Version:$Revision$
Last-Modified:$Date$
Author:Georg Brandl <georg at python.org>
Status:Withdrawn
Type:Process
Content-Type:text/x-rst
Created:05-Apr-2006
Post-History:
-
- -
-

Abstract

-

This PEP describes a procedure for reviewing and improving standard -library modules, especially those written in Python, making them ready -for Python 3000. There can be different steps of refurbishing, each -of which is described in a section below. Of course, not every step -has to be performed for every module.

-
-
-

Removal of obsolete modules

-

All modules marked as deprecated in 2.x versions should be removed for -Python 3000. The same applies to modules which are seen as obsolete today, -but are too widely used to be deprecated or removed. Python 3000 is the -big occasion to get rid of them. pep-3001-1.png

-

There will have to be a document listing all removed modules, together -with information on possible substitutes or alternatives. This infor- -mation will also have to be provided by the python3warn.py porting -helper script mentioned in PEP XXX.

-
-
-

Renaming modules

-

There are proposals for a "great stdlib renaming" introducing a hierarchic -library namespace or a top-level package from which to import standard -modules. That possibility aside, some modules' names are known to have -been chosen unwisely, a mistake which could never be corrected in the 2.x -series. Examples are names like "StringIO" or "Cookie". For Python 3000, -there will be the possibility to give those modules less confusing and -more conforming names.

-

Of course, each rename will have to be stated in the documentation of -the respective module and perhaps in the global document of Step 1. -Additionally, the python3warn.py script will recognize the old module -names and notify the user accordingly.

-

If the name change is made in time for another release of the Python 2.x -series, it is worth considering to introduce the new name in the 2.x -branch to ease transition.

-
-
-

Code cleanup

-

As most library modules written in Python have not been touched except -for bug fixes, following the policy of never changing a running system, -many of them may contain code that is not up to the newest language -features and could be rewritten in a more concise, modern Python.

-

PyChecker should run cleanly over the library. With a carefully tuned -configuration file, PyLint should also emit as few warnings as possible.

-

As long as these changes don't change the module's interface and behavior, -no documentation updates are necessary.

-
-
-

Enhancement of test and documentation coverage

-

Code coverage by unit tests varies greatly between modules. Each test -suite should be checked for completeness, and the remaining classic tests -should be converted to PyUnit (or whatever new shiny testing framework -comes with Python 3000, perhaps py.test?).

-

It should also be verified that each publicly visible function has a -meaningful docstring which ideally contains several doctests.

-

No documentation changes are necessary for enhancing test coverage.

-
-
-

Unification of module metadata

-

This is a small and probably not very important step. There have been -various attempts at providing author, version and similar metadata in -modules (such as a "__version__" global). Those could be standardized -and used throughout the library.

-

No documentation changes are necessary for this step, too.

-
-
-

Backwards incompatible bug fixes

-

Over the years, many bug reports have been filed which complained about -bugs in standard library modules, but have subsequently been closed as -"Won't fix" since a fix would have introduced a major incompatibility -which was not acceptable in the Python 2.x series. In Python 3000, the -fix can be applied if the interface per se is still acceptable.

-

Each slight behavioral change caused by such fixes must be mentioned in -the documentation, perhaps in a "Changed in Version 3.0" paragraph.

-
-
-

Interface changes

-

The last and most disruptive change is the overhaul of a module's public -interface. If a module's interface is to be changed, a justification -should be made beforehand, or a PEP should be written.

-

The change must be fully documented as "New in Version 3.0", and the -python3warn.py script must know about it.

-
-
-

References

-

None yet.

-
- - diff --git a/peps/tests/test_commands.py b/peps/tests/test_commands.py deleted file mode 100644 index 2579a5f99..000000000 --- a/peps/tests/test_commands.py +++ /dev/null @@ -1,56 +0,0 @@ -import io - -from bs4 import BeautifulSoup - -from django.test import TestCase, override_settings -from django.conf import settings -from django.core import serializers -from django.core.management import call_command - -import responses - -from pages.models import Image - -from . import FAKE_PEP_ARTIFACT - - -PEP_ARTIFACT_URL = 'https://example.net/fake-peps.tar.gz' - - -@override_settings(PEP_ARTIFACT_URL=PEP_ARTIFACT_URL) -class PEPManagementCommandTests(TestCase): - - def setUp(self): - responses.add( - responses.GET, - PEP_ARTIFACT_URL, - headers={'Last-Modified': 'Sun, 24 Feb 2019 18:01:42 GMT'}, - stream=True, - content_type='application/x-tar', - status=200, - body=open(FAKE_PEP_ARTIFACT, 'rb'), - ) - - @responses.activate - def test_generate_pep_pages_real_with_remote_artifact(self): - call_command('generate_pep_pages') - - @override_settings(PEP_ARTIFACT_URL=FAKE_PEP_ARTIFACT) - def test_generate_pep_pages_real_with_local_artifact(self): - call_command('generate_pep_pages') - - @responses.activate - def test_image_generated(self): - call_command('generate_pep_pages') - img = Image.objects.get(page__path='dev/peps/pep-3001/') - soup = BeautifulSoup(img.page.content.raw, 'lxml') - self.assertIn(settings.MEDIA_URL, soup.find('img')['src']) - - @responses.activate - def test_dump_pep_pages(self): - call_command('generate_pep_pages') - stdout = io.StringIO() - call_command('dump_pep_pages', stdout=stdout) - output = stdout.getvalue() - result = list(serializers.deserialize('json', output)) - self.assertGreater(len(result), 0) diff --git a/peps/tests/test_converters.py b/peps/tests/test_converters.py deleted file mode 100644 index 833bf7c0e..000000000 --- a/peps/tests/test_converters.py +++ /dev/null @@ -1,64 +0,0 @@ -from django.test import TestCase, override_settings -from django.core.exceptions import ImproperlyConfigured -from django.test.utils import captured_stdout - -from peps.converters import get_pep0_page, get_pep_page, add_pep_image - -from . import FAKE_PEP_REPO - - -class PEPConverterTests(TestCase): - - def test_source_link(self): - pep = get_pep_page(FAKE_PEP_REPO, '0525') - self.assertEqual(pep.title, 'PEP 525 -- Asynchronous Generators') - self.assertIn( - 'Source: https://github.com/python/peps/blob/master/pep-0525.txt', - pep.content.rendered - ) - - def test_source_link_rst(self): - pep = get_pep_page(FAKE_PEP_REPO, '0012') - self.assertEqual(pep.title, 'PEP 12 -- Sample reStructuredText PEP Template') - self.assertIn( - 'Source: https://github.com/python/peps/blob/master/pep-0012.rst', - pep.content.rendered - ) - - def test_invalid_pep_number(self): - with captured_stdout() as stdout: - get_pep_page(FAKE_PEP_REPO, '9999999') - self.assertRegex( - stdout.getvalue(), - r"PEP Path '(.*)9999999(.*)' does not exist, skipping" - ) - - def test_add_image_not_found(self): - with captured_stdout() as stdout: - add_pep_image(FAKE_PEP_REPO, '0525', '/path/that/does/not/exist') - self.assertRegex( - stdout.getvalue(), - r"Image Path '(.*)/path/that/does/not/exist(.*)' does not exist, skipping" - ) - - def test_html_do_not_prettify(self): - pep = get_pep_page(FAKE_PEP_REPO, '3001') - self.assertEqual( - pep.title, - 'PEP 3001 -- Procedure for reviewing and improving standard library modules' - ) - self.assertIn( - 'Title:' - 'Procedure for reviewing and improving ' - 'standard library modules\n', - pep.content.rendered - ) - - def test_strip_html_and_body_tags(self): - pep = get_pep_page(FAKE_PEP_REPO, '0525') - self.assertNotIn('', pep.content.rendered) - self.assertNotIn('', pep.content.rendered) - self.assertNotIn('', pep.content.rendered) - self.assertNotIn('', pep.content.rendered) diff --git a/pydotorg/settings/base.py b/pydotorg/settings/base.py index 30dc8de4a..2c392b355 100644 --- a/pydotorg/settings/base.py +++ b/pydotorg/settings/base.py @@ -222,7 +222,6 @@ 'minutes', 'nominations', 'pages', - 'peps', 'sponsors', 'successstories', 'users', @@ -285,10 +284,6 @@ ### Registration mailing lists MAILING_LIST_PSF_MEMBERS = "psf-members-announce-request@python.org" -### PEP Repo Location -PEP_REPO_PATH = None -PEP_ARTIFACT_URL = 'https://pythondotorg-assets-staging.s3.amazonaws.com/fake-peps.tar.gz' - ### Fastly ### FASTLY_API_KEY = False # Set to Fastly API key in production to allow pages to # be purged on save diff --git a/templates/components/pep-widget.html b/templates/components/pep-widget.html deleted file mode 100644 index bba29ea2d..000000000 --- a/templates/components/pep-widget.html +++ /dev/null @@ -1,19 +0,0 @@ -{% load peps %} -
- -

- >>> Python Enhancement Proposals (PEPs): The future of Python is discussed here. - -

- - - {# This isn't that awesome, commenting out for now #} - {% comment %} - {% get_newest_pep_pages as peps %} - - {% endcomment %} -
\ No newline at end of file diff --git a/templates/python/documentation.html b/templates/python/documentation.html index 7db3662d2..e301b0010 100644 --- a/templates/python/documentation.html +++ b/templates/python/documentation.html @@ -106,7 +106,4 @@

P - - {% include 'components/pep-widget.html' %} - {% endblock content %} diff --git a/templates/python/index.html b/templates/python/index.html index ac8b191df..753a53407 100644 --- a/templates/python/index.html +++ b/templates/python/index.html @@ -85,8 +85,6 @@ - {% include 'components/pep-widget.html' %} - {% include 'components/psf-widget.html' %} {% endblock content %} From 552229a495fc79990cc311d85080efdecfd652c1 Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Mon, 16 Sep 2024 08:05:48 -0500 Subject: [PATCH 2/6] feat(#639): update wording for job tech -> type (#2557) --- ...pe_options_alter_job_job_types_and_more.py | 27 +++++++++++++++++++ jobs/models.py | 8 +++--- 2 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 jobs/migrations/0022_alter_jobtype_options_alter_job_job_types_and_more.py diff --git a/jobs/migrations/0022_alter_jobtype_options_alter_job_job_types_and_more.py b/jobs/migrations/0022_alter_jobtype_options_alter_job_job_types_and_more.py new file mode 100644 index 000000000..4013f2376 --- /dev/null +++ b/jobs/migrations/0022_alter_jobtype_options_alter_job_job_types_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.16 on 2024-09-13 17:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jobs', '0021_alter_job_creator_alter_job_last_modified_by_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='jobtype', + options={'ordering': ('name',), 'verbose_name': 'job types', 'verbose_name_plural': 'job types'}, + ), + migrations.AlterField( + model_name='job', + name='job_types', + field=models.ManyToManyField(blank=True, limit_choices_to={'active': True}, related_name='jobs', to='jobs.jobtype', verbose_name='Job types'), + ), + migrations.AlterField( + model_name='job', + name='other_job_type', + field=models.CharField(blank=True, max_length=100, verbose_name='Other job types'), + ), + ] diff --git a/jobs/models.py b/jobs/models.py index 54722873d..8b232fb93 100644 --- a/jobs/models.py +++ b/jobs/models.py @@ -30,8 +30,8 @@ class JobType(NameSlugModel): objects = JobTypeQuerySet.as_manager() class Meta: - verbose_name = 'job technologies' - verbose_name_plural = 'job technologies' + verbose_name = 'job types' + verbose_name_plural = 'job types' ordering = ('name', ) @@ -59,11 +59,11 @@ class Job(ContentManageable): JobType, related_name='jobs', blank=True, - verbose_name='Job technologies', + verbose_name='Job types', limit_choices_to={'active': True}, ) other_job_type = models.CharField( - verbose_name='Other job technologies', + verbose_name='Other job types', max_length=100, blank=True, ) From 7eac3351ee0827dc7f4a311d1164e56b141f3e2a Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Mon, 16 Sep 2024 08:06:55 -0500 Subject: [PATCH 3/6] feat(#2492): use newest compose command (#2558) --- Makefile | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 50585463a..bd4291bbe 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ default: .state/docker-build-web: Dockerfile dev-requirements.txt base-requirements.txt # Build web container for this project - docker-compose build --force-rm web + docker compose build --force-rm web # Mark the state so we don't rebuild this needlessly. mkdir -p .state && touch .state/docker-build-web @@ -24,35 +24,35 @@ default: .state/db-initialized: .state/docker-build-web .state/db-migrated # Load all fixtures - docker-compose run --rm web ./manage.py loaddata fixtures/*.json + docker compose run --rm web ./manage.py loaddata fixtures/*.json # Mark the state so we don't rebuild this needlessly. mkdir -p .state && touch .state/db-initialized serve: .state/db-initialized - docker-compose up --remove-orphans + docker compose up --remove-orphans migrations: .state/db-initialized # Run Django makemigrations - docker-compose run --rm web ./manage.py makemigrations + docker compose run --rm web ./manage.py makemigrations migrate: .state/docker-build-web # Run Django migrate - docker-compose run --rm web ./manage.py migrate + docker compose run --rm web ./manage.py migrate manage: .state/db-initialized # Run Django manage to accept arbitrary arguments - docker-compose run --rm web ./manage.py $(filter-out $@,$(MAKECMDGOALS)) + docker compose run --rm web ./manage.py $(filter-out $@,$(MAKECMDGOALS)) shell: .state/db-initialized - docker-compose run --rm web ./manage.py shell + docker compose run --rm web ./manage.py shell clean: - docker-compose down -v + docker compose down -v rm -f .state/docker-build-web .state/db-initialized .state/db-migrated test: .state/db-initialized - docker-compose run --rm web ./manage.py test + docker compose run --rm web ./manage.py test docker_shell: .state/db-initialized - docker-compose run --rm web /bin/bash + docker compose run --rm web /bin/bash From 4aa0d26fc41ab98642f30722b4b94677f494ca2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 10:44:14 -0500 Subject: [PATCH 4/6] Bump panflute from 2.3.0 to 2.3.1 (#2563) Bumps [panflute](https://github.com/sergiocorreia/panflute) from 2.3.0 to 2.3.1. - [Release notes](https://github.com/sergiocorreia/panflute/releases) - [Commits](https://github.com/sergiocorreia/panflute/commits) --- updated-dependencies: - dependency-name: panflute dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- base-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base-requirements.txt b/base-requirements.txt index 4f9c0aa39..680685cfc 100644 --- a/base-requirements.txt +++ b/base-requirements.txt @@ -52,5 +52,5 @@ django-extensions==3.1.4 django-import-export==2.7.1 pypandoc==1.12 -panflute==2.3.0 +panflute==2.3.1 Unidecode==1.3.8 From 0969d7213ee012331fa05b12ca35c87fa60a4eb2 Mon Sep 17 00:00:00 2001 From: Ee Durbin Date: Mon, 16 Sep 2024 11:45:02 -0400 Subject: [PATCH 5/6] Pages: Also purge trailing slash (#2565) Fastly purge requests are sensitive to trailing slash, so to ensure we get the result we want we should also purge that. --- pages/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pages/models.py b/pages/models.py index 9b67997e1..c3973ce68 100644 --- a/pages/models.py +++ b/pages/models.py @@ -137,6 +137,8 @@ def purge_fastly_cache(sender, instance, **kwargs): Requires settings.FASTLY_API_KEY being set """ purge_url(f'/{instance.path}') + if not instance.path.endswith('/'): + purge_url(f'/{instance.path}/') def page_image_path(instance, filename): From 1795b1f27dba8b40c27c82675411a8ba4978563b Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Mon, 16 Sep 2024 11:00:00 -0500 Subject: [PATCH 6/6] feat: add ngwaf (#2527) * feat: add ngwaf with successful tfplan * docs: update to latest var name * chore: apply formatting * feat: make ngwaf bits enabled via var * fix: use var fvor activation * fix: fix invalid syntax * fix: fix invalid syntax again * chore: use service account * chore: cleanup cruft * fix: apply patch for dynamic dynamic things * Update infra/cdn/README.md * Update infra/cdn/README.md --- infra/.terraform.lock.hcl | 22 +++++++++++++ infra/cdn/README.md | 28 ++++++++++++++-- infra/cdn/main.tf | 69 +++++++++++++++++++++++++++++++++++++++ infra/cdn/ngwaf.tf | 49 +++++++++++++++++++++++++++ infra/cdn/providers.tf | 8 +++++ infra/cdn/variables.tf | 36 +++++++++++++++++++- infra/cdn/versions.tf | 4 +++ infra/main.tf | 18 +++++++--- infra/variables.tf | 7 +++- 9 files changed, 233 insertions(+), 8 deletions(-) create mode 100644 infra/cdn/ngwaf.tf diff --git a/infra/.terraform.lock.hcl b/infra/.terraform.lock.hcl index 165cd9357..5844f52bd 100644 --- a/infra/.terraform.lock.hcl +++ b/infra/.terraform.lock.hcl @@ -22,3 +22,25 @@ provider "registry.terraform.io/fastly/fastly" { "zh:ec8d899cafd925d3492f00c6523c90599aebc43c1373ad4bd6c55f12d2376230", ] } + +provider "registry.terraform.io/signalsciences/sigsci" { + version = "3.3.0" + constraints = "3.3.0" + hashes = [ + "h1:DIoFVzfofY8lQSxFTw9wmQQC28PPMq+5l3xbPNw9gLc=", + "zh:07c25e1cca9c13314429a8430c2e999ad94c7d5e2f2a11501ee2608182387e61", + "zh:07daf79b672f3e0bec7b48e3ac8dcdeec02af06b10d653bd8158a74236b0746b", + "zh:1e24a050c3d3571ec3224c4bb5c82635caf636e707b5993a1cc97c9a1f19fa8f", + "zh:24293ae24b3de13bda8512c47967f01814724805396a1bfbfbfc56f5627615cc", + "zh:2cc6ba7a38d9854146d1d05f4b7a2f8e18a33c1267b768506cbe37168dad01dc", + "zh:42065bfee0cfde04096d6140c65379253359bed49b481a97aff70aa65bf568b3", + "zh:6f7f4d96967dfd92f098b57647d396679b70d92548db6d100c4dc8723569d175", + "zh:a2e4431f045cef16ed152c0d1f8a377b6468351b775ad1ca7ce3fe74fb874be2", + "zh:b0ed1cb03d6f191fe211f10bb59ef8daed6f89e3d99136e7bb5d38f2ac72fa45", + "zh:b61ea18442a65d27b97dd1cd43bdd8d0a56c2b4b8db6355480e89f8507c6782a", + "zh:c31bb2f50ac2a636758f93afec0b9d173be6d7d7476f9e250b4554e70c6d8d82", + "zh:cb7337f7b4678ad7ece28741069c07ce5601d2a103a9667db568cf10ed0ee5a2", + "zh:d521a7dac51733aebb0905e25b8f7c1279d83c06136e87826e010c667528fd3e", + "zh:ef791688acee3b8b1191b3c6dc54dabf69612dbfb666720280b492ce348a3a06", + ] +} diff --git a/infra/cdn/README.md b/infra/cdn/README.md index 6ebe5a637..a667f63db 100644 --- a/infra/cdn/README.md +++ b/infra/cdn/README.md @@ -29,5 +29,29 @@ N/A ## Requirements Tested on -- Tested on Terraform 1.8.5 -- Fastly provider 5.13.0 \ No newline at end of file +- Tested on Terraform 1.9.5 +- Fastly provider 5.13.0 + +# Fastly's NGWAF + +This module also conditionally can set up the Fastly Next-Gen Web Application Firewall (NGWAF) +for our Fastly services related to python.org / test.python.org. + +## Usage + +```hcl +module "fastly_production" { + source = "./cdn" + + ... + activate_ngwaf_service = true + ... +} +``` + +## Requirements + +Tested on +- Terraform 1.9.5 +- Fastly provider 5.13.0 +- SigSci provider 3.3.0 \ No newline at end of file diff --git a/infra/cdn/main.tf b/infra/cdn/main.tf index 12d1fbba4..eb6c6858c 100644 --- a/infra/cdn/main.tf +++ b/infra/cdn/main.tf @@ -342,4 +342,73 @@ resource "fastly_service_vcl" "python_org" { response = "Forbidden" status = 403 } + + dynamic "dictionary" { + for_each = var.activate_ngwaf_service ? [1] : [] + content { + name = var.edge_security_dictionary + } + } + + dynamic "dynamicsnippet" { + for_each = var.activate_ngwaf_service ? [1] : [] + content { + name = "ngwaf_config_init" + type = "init" + priority = 0 + } + } + + dynamic "dynamicsnippet" { + for_each = var.activate_ngwaf_service ? [1] : [] + content { + name = "ngwaf_config_miss" + type = "miss" + priority = 9000 + } + } + + dynamic "dynamicsnippet" { + for_each = var.activate_ngwaf_service ? [1] : [] + content { + name = "ngwaf_config_pass" + type = "pass" + priority = 9000 + } + } + + dynamic "dynamicsnippet" { + for_each = var.activate_ngwaf_service ? [1] : [] + content { + name = "ngwaf_config_deliver" + type = "deliver" + priority = 9000 + } + } + + lifecycle { + ignore_changes = [ + product_enablement, + ] + } +} + +output "service_id" { + value = fastly_service_vcl.python_org.id + description = "The ID of the Fastly service" +} + +output "backend_address" { + value = var.backend_address + description = "The backend address for the service." +} + +output "service_name" { + value = var.name + description = "The name of the Fastly service" +} + +output "domain" { + value = var.domain + description = "The domain of the Fastly service" } diff --git a/infra/cdn/ngwaf.tf b/infra/cdn/ngwaf.tf new file mode 100644 index 000000000..8ca3a61f6 --- /dev/null +++ b/infra/cdn/ngwaf.tf @@ -0,0 +1,49 @@ +resource "fastly_service_dictionary_items" "edge_security_dictionary_items" { + count = var.activate_ngwaf_service ? 1 : 0 + service_id = fastly_service_vcl.python_org.id + dictionary_id = one([for d in fastly_service_vcl.python_org.dictionary : d.dictionary_id if d.name == var.edge_security_dictionary]) + items = { + Enabled : "100" + } +} + +resource "fastly_service_dynamic_snippet_content" "ngwaf_config_snippets" { + for_each = var.activate_ngwaf_service ? toset(["init", "miss", "pass", "deliver"]) : [] + service_id = fastly_service_vcl.python_org.id + snippet_id = one([for d in fastly_service_vcl.python_org.dynamicsnippet : d.snippet_id if d.name == "ngwaf_config_${each.key}"]) + content = "### Terraform managed ngwaf_config_${each.key}" + manage_snippets = false +} + +# NGWAF Edge Deployment on SignalSciences.net +resource "sigsci_edge_deployment" "ngwaf_edge_site_service" { + count = var.activate_ngwaf_service ? 1 : 0 + provider = sigsci.firewall + site_short_name = var.ngwaf_site_name +} + +resource "sigsci_edge_deployment_service" "ngwaf_edge_service_link" { + count = var.activate_ngwaf_service ? 1 : 0 + provider = sigsci.firewall + site_short_name = var.ngwaf_site_name + fastly_sid = fastly_service_vcl.python_org.id + activate_version = var.activate_ngwaf_service + percent_enabled = 100 + depends_on = [ + sigsci_edge_deployment.ngwaf_edge_site_service, + fastly_service_vcl.python_org, + fastly_service_dictionary_items.edge_security_dictionary_items, + fastly_service_dynamic_snippet_content.ngwaf_config_snippets, + ] +} + +resource "sigsci_edge_deployment_service_backend" "ngwaf_edge_service_backend_sync" { + count = var.activate_ngwaf_service ? 1 : 0 + provider = sigsci.firewall + site_short_name = var.ngwaf_site_name + fastly_sid = fastly_service_vcl.python_org.id + fastly_service_vcl_active_version = fastly_service_vcl.python_org.active_version + depends_on = [ + sigsci_edge_deployment_service.ngwaf_edge_service_link, + ] +} diff --git a/infra/cdn/providers.tf b/infra/cdn/providers.tf index 201f5de4a..bdee7a807 100644 --- a/infra/cdn/providers.tf +++ b/infra/cdn/providers.tf @@ -2,3 +2,11 @@ provider "fastly" { alias = "cdn" api_key = var.fastly_key } + +provider "sigsci" { + alias = "firewall" + corp = var.ngwaf_corp_name + email = var.ngwaf_email + auth_token = var.ngwaf_token + fastly_api_key = var.fastly_key +} diff --git a/infra/cdn/variables.tf b/infra/cdn/variables.tf index 4cbf6db6e..5c1be4562 100644 --- a/infra/cdn/variables.tf +++ b/infra/cdn/variables.tf @@ -40,4 +40,38 @@ variable "backend_address" { variable "default_ttl" { type = number description = "The default TTL for the service." -} \ No newline at end of file +} + +## NGWAF +variable "activate_ngwaf_service" { + type = bool + description = "Whether to activate the NGWAF service." +} +variable "edge_security_dictionary" { + type = string + description = "The dictionary name for the Edge Security product." + default = "" +} +variable "ngwaf_corp_name" { + type = string + description = "Corp name for NGWAF" + default = "python" +} +variable "ngwaf_site_name" { + type = string + description = "Site SHORT name for NGWAF" + + validation { + condition = can(regex("^(test|stage|prod)$", var.ngwaf_site_name)) + error_message = "'ngwaf_site_name' must be one of the following: test, stage, or prod" + } +} +variable "ngwaf_email" { + type = string + description = "Email address associated with the token for the NGWAF API." +} +variable "ngwaf_token" { + type = string + description = "Secret token for the NGWAF API." + sensitive = true +} diff --git a/infra/cdn/versions.tf b/infra/cdn/versions.tf index da9c01f79..f8c137ba6 100644 --- a/infra/cdn/versions.tf +++ b/infra/cdn/versions.tf @@ -4,5 +4,9 @@ terraform { source = "fastly/fastly" version = "5.13.0" } + sigsci = { + source = "signalsciences/sigsci" + version = "3.3.0" + } } } diff --git a/infra/main.tf b/infra/main.tf index b3ec26a77..90c2ba9c5 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -12,15 +12,20 @@ module "fastly_production" { fastly_key = var.FASTLY_API_KEY fastly_header_token = var.FASTLY_HEADER_TOKEN s3_logging_keys = var.fastly_s3_logging + + ngwaf_site_name = "prod" + ngwaf_email = "infrastructure-staff@python.org" + ngwaf_token = var.ngwaf_token + activate_ngwaf_service = false } module "fastly_staging" { source = "./cdn" - name = "test.python.org" - domain = "test.python.org" - subdomain = "www.test.python.org" - extra_domains = ["www.test.python.org"] + name = "test.python.org" + domain = "test.python.org" + subdomain = "www.test.python.org" + extra_domains = ["www.test.python.org"] # TODO: adjust to test-pythondotorg when done testing NGWAF backend_address = "pythondotorg.ingress.us-east-2.psfhosted.computer" default_ttl = 3600 @@ -29,4 +34,9 @@ module "fastly_staging" { fastly_key = var.FASTLY_API_KEY fastly_header_token = var.FASTLY_HEADER_TOKEN s3_logging_keys = var.fastly_s3_logging + + ngwaf_site_name = "test" + ngwaf_email = "infrastructure-staff@python.org" + ngwaf_token = var.ngwaf_token + activate_ngwaf_service = true } diff --git a/infra/variables.tf b/infra/variables.tf index ec23b23ec..33fc1dda5 100644 --- a/infra/variables.tf +++ b/infra/variables.tf @@ -17,4 +17,9 @@ variable "fastly_s3_logging" { type = map(string) description = "S3 bucket keys for Fastly logging" sensitive = true -} \ No newline at end of file +} +variable "ngwaf_token" { + type = string + description = "Secret token for the NGWAF API." + sensitive = true +}