Hi, Here is a proposal about naming conventions around packaging.
Main question is: would you accept it as a PEP? ############################################ Preliminary notes (not part of the proposal) ############################################ Before I start, here are some preliminary notes about the context... ********* Situation ********* * first posted to distutils-sig list: http://mail.python.org/pipermail/distutils-sig/2012-May/018551.html * then opened a ticket on CPython issue tracker: http://bugs.python.org/issue14899 * started to work in a fork... https://bitbucket.org/benoitbryon/cpython/src/doc-package-names/Doc/packaging/packagenames.rst * ... but this looks like a PEP. So, let's follow PEP 1 and post a proposal here. *********************** What follows is a draft *********************** * First of all, I promise I did my best ;) * I guess most points already have been discussed here or elsewhere. But since I couldn't find an official reference (i.e. a refused PEP), I believe consensus is possible. * I splitted proposals in small sections, so that people can discuss or refuse some items while accepting others. If you see something that is not atomic, please propose a split. * I tried to relate facts. But I feel like a newbie about some points, so, if you see something wrong or incomplete, please send information and/or sources. * I tried to propose actions and take existing projects into account. In my humble opinion, motivations and proposals below seem reasonable enough. That's why I am posting here to get feedback from the community. * Finally, I am not a native english speaker... feel free to improve vocabulary, grammar and so on :) That said, let's go... .. note:: You can also get the following proposal online at https://bitbucket.org/benoitbryon/cpython/src/doc-package-names/Doc/packaging/packagenames.rst Thanks to Martin Aspeli for his article. Thanks to early reviewers: Alexis Métaireau, Éric Bréhault, Jean-Philippe Camguilhem and Mathieu Leplâtre. Regards, Benoit ########################################### Names in packaging: conventions and recipes ########################################### This document deals with: * names of Python projects, * names of distributions in projects, * names of Python packages or modules being distributed, * namespace packages. It provides conventions and recipes for distribution authors. Main use case is: * as a developer, I want to create a project in order to distribute a package. So I have to choose names. Which names are "good"? * `The Zen of Python`_ says: In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. * So I need clear and official (i.e. obvious) guidelines or conventions that I can follow. * Here are conventions, guidelines and recipes. Guidelines for existing projects are also given. *********** Terminology *********** First of all, let's make sure there is no confusion... Distribution name Distribution name is used by packaging utilities: * in :doc:`setup script</packaging/setupscript>`, it is the value passed as ``name`` to ``packaging.core.setup()`` * it appears on `PyPI`_ if the package is registered on it * it can be used in `pip` requirements files * it can be used in `buildout` configuration files. Distribution term is introduced in :doc:`packaging docs</packaging/index>`. Egg name It is the same concept as distribution name. Technically, the egg is not the distribution. But they use the same name: it is declared as ``packaging.core.setup()`` name argument. "Egg" term is the legacy counterpart to "distribution". It was used by distutils and setuptools. It becomes deprecated with the new packaging module. This document focuses on distributions, not eggs. Package and module names Package and module names are used in Python code. It is the string used in :ref:`import statements<import>`. Remember that, from a file system perspective, packages are directories and modules are files. :ref:`Python packaging allows distributions to distribute several packages and/or modules<setupcfg-section-files>`. Project name Usually the name of the repository or folder in which distribution authors put their code. It generally is the directory name of the "distribution root", as defined in :ref:`packaging-term`. Namespace packages It is common practice to use namespaces in package names. `PEP 420`_ brings this concept to core Python. When PEP 420 will be accepted, Python officially supports namespace packages. As an example, consider `django-debug-toolbar`_: * ``django-debug-toolbar`` is the distribution name. `It is declared in setup.py file <https://github.com/django-debug-toolbar/django-debug-toolbar/blob/599d76e41c5728c0e342d55250690b2694bd2aad/setup.py#L4>`_. * ``debug_toolbar`` is the package name. It is what would appear in Django's INSTALLED_APPS setting or be used as ``import debug_toolbar``. Technically, all those names can be different. ********* Rationale ********* Relationship with other PEPs ============================ * `PEP 8`_ deals with code style guide, including names of Python packages and modules. It covers syntax of package/modules names. * `PEP 345`_ deals with packaging metadata, and defines distribution name. * `PEP 420`_ deals with namespace packages. It brings support of namespace packages to Python core. Before, namespaces packages were implemented by external libraries. * `PEP 3108`_ deals with transition between Python 2.x and Python 3.x applied to standard library: some modules to be deleted, some to be renamed. Other sources of inspiration ============================ * `Martin Aspeli's article about names`_. Some parts of this proposal are quotes from this article. * `The Hitchhiker's Guide to Packaging`_, which has an empty placeholder for "naming specification". * and, of course, `in development official packaging documentation`_. Facts ===== Before Python version 3.3, there is no official guidelines to name projects, distributions or packages/modules. Current PEPs (see `Relationship with other PEPs`_) are very open on this topic. Distribution authors have to follow their own intuition. Several standards emerged from communities. As examples: * `Plone`_ community uses "plone.*" namespace for official Plone products, and "collective.*" for community products. This is a convention explicitely promoted by Plone community. `Martin Aspeli's article about names`_ is about conventions and usages in Plone community. .. note:: Is there an official document about these conventions? A PLIP? * most `Django`_ applications from community use "django-\*" pattern as distribution name. This is a de facto standard. * many `Pyramid`_ applications from community use "pyramid_*" pattern as distribution name. Thus, as `PyPI`_ testifies, distribution names and package/module names are really heterogeneous. Impacts ======= Here are points this document tries to resolve. Ambiguity --------- When distribution authors come to choose a name, they can't find an unique official guideline. In such a situation, "Now is better than never" wins over "Refuse temptation to guess". So distribution authors follow one of the conventions they discovered (usually the one from their community), or follow their own intuition. Confusion --------- As explained in `terminology`_ above, project, distribution and package names can be assigned distinct values. That is a big source of confusion, especially for Python developers who are not used to packaging. Time loss --------- As a direct consequence of `ambiguity`_ and `confusion`_, new Python developers spend too much time to understand Python projects/distributions/packages names: * they can't find obvious (i.e. official) naming conventions (or at least guidelines) even if they search for it. They have to ask community to resolve the `ambiguity`_. * it's hard to resolve the `confusion`_ between names. It's even harder because community itself is a bit confused. Their best chance is to find one of the `Other sources of inspiration`_ listed above or ask a well informed person. * developers from some other languages suppose Python have official naming conventions for distributions and packages. So they search for it, and feel worried when they figure out that it doesn't exist. Experienced Python users are less affected: they built their opinion in the past and keep on following their habits. Community partitionning ----------------------- The global Python community is partitionned into opposed sub-communities: * most Python developers are linked to at least one community (i.e. Zope, Plone, Pyramid, Django...). * communities usually resolved naming conventions with official documents or with de facto usage. * developers usually follow their community's standards. * developers usually believe their community made the best choice. They usually adhere to community arguments. * choices and reasons differ from one community to another. * when Python communities meet, package names are a never-ending topic of discussion. * people discuss about package names when they should work together on more valuable stories. * they can't settle the issue, because: * arguments have historical reasons. In history, these reasons were enough. * accepting someone else's arguments means changing habits, and maybe re-packaging existing projects, i.e. efforts and time. * there is no guidelines from an higher authorithy (i.e. python.org). There is no comparison standard. Both choices are legitimate. An additional note about developers who belong to several communities: * they usually adhere to the naming conventions from one community, * it's hard to adopt another convention when contributing in another community. Proposal ======== As `The Zen of Python`_ says: "There should be one-- and preferably only one --obvious way to do it." So the proposal is: * adopt strict conventions where Python community finds a consensus, * provide guidelines or recipes for what cannot be covered by conventions. What about existing projects? ============================= It's impossible to **require** a change for every existing project, for obvious reasons. But it is possible to first **document** existing naming conventions, then **promote** a change. This document proposes two things: * a status on current existing naming conventions, inside each project or community. So that custom naming conventions are at least self-documented. See `Organize community contributions`_ for details. * a `Transition plan`_ for those who are ready to migrate. .. _`packagenames-opportunity`: Opportunity =========== As of Python 3.3 being developed: * many projects are not Python 3.x compatible. It includes "big" products or frameworks. It means that many projects will have to do a migration to support Python 3.x. * packaging (aka distutils2) is on the starting blocks. When it is released, projects will be invited to migrate and use new packaging. * `PEP 420`_ brings official support of namespace packages to Python. It means that most active projects should be about to migrate in the next year(s) to support Python 3.x, new packaging or new namespace packages. Such an opportunity is unique and won't come again soon! So let's introduce and promote naming conventions as soon as possible (i.e. **now**). *************** Transition plan *************** New distributions ================= In order of priority: 1. If the project belongs to a community (i.e. product/framework), **and** the community have official conventions, then follow community conventions. .. note:: :ref:`Communities SHOULD organize contributions <packagenames-organizecommunities>`. As an example new community project related to Plone should be distributed as "collective.*", because it is an explicit standard of the Plone community. 2. New projects SHOULD follow `Conventions`_ described in this document. Existing projects ================= **There is no obligation for existing distributions to be renamed**. The choice is left to distribution authors and mainteners for obvious reasons. However, distribution authors are invited to `promote migrations`_. In order to rename an existing distribution, follow `Renaming howto`_ guidelines below. Promote migrations ================== Every Python developer should migrate whenever possible, or promote the migrations in their respective communities. Apply this convention on your projects, then the community will see it is safe. In particular, "leaders" such as authors of popular projects are influential, they have power and, thus, responsability over communities. Apply this conventions on popular projects, then communities will adopt the conventions too. **Popular projects SHOULD promote migrations when they release a new (major) version**, particularly :ref:`if this version introduces support for Python 3.x, new standard library's packaging or namespace packages <packagenames-opportunity>`. .. note:: On the contrary, if popular projects refuse the conventions, communities may not adopt the conventions. Improved handling of renamed distributions on PyPI ================================================== If many projects follow `Renaming howto`_, many legacy distributions will have the following characteristics: * ``Development Status :: 7 - Inactive`` classifier. * latest version is empty, except packaging stuff. * lastest version "redirects" to another distribution. E.g. it has a single dependency on the renamed distribution. * referenced as ``Obsoletes-Dist`` in a newer distribution. So it will be possible to detect renamed distributions and improve readability on PyPI. So that users can focus on active distributions. But this feature is not required now. There is no urge. It won't be covered in this document. *********** Conventions *********** Rules that you SHOULD follow. If in doubt, ask ================ If you feel unsure after reading the following conventions, ask `Python community`_ on IRC or on a mailing list. Use a single name ================= Distribute only one package (or only one module) in a distribution, and use package (or module) name as project name and distribution name. * It avoids possible confusion between all those names. * It makes the name consistent. * It is explicit: when one sees distribution name, he guesses package name, and vice versa. * It also limits implicit clashes between package/module names. By using a single name, when you register a name to PyPI, you also perform a basic package/module name availability verification. As an example, `pipeline`_, `python-pipeline`_ and `django-pipeline`_ all distribute a package or module called "pipeline". So installing two of them leads to errors. Yes: * Package name: "kheops.pyramid", i.e. ``import kheops.pyramid`` * distribution name: "kheops.pyramid", i.e. ``pip install kheops.pyramid`` * Project name: "kheops.pyramid", i.e. ``git clone g...@github.com/pharaohs/kheops.pyramid.git`` No: * Package name: "kheops" * Distribution name: "kheops-pyramid" * Project name: "KheopsPyramid" .. note:: For historical reasons, on `PyPI`_, you can find many distributions using different values for project, distribution and package/module name. Multiple packages/modules should be rare ---------------------------------------- Technically, Python distributions can provide multiple packages and/or modules. See :ref:`setup script reference<packaging-setup-script>` for details. Some distributions actually does. As an example, `setuptools`_ and `distribute`_ are both declaring "pkg_resources", "easy_install" and "site" modules in addition to respective "setuptools" and "distribute" packages. Consider this use case as exceptional. In most cases, you don't need this feature. So a distribution should provide only one package or module at a time. Explicit distinct names should be rare -------------------------------------- A notable exception to the "Use a single name" rule is when you explicitely need distinct names. As an example, the `Pillow`_ distribution is an alternative to the original `PIL`_ distribution. They both provide a "PIL" package. Consider this use case as exceptional. In most cases, you don't need this feature. So a distributed package name should be equal to distribution name. Follow PEP 8 for package names syntax ===================================== `PEP 8`_ applies to Python package and module names. If you `Use a single name`_, `PEP 8`_ also applies to project and distribution names. The exceptions are namespace packages, where dots are required in the name. Pick meaningful names ===================== Ask yourself "how would I describe in one sentence what this name is for?", and then "could anyone have guessed that by looking at the name?". When you are using namespaces, make sure each part is meaningful. .. _`packagenames-ownership`: Top level namespace relates to code ownership ============================================= This helps avoid clashes between distribution names. Ownership could be: * an individual. Example: `gp.fileupload`_ is owned and maintained by Gael Pasgrimaud. * an organization. Examples: * `zest.releaser`_ is owned and maintained by Zest Software. * `Django`_ is owned and maintained by the Django Software Fundation. * a group or community. Example: `sphinx`_ is maintained by developers of the Sphinx project, not only by its author, Georg Brandl. * a group or community related to another package. Example: `collective.recaptcha`_ is owned by its author: David Glick, Groundwire. But the "collective" namespace is owned by Plone community. Respect ownership ----------------- Understand the purpose of namespace before you use it. **DO NOT** plug into a namespace you don't own, unless explicitely authorized. `If in doubt, ask`_. As an example, **DO NOT** use "django.contrib" namespace: it is managed by Django's core contributors. Exceptions CAN be defined by distribution authors. See `Organize community contributions`_ below. Private (including closed-source) distributions use a namespace --------------------------------------------------------------- ... because private distributions are owned by somebody. So apply the :ref:`ownership rule<packagenames-ownership>`. For internal/customer projects, use your company name as the namespace. This rule applies to closed-source distributions. As an example, if you are creating a "climbing" distribution for the "Python Sport" company: use "pythonsport.climbing" name, even if it is closed source. Individual projects use a namespace ----------------------------------- ... because they are owned by individuals. So apply the :ref:`ownership rule <packagenames-ownership>`. There is no shame in releasing a distribution as open source even if it has an "internal" name. If the project comes to a point where the author wants to change ownership (i.e. the project no longer belongs to an individual), keep in mind :ref:`it is easy to rename the project<packagenames-rename>`. Independant community Python projects CAN avoid namespaces ---------------------------------------------------------- If your project is generic enough (i.e. it is not a contrib to another product or framework), you CAN avoid namespaces. The base condition is generally that your project is owned by a group (i.e. the development team) which is dedicated to this project. Only use a "shared" namespace if you really intend the code to be community owned. As an example, `sphinx`_ project belongs to the Sphinx development team. In doubt, use an individual/organization namespace -------------------------------------------------- If your project is not mature or hasn't been proven useful to a community, best choice is to use an individual or organization namespace. It allows distribution authors to release projects early. And it doesn't block future changes. When a project becomes mature, and if it appears there is no reason to keep individual ownership, :ref:`it remains possible to rename the project<packagenames-rename>`. Avoid deep nesting ================== `The Zen of Python`_ says: Flat is better than nested. Two levels is almost always enough ---------------------------------- Don't define everything in deeply nested hierarchies: you will end up with distributions and packages like "pythonsport.common.maps.forest". This type of name is both verbose and cumbersome (e.g. if you have many imports from the package). Furthermore, big hierarchies tend to break down over time as the boundaries between different packages blur. The consensus is that two levels of nesting are preferred. Yes: "pyranha" Yes: "pythonsport.climbing" Yes: "pythonsport.forestmap" No: "pythonsport.maps.forest" .. _`packagenames-othermetadata`: Limited namespace levels, unlimited metadata -------------------------------------------- Consider distribution names (with or without namespaces) as unique identifiers on PyPI. It is important that these identifiers remain human-readable. It is even better when these identifiers are meaningful. But their primary purpose is not to classify or describe distributions. As examples, if you only look at the name: * you can't guess "nose" is about testing, * or "celery" about distributed task queueing, * or that "lettuce" is about tests, and has nothing in common with "celery". The examples above are not problematic. **`Classifiers`_ and keywords metadata are made for categorization of distributions.** As an example, there is a "Framework :: TurboGears" classifier. Even if names are quite heterogeneous (they don't follow a pattern like collective.* for Plone community projects), we get the list. In order to `Organize community contributions`_, conventions about names and namespaces matter, but conventions about metadata should be even more important. As an example, we can find Plone portlets in many places: * plone.portlet.* * collective.portlet.* * collective.portlets.* * collective.*.portlets * some vendor-related distributions such as "quintagroup.portlet.cumulus" * and even distributions where "portlet" pattern doesn't appear... Even if Plone community has conventions, using the name to categorize distributions is inapropriate. It's impossible to get the full list of distributions that provide portlets for Plone by filtering on names. But it would be possible if all these distributions used "Framework :: Plone" classifier and "portlet" keyword. Do you really need 3 levels? ---------------------------- For example, we have ``plone.principalsource`` instead of ``plone.source.principal`` or something like that. The name is shorter, the package structure is simpler, and there would be very little to gain from having three levels of nesting here. It would be impractical to try to put all "core Plone" sources (a source is kind of vocabulary) into the ``plone.source.*`` namespace, in part because some sources are part of other packages, and in part because sources already exist in other places. Had we made a new namespace, it would be inconsistently used from the start. 3 levels are also tempting when: * you are pluging into a community namespace, such as "collective". * and you want to add a more restrictive "ownership" level, to avoid clashes inside the community. In such a case, you'd better use the most restrictive ownership level as first level. As an example, where "collective" is a major community namespace that "gergovie" belongs to, and "vercingetorix" it the name of "gergovie" author: No: "collective.vercingetorix.gergovie" Yes: "vercingetorix.collectivegergovie" 3 levels are supported for historical reasons --------------------------------------------- Even if not recommended, 3 levels are supported. This is mainly for historical reasons: 3 levels can be accepted where top level namespace owner explicitely allows it with a specific convention. See `Organize community contributions`_ for details. Don't use more than 3 levels ---------------------------- * 1 or 2 levels are recommended. * 3 levels are discouraged, but supported for historical reasons. * you shouldn't need more than 3 levels. .. note:: Even communities where namespaces are standard don't use more than 3 levels. .. _`packagenames-organizecommunities`: Organize community contributions ================================ Actions: * Choose a naming convention for community contributions. * If it is not :ref:`the default<packagenames-contribnamespace>`, document it. * if you use the :ref:`default convention<packagenames-contribnamespace>`, this document should be enough. Don't reapeat it. You MAY reference it. * else, tell users about custom conventions in project's "contribute" or "create modules" documentation. * Also recommend the use of additional metadata, such as :ref:`classifiers and keywords<packagenames-othermetadata>`. About convention choices: * New projects SHOULD choose the default scheme. * Existing projects with community contributions CAN start with custom conventions. Then they SHOULD `Promote migrations`_. It means that existing community conventions doesn't need to be changed. But they need to be explicitely documented: first state about current naming conventions, then about future. Example: "pyranha" is your project name, distribution name and package name. Tell contributors that: * pyranha-related distributions should use the "pyranha" keyword * pyranha distributions providing templates should also use "templates" keyword. * community contributions should be released under "pyranhacontrib" namespace (i.e. use "pyranhacontrib.*" pattern): .. _`packagenames-contribnamespace`: Community contributions SHOULD use "${DIST}contrib.*" pattern ================================================================ The idea is to use a standard pattern to store community contributions for any product or framework. It is the simplest way to `Organize community contributions`_: the obvious way to go is "${DIST}contrib", no ambiguity. As an example: * you are the author of "pyranha" project. You own the "pyranha" namespace. * a third-party developer wants to publish a "giantteeth" project related to your "pyranha" project. He can publish it as "pyranhacontrib.giantteeth". .. note:: Why ``${DIST}contrib.*`` pattern? * ``${DIST}c.*`` is not explicit enough. As examples, "zc" belongs to "Zope Corporation" whereas "z3c" belongs to "Zope 3 community". * ``${DIST}community`` is too long. * ``${DIST}community`` conflicts with existing namespaces such as "iccommunity" or "PyCommunity". * ``${DIST}.contrib`` is inside ${DIST} namespace, i.e. it is owned by ${DIST} authors. It breaks the `Top level namespace relates to code ownership`_. * ``${DIST}.contrib.*`` breaks the `Avoid deep nesting`_ rule. * names where ``${DIST}`` doesn't appear are not explicit enough, i.e. nobody can guess they are related to ``${DIST}``. * ``{$DIST}contrib.*`` may conflict with existing ``sphinxcontrib-*`` packages. But ``sphinxcontrib-*`` is actually about Sphinx contrib, so this is not a real conflict... In fact, the "contrib" suffix was inspired by "sphinxcontrib". ******* Recipes ******* How to avoid duplicate names ============================ Before you choose a distribution name, make sure it hasn't already been registered in the following locations: * `PyPI`_ * Popular code repositories such as: * `Github`_ * `Bitbucket`_ * `Gitorious`_ * `djangopackages.com`_ .. note:: A web service would be welcome for this! Also make sure the package name hasn't already been registered: * in the `Python Standard Library`_, * in the locations where you checked for distribution name availability. .. _`packagenames-rename`: Renaming howto ============== Renaming a project is possible, but keep in mind that it will cause some confusions. So, pay particular attention to README and documentation, so that users understand what happened. #. First of all, **do not remove legacy distribution from PyPI**. Because some users may be using it. #. Copy the legacy project, then change names (project, distribution and package/module). Pay attention to, at least: * packaging files, * folder name that contains source files, * documentation, including README, * import statements in code. #. Assign ``Obsoletes-Dist`` metadata to new distribution in setup.cfg file. See `PEP 345 about Obsolete-Dist`_ and :ref:`setup.cfg specification <setupcfg-spec>`. #. Release the renamed distribution as a new version, then publish it. #. Edit legacy distribution: * add dependency to new distribution, * drop everything except packaging stuff, * add the ``Development Status :: 7 - Inactive`` classifier in setup script, * publish a new release. So, users of the legacy package: * can continue using the legacy distribution at a deprecated version, * can upgrade to last version of legacy distribution, which is empty, ... * ... and automatically download new distribution as a dependency of the legacy one. Users who discover the legacy distribution see it is inactive. ********** References ********** .. target-notes:: .. _`Martin Aspeli's article about names`: http://www.martinaspeli.net/articles/the-naming-of-things-package-names-and-namespaces .. _`PEP 1`: http://www.python.org/dev/peps/pep-0001/ .. _`The Zen of Python`: http://www.python.org/dev/peps/pep-0020/ .. _`PEP 8`: http://www.python.org/dev/peps/pep-0008/#package-and-module-names .. _`PEP 345`: http://www.python.org/dev/peps/pep-0345/ .. _`PEP 420`: http://www.python.org/dev/peps/pep-0420/ .. _`PEP 3108`: http://www.python.org/dev/peps/pep-3108 .. _`The Hitchhiker's Guide to Packaging`: http://guide.python-distribute.org/specification.html#naming-specification .. _`in development official packaging documentation`: http://docs.python.org/dev/packaging/ .. _`plone`: http://plone.org/community/develop .. _`django`: http://djangoproject.com/ .. _`pyramid`: http://pylonsproject.org .. _`pypi`: http://pypi.python.org .. _`django-debug-toolbar`: https://github.com/django-debug-toolbar/django-debug-toolbar .. _`gp.fileupload`: http://pypi.python.org/pypi/gp.fileupload/ .. _`zest.releaser`: http://pypi.python.org/pypi/zest.releaser/ .. _`sphinx`: http://sphinx.pocoo.org .. _`Classifiers`: http://pypi.python.org/pypi?:action=list_classifiers .. _`collective.recaptcha`: http://pypi.python.org/pypi/collective.recaptcha/ .. _`Python community`: http://www.python.org/community/ .. _`pipeline`: http://pypi.python.org/pypi/pipeline/ .. _`python-pipeline`: http://pypi.python.org/pypi/python-pipeline/ .. _`django-pipeline`: http://pypi.python.org/pypi/django-pipeline/ .. _`setuptools`: http://pypi.python.org/pypi/setuptools .. _`distribute`: http://packages.python.org/distribute/ .. _`Pillow`: http://pypi.python.org/pypi/Pillow/ .. _`PIL`: http://pypi.python.org/pypi/PIL/ .. _`Python Standard Library`: http://docs.python.org/library/index.html .. _`github`: https://github.com .. _`bitbucket`: https://bitbucket.org .. _`gitorious`: https://gitorious.org/ .. _`djangopackages.com`: http://djangopackages.com .. _`PEP 345 about Obsolete-Dist`: http://www.python.org/dev/peps/pep-0345/#obsoletes-dist-multiple-use -- http://mail.python.org/mailman/listinfo/python-list