diff --git a/InternalDocs/README.md b/InternalDocs/README.md new file mode 100644 index 000000000..292742d5c --- /dev/null +++ b/InternalDocs/README.md @@ -0,0 +1,47 @@ +# Jython Internals Documentation + +The documentation in this folder is intended for Jython maintainers +and contributors. +It describes implementation details of Jython, which should not be +assumed to be part of the Python language specification +or API. + +These details can change between any two Jython versions. +Some of this material (for Jython 2) +was recovered from the Jython wiki in an archived state, +in which it has lost its contribution history. +Thanks however are mainly due to Jython Developers active before 2013. + + + + +## General Resources + +- [Source Code Structure](structure.md) + + +## Compiling Python Source Code + + + + +## Runtime Objects + + + + + +## Program Execution + + + + +## Built-in Types + +- [Defining Python Types in Java](python_types_in_java.md) +- [Python Subclasses of Built-in Types](generated_derived_classes.md) + + +## Modules + + diff --git a/InternalDocs/generated_derived_classes.md b/InternalDocs/generated_derived_classes.md new file mode 100644 index 000000000..3cce396b4 --- /dev/null +++ b/InternalDocs/generated_derived_classes.md @@ -0,0 +1,286 @@ +# Generating the `*Derived` classes + +These are notes on the use of `gderived.py`, +a tool you need when implementing new types in Jython, +so that they may have Python subclasses. + + +## Background + +Many of the Java classes that implement Python types +have a counterpart class with the same name but with "Derived" appended. +For example `PyString` is paired with `PyStringDerived`, +`PyType` with `PyTypeDerived`, and so on. +The `*Derived` classes are each a subclass of their corresponding principal class. +They come into play when you create a subclass (in Python) +and override (in Python) one or more methods +whose base definition is exposed from the Java implementation. +They ensure that this overriding (Python) method is the version invoked, +even when the call is from Java. + +There are two parts to this remarkable feature. +One is in the implementation of the principal class itself, +where the static exposed new method checks to see +whether the actual (Python) type of the object being created +is exactly the type that the principal class implements. +If it is, then a new instance of the (Java) class is returned. +If it is not exactly that class, +an instance of the counterpart `*Derived` class is returned. + +The second part of the feature is in the `*Derived` class. +There, each exposed method may be overridden in a stylised way: +it will check for the existence of a (Python) method +redefining (the exposed name of) that method. +If it fails to find one, +it calls the version in the principal class (using the super keyword in Java). +If it finds a Python re-definition, +it invokes that using `PyObject.__call__()`. + +The `*Derived` counterpart of each principal class is generated +using the script `gderived.py` and a brief specification. +The script is in the `src/templates` directory, +together with several modules it imports, +and it has to be run with that as the current directory. +The specification corresponding to each principal class +is in the same directory. + +One of the imported modules is `gexposed.py`. +This used to have a function in its own right, +but it is superseded by the exposer (`org.python.expose.generate.Exposer`) +and the corresponding Ant task. +If you use the new exposer, +even if you prohibit subclassing with `@ExposedType(isBaseType=false)`, +it will generate a reference to the sort of class `gderived.py` creates. +The modern exposer is described in the article +[Python Types in Java](python_types_in_java.md). + + +### Author's note: + +At the time of starting these notes, +there is no user guide to `gderived.py` and what it achieves. +These notes stem from use of the tool and a certain amount of reverse-engineering. +Please improve on them by correcting misunderstandings and omissions. + +The work was done on a Windows 7 system, +using Python 2.7 (without trying later versions, +because of the vintage of the code). +The choice of OS shows sometimes in the direction of slashes in pathnames, +but that shouldn't confuse anyone. +Although the motivation was to add a serious Python type (`bytearray`) to Jython, +illustrations will be drawn from a facetiously-named type (`piranha`), +with a Java implementation in `src/org/python/ethel/the/frog/Piranha.java`. + + +## `gderived.py` as a Command + +### The 2-argument Forms + +The most transparent form of the command is: +``` +python [--lazy] gderived.py +``` +When using `gderived.py` in that way one is working with three user files: + +``, the specification for the contents of the derived Java class. +By convention, this has the extension `.derived`, +and the Python name of the type being defined. +The files for Jython types are in the `src/templates` folder +along with the scripts, +but anywhere seems to work with this form of the command, +so we'll use `src/org/python/ethel/the/frog/piranha.derived` +(note lower case type name `piranha`). + +``, the file in which the generated class will be written. +This has to be in the Jython source tree under `src/org/python`. +If your code is not there, `gderived.py` seems to run correctly, +but will get the Java package statement wrong. +You can supply any filename you like, +but the class it writes will be named by adding "Derived" +to the name of the input class. +For our example the output file is +`src/org/python/ethel/the/frog/PiranhaDerived.java`. + +And last but not least, the class file that implements your type. +The input file is identified from the directory of the output file +and the class name given in the text of `` (see below). +This therefore also has to be in the Jython source tree under `src/org/python`. +For our example the input file is `src/org/python/ethel/the/frog/Piranha.java`. + +The `--lazy` option causes `gderived.py` only to generate the output file +if the input file is newer. + +### The 1-argument and 0-argument Forms + +A second form of the command is: +``` +python gderived.py [--lazy] [] +``` +When using `gderived.py` in that way +one is working with the same three user files as above, +and a configuration file `src/templates/mappings`. +The entries in that file look like this: +``` +int.derived:org.python.core.PyIntegerDerived +object.derived:org.python.core.PyObjectDerived +random.derived:org.python.modules.random.PyRandomDerived +ast_Assert.derived:org.python.antlr.ast.AssertDerived +``` +In effect, this file allows `gderived.py` to look up the second argument +given the first, +although this second argument is now given in dotted notation. +In this form, the specification file `.derived` +has to be in `src/templates` and the input and output classes +will be found relative to src. + +Finally, the `` argument is optional. +In the zero-argument form, +`gderived.py` will process all the entries in `src/templates/mappings`. +It is essentially this form, +with the `--lazy` option, +that implements the template Ant target in `build.xml`. + + +## The Specification file .derived + +### Available Directives + +`base_class` +Define the name of the input class. +**Do not qualify the class name with the package**: +the script will work it out from the output file path, relative to `src`. +E.g. `base_class: Piranha` + +`want_dict` Request creation of a dictionary in the derived class. +If not specified, only a slots array is created. + +`require` + + +`define` + + +`ctr` arguments to the constructor, after the subtype argument. +For example, in `_json.Scanner.derived` we have `ctr: PyObject context`, +and this leads to the constructor +`ScannerDerived(PyType subtype, PyObject context)` +which calls `super(subtype, context)`. + +`incl` include all the methods from this base class, + +`unary1` + +`binary` + +`ibinary` + +`no_toString`: `[true|false]` +If the parameter is `false` or the directive is not present, +generated class will be given a custom `toString()` method that invokes +`__repr__`. +If `true`, or the directive is given without a parameter, +the generated class will not be given a custom `toString()` method. +Use this when you already have a satisfactory one in the base. + +`rest`: The rest of the file is Java code to insert (pretty much verbatim) +into the derived class. +Use this to provide your own custom overriding methods. + + +### Related Templates in gderived-defs + +TBD + + +## Examples of Use + +### Minimal Case + +#### Input `Piranha.java` + +Here is an example of a type defined in Java for access as a built-in in Jython. +For information on the annotations and structure see PythonTypesInJava. +``` +package org.python.ethel.the.frog; + +import org.python.core.PyObject; +import org.python.core.PyString; +import org.python.core.PyType; +import org.python.expose.ExposedMethod; +import org.python.expose.ExposedNew; +import org.python.expose.ExposedType; + +@ExposedType(name="piranha") +public class Piranha extends PyObject { + + public static final PyType TYPE = PyType.fromClass(Piranha.class); + + public Piranha() { this(TYPE); } + + public Piranha(PyType subType) { super(subType); } + + @ExposedNew + final void newPiranha(PyObject[] args, String[] keywords) {} + + @ExposedMethod(names={"theOperation", "theOtherOperation"}) + public int operation(int payment) { return payment*2; } +} +``` + +#### Specification `piranha.derived` +``` +base_class: Piranha +``` + +#### Output `PiranhaDerived.java` + +The command +``` +python gderived.py piranha.derived ../org/python/ethel/the/frog/PiranhaDerived.java +``` +issued with current directory src/templates produces: +``` +/* Generated file, do not modify. See jython/src/templates/gderived.py. */ +package org.python.ethel.the.frog; + +import java.io.Serializable; +import org.python.core.*; + +public class PiranhaDerived extends Piranha implements Slotted { + + public PyObject getSlot(int index) { + return slots[index]; + } + + public void setSlot(int index,PyObject value) { + slots[index]=value; + } + + private PyObject[]slots; + + public String toString() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__repr__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (!(res instanceof PyString)) + throw Py.TypeError( + "__repr__ returned non-string (type "+ + res.getType().fastGetName()+")"); + return((PyString)res).toString(); + } + return super.toString(); + } + +} +``` + + \ No newline at end of file diff --git a/InternalDocs/images/jy_logo_large_bw.png b/InternalDocs/images/jy_logo_large_bw.png new file mode 100644 index 000000000..b141f4ad4 Binary files /dev/null and b/InternalDocs/images/jy_logo_large_bw.png differ diff --git a/InternalDocs/images/jy_logo_large_c.png b/InternalDocs/images/jy_logo_large_c.png new file mode 100644 index 000000000..4a705feb6 Binary files /dev/null and b/InternalDocs/images/jy_logo_large_c.png differ diff --git a/InternalDocs/images/jy_logo_medium_bw.png b/InternalDocs/images/jy_logo_medium_bw.png new file mode 100644 index 000000000..9247c0d2f Binary files /dev/null and b/InternalDocs/images/jy_logo_medium_bw.png differ diff --git a/InternalDocs/images/jy_logo_medium_c.png b/InternalDocs/images/jy_logo_medium_c.png new file mode 100644 index 000000000..43058009f Binary files /dev/null and b/InternalDocs/images/jy_logo_medium_c.png differ diff --git a/InternalDocs/images/jy_logo_small_bw.png b/InternalDocs/images/jy_logo_small_bw.png new file mode 100644 index 000000000..5037ce9e9 Binary files /dev/null and b/InternalDocs/images/jy_logo_small_bw.png differ diff --git a/InternalDocs/images/jy_logo_small_c.png b/InternalDocs/images/jy_logo_small_c.png new file mode 100644 index 000000000..e65a7f18e Binary files /dev/null and b/InternalDocs/images/jy_logo_small_c.png differ diff --git a/InternalDocs/logo.md b/InternalDocs/logo.md new file mode 100644 index 000000000..d108da092 --- /dev/null +++ b/InternalDocs/logo.md @@ -0,0 +1,37 @@ +# Jython Logo + +Logos displayed on the Jython Wiki were linked images +hosted on the website of (we assume) the originating artist, +[Jesse Ross](https://jesseross.com/) +specifically at https://jesseross.com/clients/jython/, +where it is accompanied by the statement: + +> This work is licensed under a +> [Creative Commons License](http://creativecommons.org/licenses/sa/1.0/)". + +(In spite of that, we treat "Jython" as a trademark of the PSF.) + +In the interests of making sure +we can always access these images when we need them, +they are copied here. + +## Large Format + +![Jython large logo in black and white](images/jy_logo_large_bw.png) + +![Jython large logo in colour](images/jy_logo_large_c.png) + +## Medium Format + +![Jython medium logo in black and white](images/jy_logo_medium_bw.png) + +![Jython medium logo in colour](images/jy_logo_medium_c.png) + + +## Small Format + +![Jython small logo in black and white](images/jy_logo_small_bw.png) + +![Jython small logo in colour](images/jy_logo_small_c.png) + + diff --git a/InternalDocs/python_types_in_java.md b/InternalDocs/python_types_in_java.md new file mode 100644 index 000000000..dc8f5a310 --- /dev/null +++ b/InternalDocs/python_types_in_java.md @@ -0,0 +1,263 @@ +# How to Expose Python Types from Java + +## Introduction + +Starting in Jython 2.5, +Python types are exposed from Java code by adding several different +annotations to fields and methods on the Java class +to define the fields and methods that will be visible +on the resulting Python type. +A bytecode processor then adds code to the compiled class +to build the type's dict and descriptors. +An Ant task, `org.python.expose.generate.ExposeTask`, +handles finding the classes to expose, running the processor on them, +and writing them back out. +This document describes how to use this type exposing system. + +## Adding `@ExposedType` and generating bytecode + +The first step in exposing a class is to use the `org.python.expose.ExposedType` +annotation on the actual class to indicate that it should be exposed. +This annotation goes on the class itself and has three optional fields. +The first, name, defines what the type will be called in Python. +If it isn't specified, +the type's name is just assumed to be the same as the Java class name. +The second field, base, indicates the base type this type extends. +It must be another Java class that's likewise +been annotated with `@ExposedType` and sets up the type hierarchy in Python. +If unspecified, +it's assumed that this type extends from the base newstyle Python type, object. +Unlike classes defined in Python code, +types defined in Java can only use single inheritance. +The third, `isBaseType`, +defines if this type can be subclassed from Python code. +When unspecified this value defaults to true. +Any type defined in CPython +that lacks a Py_TPFLAGS_BASETYPE bit in its tp_flags +should have `isBaseType` set to `false` in Jython. + +Once the class has `@ExposedType` on it, +it can be fed into the type bytecode processor +to fill in the additional bytecode necessary +to actually make the class work as a type. +The resulting type won't have any fields or methods defined on it, +but they can be added later. +To make the Ant task aware of it, +the newly exposed class needs to be added to `CoreExposed.includes` file +in the base of the jython checkout. +CoreExposed just contains a single line for each file to be exposed +with the full class name as a path in it, +ie `org/python/core/PyObject.class` for `PyObject`. +After adding the class to CoreExposed, +executing ant will compile the code and process the new bytecode. + +## Exposing methods with `@ExposedMethod` + +To actually add methods to the type, +they need to be exposed with `org.python.expose.ExposedMethod`. +`@ExposedMethod` can be applied to non-static methods on the class +that are public, protected or package protected. +When the bytecode processor encounters an `@ExposedMethod`, +it generates a new inner class extending `PyMethodDescriptor` +that simply calls the annotated method when bound. +Because it's being called from a method descriptor, +and Python method descriptors are available on types +regardless of their location in the inheritance hierarchy, +it's generally a good idea to make exposed methods final. +This ensures that subclasses won't override the behavior of the method, +so the descriptor always calls the same code +when accessed directly off of the type. +Also, exposed methods should avoid to call other virtual methods, +directly or indirectly, +unless the interactions with subclasses overriding the invoked methods +are clearly documented and understood. + +The generated descriptor maps the generic `PyObject.__call__` method +into the actual exposed method. +Like `__call__`, it will pass up to four arguments directly to the method. +If more than four arguments are required, +the method must take the generic `__call__` arguments, `PyObject[], String[]`. +For methods with four or fewer arguments, +the descriptor will also coerce its `PyObject` arguments into `String`s or +Java primitives if the method takes that type. +If the `PyObject` can't be coerced to that type, +a `TypeException` will be raised. +If the argument isn't a String or primitive, +it must be a regular `PyObject` and the method must do its own type checking. + +The return type from the method can be coerced in a similar fashion. +Returns of String and char will be wrapped in a `PyString`; +int, byte and short in `PyInteger`; +long in `PyLong`; +float and double in `PyFloat`; +and boolean in `PyBoolean`. +If the method returns void, `PyNone` will be returned in its place. +If it doesn't return any of these types, it must return `PyObject`. + +As with `@ExposeType`, +there are several fields on `@ExposedMethod` that control +how it appears in Python. +The names field operates similarly to the name field on `@ExposedType`, +except that it accepts multiple values +in case the method needs to appear under multiple names in the type. +For instance, the method `int_toString` on `PyInteger` is exposed with +`names = {"__repr__", "__str__"}` as it works as +both `__repr__` and `__str__` for int. +If names isn't specified, +the Java method's name is used to determine what the method will be exposed as. +If the name starts with the type name followed by an underscore, +the method will be exposed as the portion of its name following the underscore. +This allows `final`, package protected methods to be specified for types +that on `PyObject` as well without requiring setting the name on all of them. +For example, `PyInteger`'s exposed method `int___add__` doesn't set names, +so it appears in its type dict as `__add__`. +If the method name doesn't start with the type name and an underscore, +it's just exposed as the full method name. + +The next field, defaults, allows the method to specify that +some of its arguments are optional, +and if they aren't supplied, what values fill in. +If the Java method takes three arguments, +and two defaults are given in the annotation, +it can be called from Python with anywhere from one to three arguments. +If only one argument is given, +the first default is used for the second argument, +and the second default for the third argument. +If two arguments are given, +the second default is again used for the third argument. +If three arguments are given, the defaults are ignored. +If only a single default is given, it will only be used for the final argument. +As with argument coercion, +defaults can only be used with methods taking up to four arguments. +The defaults can take three types of value: + +`null` + the String null produces a Java null + +`Py.None` + the String `Py.None` produces the Python `None` value + +primitive + if the method takes a Java primitive + (`boolean`, `byte`, `short`, `char`, `int`, `long`, `float` or `double`) + in the default's position, the string will be parsed using + the primtive type's Java wrapper (e.g. `java.lang.Boolean` for `boolean`). + +The final field on `@ExposedMethod` is type. +This field takes one of three `MethodType` enums. +The aptly named default value, `DEFAULT`, +indicates that the method descriptor should simply call the exposed method +and return using the normal coercion directly. +`BINARY` indicates that the method is a binary operation +like `__add__` or `__sub__`. +For these types, the descriptor checks if the method returned null, +and if so, it raises a `NotImplemented` exception. +This is generally used in the case where the binary method +doesn't handle the passed in type. +The `CMP` type is only for `__cmp__` methods. +If used, it checks if the method returns -2, and if so, +raises a `TypeException`. +This is the same sort of type check that `BINARY` is doing with `null`. + + +## Exposing fields with `@ExposedGet`, `@ExposedSet` and `@ExposedDelete` + +A trio of annotations are used to expose a field on a type, +each handling a different aspect of accessing the field. +`@ExposedGet` takes care of read access. +It can be applied to a method that takes no arguments +and returns a value, or to a field. +If on a method, that method will be called every time read access is made +on that field on instances of the type. +If on a field, the descriptor will just directly access that field +on the instance and return it. +`@ExposedSet` can also be applied to a field or a method. +If used on a method, the method must take a single argument +of the same type as the `@ExposedGet` with the same name. +`@ExposedDelete` is only allowed on methods that return `void` +and take no arguments. +If specified, when del `typeinstance.fieldname` is invoked in Python, +that delete method will be called. +Neither `@ExposedDelete` nor `@ExposedSet` can be used +if an `@ExposedGet` of the same doesn't exist on the type. +The names of the exposed field can be specified as `name` in the annotation, +or if that isn't specified the name is taken directly +from the name of the field or method. + + +## Making the type instantiable with `@ExposedNew` + +The final step in making a Java class usable as a Python type +is to make the type instantiable by adding a `__new__` method to it. +This is done with the `@ExposedNew` annotation. +If there is no `@ExposedNew` in the class, +the type won't be instantiable from Python. +It can still be created from Java by calling its constructors directly. +See `org.python.core.PyNone` for an example of this. +However, in most cases, a type should be creatable from Python, +so it needs an `@ExposedNew`. +There are two ways it can be used, +a simple way for types that allow their subtypes to completely override +the `__new__` process, +and a more complicated version for types that need +to have their `__new__` invoked by every subclass. + +In the simple form, `@ExposedNew` is applied to an instance method +that takes the standard Jython call arguments, +`PyObject[] args, String[] keywords`. +In this form, the basic new functionality is handled by +`org.python.core.PyOverridableNew` +and the method annotated with `@ExposedNew` is called as `__init__` +as part of that process for instantiating the defined type directly. +This allows subtypes to completely redefine new +and create objects however they like. + +In the more complex form, `@ExposedNew` must be applied to a static method +that takes the arguments `PyNewWrapper new_, boolean init, PyType subtype, +PyObject[] args, String[] keywords`. +In this case, +the method has full responsibility for creating and initting the object +and will be invoked for every subtype of this exposed type. +Essentially it's for object instantation +that must be called for every instance of that object. +See `PyInteger.int_new` for an example of this type of `@ExposedNew`. + +With either form, there can be only one `@ExposedNew` per class. + + +## Post-processing + +Exposing Python types involves creating classes +that the class loader can find, +and then (as above) calling `PyType.fromClass()` on some class. +The classes get created by the `ExposedTypeProcessor` class +in `org.python.expose.generate`, +but as of this writing (r5593), +these classes are not included in the Jython jar. +It is intended that eventually `ExposedTypeProcessor` +and related classes will be shipped separately, +as a support jar. +Your `build.xml` should also have a clause that runs the `ExposeTask` +on the relevant classes, like this: +```xml + + + + + + +``` +```xml + +``` +(Taken from jython's `build.xml`.) +You probably want to make your non-preprocessed classes +(all the ones in `JythonExposed.includes`) +get compiled someplace else, +and only have the compiled ones show up in this "exposed" directory, +so that you don't have two versions of each class on your classpath, +and be careful if you're using an IDE. + +See also [Generated Derived Classes](generated_derived_classes.md). diff --git a/InternalDocs/structure.md b/InternalDocs/structure.md new file mode 100644 index 000000000..a237ba96b --- /dev/null +++ b/InternalDocs/structure.md @@ -0,0 +1,121 @@ +# Source Code Structure + +This section provides a view of the Jython code base +mostly from the perspective of the kind of object you might be looking for. + +## Java Vintage + +This is a single (i.e. monolithic) Java project +and so the bulk of the source is in package structure below `src/`, +or for unit tests in Java, below `tests/java`, +in the root of the project. +Jython pre-dates the introduction of module structure to Java (project Jigsaw). + +Note that Java source files may contain multiple classes. +(Some of this code also pre-dates nested classes.) + +All file paths here are relative to the project root. +We may use Java package names as the navigation +in place of file paths. +The implied file path is then relative to `src` or `test + +## Python Standard Library + +We re-use as much as possible of the Python Standard Library (the "stdlib"), +but in many cases we have to modify a library module for Jython. +The modified ones are in `Lib`, as in the CPython project. +There are also a good few modules, especially amongs the unit tests, +that only exist for Jython. + +Modules we re-use without modification are in `lib-python/2.7`. +These have been copied from CPython **and are never modified**. +They haven't all been copied from the same (patch) version of CPython. + +During the build, +the standard library is copied from `lib-python/2.7` and `Lib` to `dist/Lib`, +with identically-named files in `Lib` (the Jython-specific ones) +taking precedence. + + + +## Source Code Locations + +### Built-in types + +Built-in types, +meaning here those provided by Python and defined in Java, +are found in `src/org/python/core`. +For a built-in type, for example `float`, +the naming of classes and tests is: + +* Principal implementation: + [`src/org/python/core/PyFloat.java`](../src/org/python/core/PyFloat.java). +* [`src/templates/float.derived`](../src/templates/float.derived) specifies + `src/org/python/core/PyFloatDerived.java` + (see ([Generated Derived Classes](generated_derived_classes.md))). +* Tests: + [`python-lib/2.7/test/test_float.py`](../lib-python/2.7/test/test_float.py) + and [`Lib/test/test_float_jy.py`](../Lib/test/test_float_jy.py). + +However, for historical reasons and to maintain similarity with CPython, +the naming is not always so consistent as `float->PyFloat`, +for example `int -> PyInteger`. + + +### Built-in functions + +Build-in functions are implemented in a single source file (but many classes): +* Implementation: + [`src/org/python/core/__builtins__.java`](../src/org/python/core/__builtins__.java). +* Tests: + [`lib-python/2.7/test/test_builtin.py`](../lib-python/2.7/test/test_builtin.py) + and [`Lib/test/test_builtin_jy.py`](../Lib/test/test_builtin_jy.py). + + + + +### Built-in modules + +A Python module defined in Java is typically defined in +[`org.python.modules`](../src/org/python/modules). +For example, for the `math` module: + +* Implementation: + [`/src/org/python/modules/math.java`](../src/org/python/modules/math.java) +* Tests: [`Lib/test/test_math.py`](../Lib/test/test_math.py) + superseding the `lib-python` tests, + and [`Lib/test/test_math_jy.py`](../Lib/test/test_math_jy.py). + +A Python module defined in Python is typically defined in `Lib` +or `lib-python/2.7` when we are able to use the Python implementation directly. +There may be a supporting module implemented in Java in `org.python.modules` +that will be referenced from the Python with an underscore. +(The author of the Python module thinks it is in C, but never mind.) + +For example, for `codecs` module: + +* Implementation: + [`lib-python/2.7/codecs.py`](../lib-python/2.7/codecs.py). +* Supporting implementation in Java: + [`org.python.modules._codecs`](../src/org/python/modules/_codecs.java) +* Tests: [`Lib/test/test_codecs.py`](../Lib/test/test_codecs.py) + superseding the `lib-python` tests, + and [`Lib/test/test_codecs_jy.py`](../Lib/test/test_codecs_jy.py). + +Many useful modules are implemented in pure Python and +come from the `lib-python` tree. +Where they need compiled support or tweaks to accommodate Jython, +there is quite a lot of variety in the implementation. +The pattern shown for (`codecs`) is normative but in the minority. +A mapping of built-in module names to implementations is found in +[`org.python.modules.Setup`](../src/org/python/modules/Setup.java). + +For the documentation of modules, we rely almost entirely on +the widely-available module documentation published from +the CPython project. +Specifics of the Jython implementation of a module are +not (at the time of writing) comparably documented, +but a sensible choice would be to mirror CPython and use: + +* `Doc/library/.rst` +