Permalink
Cannot retrieve contributors at this time
181 lines (146 sloc)
8.17 KB
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| PEP: 399 | |
| Title: Pure Python/C Accelerator Module Compatibility Requirements | |
| Version: $Revision: 88219 $ | |
| Last-Modified: $Date: 2011-01-27 13:47:00 -0800 (Thu, 27 Jan 2011) $ | |
| Author: Brett Cannon <brett@python.org> | |
| Status: Final | |
| Type: Informational | |
| Content-Type: text/x-rst | |
| Created: 04-Apr-2011 | |
| Python-Version: 3.3 | |
| Post-History: 04-Apr-2011, 12-Apr-2011, 17-Jul-2011, 15-Aug-2011, 01-Jan-2013 | |
| Abstract | |
| ======== | |
| The Python standard library under CPython contains various instances | |
| of modules implemented in both pure Python and C (either entirely or | |
| partially). This PEP requires that in these instances that the | |
| C code **must** pass the test suite used for the pure Python code | |
| so as to act as much as a drop-in replacement as reasonably possible | |
| (C- and VM-specific tests are exempt). It is also required that new | |
| C-based modules lacking a pure Python equivalent implementation get | |
| special permission to be added to the standard library. | |
| Rationale | |
| ========= | |
| Python has grown beyond the CPython virtual machine (VM). IronPython_, | |
| Jython_, and PyPy_ are all currently viable alternatives to the | |
| CPython VM. The VM ecosystem that has sprung up around the Python | |
| programming language has led to Python being used in many different | |
| areas where CPython cannot be used, e.g., Jython allowing Python to be | |
| used in Java applications. | |
| A problem all of the VMs other than CPython face is handling modules | |
| from the standard library that are implemented (to some extent) in C. | |
| Since other VMs do not typically support the entire `C API of CPython`_ | |
| they are unable to use the code used to create the module. Oftentimes | |
| this leads these other VMs to either re-implement the modules in pure | |
| Python or in the programming language used to implement the VM itself | |
| (e.g., in C# for IronPython). This duplication of effort between | |
| CPython, PyPy, Jython, and IronPython is extremely unfortunate as | |
| implementing a module **at least** in pure Python would help mitigate | |
| this duplicate effort. | |
| The purpose of this PEP is to minimize this duplicate effort by | |
| mandating that all new modules added to Python's standard library | |
| **must** have a pure Python implementation *unless* special dispensation | |
| is given. This makes sure that a module in the stdlib is available to | |
| all VMs and not just to CPython (pre-existing modules that do not meet | |
| this requirement are exempt, although there is nothing preventing | |
| someone from adding in a pure Python implementation retroactively). | |
| Re-implementing parts (or all) of a module in C (in the case | |
| of CPython) is still allowed for performance reasons, but any such | |
| accelerated code must pass the same test suite (sans VM- or C-specific | |
| tests) to verify semantics and prevent divergence. To accomplish this, | |
| the test suite for the module must have comprehensive coverage of the | |
| pure Python implementation before the acceleration code may be added. | |
| Details | |
| ======= | |
| Starting in Python 3.3, any modules added to the standard library must | |
| have a pure Python implementation. This rule can only be ignored if | |
| the Python development team grants a special exemption for the module. | |
| Typically the exemption will be granted only when a module wraps a | |
| specific C-based library (e.g., sqlite3_). In granting an exemption it | |
| will be recognized that the module will be considered exclusive to | |
| CPython and not part of Python's standard library that other VMs are | |
| expected to support. Usage of ``ctypes`` to provide an | |
| API for a C library will continue to be frowned upon as ``ctypes`` | |
| lacks compiler guarantees that C code typically relies upon to prevent | |
| certain errors from occurring (e.g., API changes). | |
| Even though a pure Python implementation is mandated by this PEP, it | |
| does not preclude the use of a companion acceleration module. If an | |
| acceleration module is provided it is to be named the same as the | |
| module it is accelerating with an underscore attached as a prefix, | |
| e.g., ``_warnings`` for ``warnings``. The common pattern to access | |
| the accelerated code from the pure Python implementation is to import | |
| it with an ``import *``, e.g., ``from _warnings import *``. This is | |
| typically done at the end of the module to allow it to overwrite | |
| specific Python objects with their accelerated equivalents. This kind | |
| of import can also be done before the end of the module when needed, | |
| e.g., an accelerated base class is provided but is then subclassed by | |
| Python code. This PEP does not mandate that pre-existing modules in | |
| the stdlib that lack a pure Python equivalent gain such a module. But | |
| if people do volunteer to provide and maintain a pure Python | |
| equivalent (e.g., the PyPy team volunteering their pure Python | |
| implementation of the ``csv`` module and maintaining it) then such | |
| code will be accepted. In those instances the C version is considered | |
| the reference implementation in terms of expected semantics. | |
| Any new accelerated code must act as a drop-in replacement as close | |
| to the pure Python implementation as reasonable. Technical details of | |
| the VM providing the accelerated code are allowed to differ as | |
| necessary, e.g., a class being a ``type`` when implemented in C. To | |
| verify that the Python and equivalent C code operate as similarly as | |
| possible, both code bases must be tested using the same tests which | |
| apply to the pure Python code (tests specific to the C code or any VM | |
| do not follow under this requirement). The test suite is expected to | |
| be extensive in order to verify expected semantics. | |
| Acting as a drop-in replacement also dictates that no public API be | |
| provided in accelerated code that does not exist in the pure Python | |
| code. Without this requirement people could accidentally come to rely | |
| on a detail in the accelerated code which is not made available to | |
| other VMs that use the pure Python implementation. To help verify | |
| that the contract of semantic equivalence is being met, a module must | |
| be tested both with and without its accelerated code as thoroughly as | |
| possible. | |
| As an example, to write tests which exercise both the pure Python and | |
| C accelerated versions of a module, a basic idiom can be followed:: | |
| from test.support import import_fresh_module | |
| import unittest | |
| c_heapq = import_fresh_module('heapq', fresh=['_heapq']) | |
| py_heapq = import_fresh_module('heapq', blocked=['_heapq']) | |
| class ExampleTest: | |
| def test_example(self): | |
| self.assertTrue(hasattr(self.module, 'heapify')) | |
| class PyExampleTest(ExampleTest, unittest.TestCase): | |
| module = py_heapq | |
| @unittest.skipUnless(c_heapq, 'requires the C _heapq module') | |
| class CExampleTest(ExampleTest, unittest.TestCase): | |
| module = c_heapq | |
| if __name__ == '__main__': | |
| unittest.main() | |
| The test module defines a base class (``ExampleTest``) with test methods | |
| that access the ``heapq`` module through a ``self.heapq`` class attribute, | |
| and two subclasses that set this attribute to either the Python or the C | |
| version of the module. Note that only the two subclasses inherit from | |
| ``unittest.TestCase`` -- this prevents the ``ExampleTest`` class from | |
| being detected as a ``TestCase`` subclass by ``unittest`` test discovery. | |
| A ``skipUnless`` decorator can be added to the class that tests the C code | |
| in order to have these tests skipped when the C module is not available. | |
| If this test were to provide extensive coverage for | |
| ``heapq.heappop()`` in the pure Python implementation then the | |
| accelerated C code would be allowed to be added to CPython's standard | |
| library. If it did not, then the test suite would need to be updated | |
| until proper coverage was provided before the accelerated C code | |
| could be added. | |
| To also help with compatibility, C code should use abstract APIs on | |
| objects to prevent accidental dependence on specific types. For | |
| instance, if a function accepts a sequence then the C code should | |
| default to using `PyObject_GetItem()` instead of something like | |
| `PyList_GetItem()`. C code is allowed to have a fast path if the | |
| proper `PyList_CheckExact()` is used, but otherwise APIs should work | |
| with any object that duck types to the proper interface instead of a | |
| specific type. | |
| Copyright | |
| ========= | |
| This document has been placed in the public domain. | |
| .. _IronPython: http://ironpython.net/ | |
| .. _Jython: http://www.jython.org/ | |
| .. _PyPy: http://pypy.org/ | |
| .. _C API of CPython: http://docs.python.org/py3k/c-api/index.html | |
| .. _sqlite3: http://docs.python.org/py3k/library/sqlite3.html |