Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
6212083
Begin multi-project structure natural to Gradle
jeff5 Jul 31, 2020
a6b94a7
Delete extlibs: an end to bundled JARs
jeff5 Jul 31, 2020
ed74ab1
Travis CI on JDKs 8, 11, 13 and add Windows to OSes
cclauss Jun 15, 2020
f3b0978
Improvements to Travis CI coverage and greenness
jeff5 Jul 30, 2020
26d21e6
Build (empty project) with Gradle on Travis CI
jeff5 Jul 31, 2020
5cce78e
Remove bugtests and some IDE files
jeff5 Aug 1, 2020
1569a2a
Emphasise 3.8 status in README and add link to 2.7
jeff5 Jan 18, 2021
6dd607f
Travis CI: Add comment linking to homebrew-openjdk (#88)
cclauss Mar 19, 2021
7ed1426
Foundation classes of the v3.8 data model
jeff5 Jul 11, 2021
af4fbdd
[Build fails] Move PyLong and parts of stringlib from 2 to 3
jeff5 Jul 14, 2021
4a26e5a
Get the re-used Jython 2 code to compile
jeff5 Jul 18, 2021
5333c0d
Support Python in the Gradle build
jeff5 Jul 20, 2021
4d44f98
Add the bool type and extend a test to it
jeff5 Jul 22, 2021
482e402
Formatting change to PyType
jeff5 Jul 22, 2021
457eab2
Expose special functions as slots in Operations
jeff5 Jul 22, 2021
bf47faf
Add a test of binary slot wrappers and refactor
jeff5 Jul 24, 2021
938d911
Begin to fill and use the op_* slots
jeff5 Jul 25, 2021
88203df
Begin to generate method specialisations in PyLongMethods
jeff5 Jul 25, 2021
42b4cd0
Implement int division and modulus
jeff5 Aug 1, 2021
1d7a057
Add all the special method slots and Javadoc them
jeff5 Aug 1, 2021
6122964
Rely on generated versions of the special methods (slots)
jeff5 Aug 5, 2021
aa95cf5
Copyright notices and some formatting
jeff5 Aug 5, 2021
d11547d
[Build fails] Remove stop-gap PyFloat
jeff5 Sep 4, 2021
809c1d5
[Build fails] Move Jython 2 PyFloat into core project
jeff5 Sep 4, 2021
f15cdda
Convert the implementation of float to Jython 3.8
jeff5 Sep 12, 2021
c331c8d
Floating-point floor-division and modulus conforms to Python 3
jeff5 Sep 15, 2021
35fd1b0
Switch to GitHub actions for unit test
jeff5 Oct 11, 2021
4d8b554
Implement Comparison enum and abstract API
jeff5 Nov 9, 2021
12edcb4
[Build fails] Remove stop-gap PyTuple
jeff5 Nov 10, 2021
2e068e9
[Build fails] Move Jython 2 PyTuple, PySequence and PySlice to core
jeff5 Nov 10, 2021
6011ff0
[Build fails] Preparatory work on sequence delegate and slices
jeff5 Nov 10, 2021
e02b795
[Build fails] Mark up PyTuple commenting out exposer annotations
jeff5 Nov 10, 2021
842aedc
Update PyTuple to Jython 3
jeff5 Nov 10, 2021
1f625e1
Implement slice for PyTuple and test
jeff5 Nov 18, 2021
0471501
[Build fails] Remove stop-gap PyUnicode
jeff5 Nov 21, 2021
d3fccaa
[Build fails] Move Jython 2 PyUnicode to new core
jeff5 Nov 21, 2021
309cacb
[Build fails] Mark-up exposed methods
jeff5 Nov 21, 2021
5099cb5
[Build fails] Move PyString code into PyUnicode
jeff5 Nov 27, 2021
38b824f
[Build fails] Re-write str.find, split, replace and similar
jeff5 Dec 14, 2021
fa90941
[Build fails] Many predicates and transformations in str
jeff5 Dec 23, 2021
ca0d3a9
[Build fails] Implement the strip methods in str and simplify
jeff5 Dec 24, 2021
87f9a73
[Build fails] Move Jython 2 format iterators to core
jeff5 Dec 30, 2021
5418dc4
Port __format__ support to Jython 3
jeff5 Dec 30, 2021
b110a0b
Make core:javadoc task run cleanly
jeff5 Dec 30, 2021
7c9cc0e
Factor out the int array builders from PyUnicode
jeff5 Jan 1, 2022
9ec6d47
Test that a Python str supports sequence operations
jeff5 Jan 1, 2022
0e32beb
Fix typos in comments and some formatting
jeff5 Jan 3, 2022
87ea2d5
Add test of our UTF-16 expectations
jeff5 Jan 3, 2022
18048f3
Merge PyUnicode for Jython 3 PR #147
jeff5 Jan 29, 2022
0ec6d5e
Simple Python examples and results
jeff5 Mar 28, 2022
4b60425
[Build fails] Remove stop-gap PyList
jeff5 Mar 29, 2022
258563c
[Build fails] Move legacy PyList to core project
jeff5 Mar 29, 2022
ce2c84c
[Build fails] Bulk changes to PyList method declarations
jeff5 Mar 30, 2022
5d9519c
Apply sequence delegate pattern to PyList
jeff5 Apr 3, 2022
a576d7a
Refactor PyList.sort
jeff5 Apr 8, 2022
6492c22
Additional exceptions and public types
jeff5 Apr 8, 2022
de55e18
Rudimentary implementation of bytes type
jeff5 Apr 9, 2022
fa0f2ca
Stop-gap implementation of dict type
jeff5 Apr 9, 2022
714bfce
Add framework classes for frame and code objects
jeff5 Apr 9, 2022
797725e
Implementations of code and frame for 3.8 byte code
jeff5 Apr 10, 2022
e0b852b
Implement a marshal module and test
jeff5 Apr 10, 2022
026336f
Demonstrate we can run some CPython byte code
jeff5 Apr 10, 2022
2efa2c5
Upgrade GitHub Actions on main branch (#167)
cclauss Apr 16, 2022
9f78e26
Merge branch 'main' into list-3.8
jeff5 May 3, 2022
ea7ebd3
Merge PyList implementation from PR #165
jeff5 May 3, 2022
ee7c68d
Merge PyList from 'main' into cpython-bytecode-3.8
jeff5 May 4, 2022
475cec8
Merge Cpython bytecode interpreter (PR #166)
jeff5 May 4, 2022
e5c27d7
Minor improvements in PyUnicode
jeff5 Aug 20, 2022
22b1093
Provide a parser for method argument lists
jeff5 Aug 20, 2022
64d3f0e
Enumerate methods in a built-in type
jeff5 Aug 21, 2022
98ee87e
Extend and refactor TypeExposerTest
jeff5 Aug 21, 2022
35b13c7
Implement method objects for built-in types
jeff5 Aug 21, 2022
90b30ba
Identifiers are interned java.lang.String by preference
jeff5 Aug 25, 2022
6d9882d
Implement calls on built-in from CPython byte code
jeff5 Aug 25, 2022
193631a
Implement Exposed.Member
jeff5 Sep 21, 2022
ff0a37a
Implement exposed get, set and delete
jeff5 Sep 23, 2022
d139bdd
Further test specification of Exposed.Member, Getter, etc.
jeff5 Sep 23, 2022
0ba2efe
Ensure that Java-inherited fields can be exposed
jeff5 Dec 16, 2022
b16dc42
Strengthen get-set descriptor exposure test
jeff5 Dec 16, 2022
eca847e
Explicitly choose Python 3.8 in build
jeff5 Dec 16, 2022
36f6aef
Merge branch 'main' into expose-bi-attr-3.8
jeff5 Dec 16, 2022
03cc1f1
Means expose attributes and members of built-in types PR #210
jeff5 Dec 17, 2022
48fbaba
Rename PyJavaMethod to PyJavaFunction
jeff5 Dec 30, 2022
e815693
Introduce the ModuleExposer and a test
jeff5 Dec 30, 2022
c4ba9fe
Test calling functions with a range of signatures
jeff5 Dec 31, 2022
27f79d7
PEP 489-ish extension module initialisation
jeff5 Jan 2, 2023
2b12cad
Begin a builtins module
jeff5 Jan 2, 2023
7a167a9
Show access to built-ins from byte code
jeff5 Jan 2, 2023
050bbf4
Implement simple conditional branching
jeff5 Jan 6, 2023
15b3f85
Disassemble Python examples used in tests
jeff5 Jan 6, 2023
e6c26d1
Implement indexed access opcodes
jeff5 Jan 6, 2023
09729ba
Implement comparison opcode
jeff5 Jan 7, 2023
9d11bd4
Expose functions from modules #227
jeff5 Feb 4, 2023
953bf0d
Merge branch 'main' into flow-control-opcodes-3.8
jeff5 Feb 4, 2023
8be597d
Merge flow control #229
jeff5 Feb 4, 2023
c9e2bc6
Update Gradle wrapper to 7.6
jeff5 Jun 4, 2023
7eaddca
Minimum Java version is 17
jeff5 Jun 4, 2023
75aef11
Use JUnit 5.10
jeff5 Jun 4, 2023
4efc90a
Rename CPython-specific classes for 3.11
jeff5 Jun 7, 2023
22f728e
Update classes needed for execution to 3.11
jeff5 Jun 8, 2023
c9da398
Introduce the 3.11 opcodes and implement some
jeff5 Jun 8, 2023
e9eeb64
Implement flow control opcodes
jeff5 Jun 8, 2023
a9a4942
Update workflow Java wrapper and permissions
jeff5 Jul 15, 2023
7e57b5c
Make java_object_gen.py friendly to use via API. (#294)
Stewori Dec 25, 2023
6449c88
Add APIdia badge for Jython 3 snapshot. (#358)
Stewori Sep 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Introduce the ModuleExposer and a test
Port the basic apparatus of modules defined in Java from VSJ and a test.
Rationalise ArgParser to eliminate a legacy constructor.
  • Loading branch information
jeff5 committed Dec 30, 2022
commit e81569384db18643a17fb9c548b7b73630efd0ab
409 changes: 184 additions & 225 deletions core/src/main/java/org/python/core/ArgParser.java

Large diffs are not rendered by default.

53 changes: 47 additions & 6 deletions core/src/main/java/org/python/core/Exposer.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.python.core.Exposed.PositionalOnly;
import org.python.core.Exposed.PythonMethod;
import org.python.core.Exposed.PythonStaticMethod;
import org.python.core.ModuleDef.MethodDef;

/**
* An object for tabulating the attributes of classes that define
Expand Down Expand Up @@ -67,6 +68,25 @@ protected Exposer() {
/** @return which {@link ScopeKind} of {@code Exposer} is this? */
abstract ScopeKind kind();

/**
* On behalf of the given module defined in Java, build a
* description of the attributes discovered by introspection of the
* class provided.
* <p>
* Attributes are identified by annotations. (See {@link Exposed}.)
*
* @param definingClass to introspect for members
* @return exposure result
* @throws InterpreterError on errors of definition
*/
static ModuleExposer exposeModule(Class<?> definingClass) throws InterpreterError {
// Create an instance of Exposer to hold specs, type, etc.
ModuleExposer exposer = new ModuleExposer();
// Let the exposer control the logic
exposer.expose(definingClass);
return exposer;
}

/**
* On behalf of the given type defined in Java, build a description
* of the attributes discovered by introspection of the class (or
Expand Down Expand Up @@ -617,14 +637,35 @@ private boolean isDefined() {
ArgParser getParser() {
if (parser == null && parameterNames != null
&& parameterNames.length >= posonlyargcount) {
parser = new ArgParser(name, scopeKind, methodKind, varArgsIndex >= 0,
varKeywordsIndex >= 0, posonlyargcount, kwonlyargcount, parameterNames,
regargcount);
parser = new ArgParser(name, scopeKind, methodKind, parameterNames, regargcount,
posonlyargcount, kwonlyargcount, varArgsIndex >= 0, varKeywordsIndex >= 0);
parser.defaults(defaults).kwdefaults(kwdefaults);
}
return parser;
}

/**
* Produce a method definition from this specification that
* references a method handle on the (single) defining method and
* the parser created from this specification. This is used in the
* construction of a module defined in Java (a {@link ModuleDef}).
*
* @param lookup authorisation to access methods
* @return corresponding method definition
* @throws InterpreterError on lookup prohibited
*/
MethodDef getMethodDef(Lookup lookup) throws InterpreterError {
assert methods.size() == 1;
Method m = methods.get(0);
MethodHandle mh;
try {
mh = lookup.unreflect(m);
} catch (IllegalAccessException e) {
throw cannotGetHandle(m, e);
}
return new MethodDef(getParser(), mh);
}

/**
* Add a method implementation. (A test that the signature is
* acceptable follows when we construct the {@link PyMethodDescr}.)
Expand Down Expand Up @@ -1126,9 +1167,9 @@ static class MethodSpec extends CallableSpec {
@Override
PyMethodDescr asAttribute(PyType objclass, Lookup lookup) throws InterpreterError {

ArgParser ap = new ArgParser(name, scopeKind, MethodKind.INSTANCE, varArgsIndex >= 0,
varKeywordsIndex >= 0, posonlyargcount, kwonlyargcount, parameterNames,
regargcount);
ArgParser ap = new ArgParser(name, scopeKind, MethodKind.INSTANCE, parameterNames,
regargcount, posonlyargcount, kwonlyargcount, varArgsIndex >= 0,
varKeywordsIndex >= 0);
ap.defaults(defaults).kwdefaults(kwdefaults);

// Methods have self + this many args:
Expand Down
23 changes: 23 additions & 0 deletions core/src/main/java/org/python/core/JavaModule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c)2022 Jython Developers.
// Licensed to PSF under a contributor agreement.
package org.python.core;

/** Common mechanisms for all Python modules defined in Java. */
public abstract class JavaModule extends PyModule {

final ModuleDef definition;

/**
* Construct the base {@code JavaModule} and fill the module
* dictionary from the given module definition, which is normally
* created during static initialisation of the concrete class
* defining the module.
*
* @param definition of the module
*/
protected JavaModule(ModuleDef definition) {
super(definition.name);
this.definition = definition;
definition.addMembers(this);
}
}
157 changes: 157 additions & 0 deletions core/src/main/java/org/python/core/ModuleDef.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// Copyright (c)2022 Jython Developers.
// Licensed to PSF under a contributor agreement.
package org.python.core;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles.Lookup;

/**
* A {@code ModuleDef} is a definition from which instances of a module
* may be made. It stands in relation to the Java classes that define
* Python modules, somewhat in the way a Python {@code type} object
* stands in relation to the Java classes that define Python objects.
* <p>
* What we most often encounter as "a module", a Python source file, is
* actually just a definition from which a module object may be made.
* This happens <i>once in each interpreter</i> where the module is
* imported. A distinct object, with mutable state, represents that
* module in each interpreter. There must therefore be a factory object
* that has access to the definition of the module, but is able to
* instantiate it (equivalent to executing the body of a module defined
* in Python). A {@code ModuleDef} is that factory.
* <p>
* This initialisation cannot be identified with the static
* initialisation of the Java class, since that cannot be repeated, but
* must happen per instance. It is useful, however, to have an
* intermediate cache of the results of processing the defining Java
* class once statically initialised.
*/
public class ModuleDef {
// Compare CPython PyModuleDef

/** Name of the module. */
final String name;

/** The Java class defining instances of the module. */
final Class<?> definingClass;

/**
* Definitions for the members that appear in the dictionary of
* instances of the module named. Instances receive members by copy,
* by binding to the module instance (descriptors), or by reference
* (if immutable).
*/
private final MethodDef[] methods;

/**
* Create a definition for the module, largely by introspection on
* the class and by forming {@code MethodHandle}s on discovered
* attributes.
*
* @param name of the module (e.g. "sys" or "math")
* @param lookup authorises access to the defining class.
*/
ModuleDef(String name, Lookup lookup) {
this.name = name;
this.definingClass = lookup.lookupClass();
ModuleExposer exposer = Exposer.exposeModule(definingClass);
this.methods = exposer.getMethodDefs(lookup);
// XXX ... and for fields.
// XXX ... and for types defined in the module maybe? :o
}

/**
* Get the method definitions. This method is provided for test use
* only. It isn't safe as for public use.
*
* @return the method definitions
*/
MethodDef[] getMethods() { return methods; }

/**
* Add members defined here to the dictionary of a module instance.
*
* @param module to populate
*/
void addMembers(JavaModule module) {
PyDict d = module.dict;
for (MethodDef md : methods) {
// Create function by binding to the module
PyJavaFunction func = PyJavaFunction.fromParser(
md.argParser, md.handle, module, this.name);
d.put(md.argParser.name, func);
}
}

/**
* A {@code MethodDef} describes a built-in function or method as it
* is declared in a Java module. It holds an argument parser and a
* handle for calling the method.
* <p>
* Recall that a module definition may have multiple instances. The
* {@code MethodDef} represents the method between the definition of
* the module (exposure as a {@link ModuleDef}) and the creation of
* actual {@link JavaModule} instances.
* <p>
* When a method is declared in Java as an instance method of the
* module, the {@code MethodDef} that describes it discounts the
* {@code self} argument. The {@link PyJavaFunction} created from it
* binds the module instance that is its target, so that it is is
* correct for a call to that {@code PyJavaFunction}. This is
* consistent with CPython.
*/
// Compare CPython struct PyMethodDef
static class MethodDef {

/*
* The object here is only superficially similar to the CPython
* PyMethodDef: it is not used as a member of descriptors or
* methods; extension writers do not declare instances of them.
* Instead, we reify the argument information from the
* declaration in Java, and associated annotations. In CPython,
* this knowledge is present at run-time in the structure of the
* code generated by Argument Clinic, incompletely in the flags
* of the PyMethodDef, and textually in the signature that
* begins the documentation string. We do it by holding an
* ArgParser.
*/

/**
* An argument parser constructed with this {@code MethodDef}
* from the description of the signature. Full information on
* the signature is available from this structure, and it is
* available to parse the arguments to a standard
* {@code (Object[], String[])} call. (In simple sub-classes it
* is only used to generate error messages once simple checks
* fail.)
*/
final ArgParser argParser;

/**
* A handle to the implementation of the function or method.
* This is generated by reflecting the same object that
* {@link #argParser} describes.
*/
// CPython PyMethodDef: ml_meth
final MethodHandle handle;

/**
* Create a {@link MethodDef} of the given kind from the
* {@link ArgParser} provided.
*
* @param argParser parser defining the method
* @param meth method handle prepared by sub-class
*/
MethodDef(ArgParser argParser, MethodHandle meth) {
this.argParser = argParser;
assert meth != null;
this.handle = meth;
}

@Override
public String toString() {
return String.format("%s[%s]", getClass().getSimpleName(),
argParser);
}
}
}
88 changes: 88 additions & 0 deletions core/src/main/java/org/python/core/ModuleExposer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (c)2022 Jython Developers.
// Licensed to PSF under a contributor agreement.
package org.python.core;

import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Method;

import org.python.core.Exposed.PythonMethod;
import org.python.core.Exposed.PythonStaticMethod;
import org.python.base.InterpreterError;
import org.python.core.ModuleDef.MethodDef;

/**
* A {@code ModuleExposer} provides access to the attributes of a module
* defined in Java (a built-in or extension module). These are primarily
* the {@link MethodDef}s derived from annotated methods in the defining
* class. It is normally obtained by a call to
* {@link Exposer#exposeModule(Class)}.
*/
class ModuleExposer extends Exposer {

/**
* Construct the {@code ModuleExposer} instance for a particular
* module.
*/
ModuleExposer() {}

/**
* Build the result from the defining class.
*
* @param definingClass to scan for definitions
*/
void expose(Class<?> definingClass) {
// Scan the defining class for definitions
scanJavaMethods(definingClass);
// XXX ... and for fields.
// XXX ... and for types defined in the module maybe? :o
}

@Override
ScopeKind kind() { return ScopeKind.MODULE; }

/**
* From the methods discovered by introspection of the class, return
* an array of {@link MethodDef}s. This array will normally be part
* of a {@link ModuleDef} from which the dictionary of each instance
* of the module will be created.
*
* A {@link MethodDef} relies on {@code MethodHandle}, so a lookup
* object must be provided with the necessary access to the defining
* class.
*
* @param lookup authorisation to access methods
* @return method definitions
* @throws InterpreterError on lookup prohibited
*/
MethodDef[] getMethodDefs(Lookup lookup) throws InterpreterError {
MethodDef[] a = new MethodDef[methodSpecs.size()];
int i = 0;
for (CallableSpec ms : methodSpecs) {
a[i++] = ms.getMethodDef(lookup);
}
return a;
}

/**
* For a Python module defined in Java, add to {@link specs}, the
* methods found in the given defining class and annotated for
* exposure.
*
* @param definingClass to introspect for definitions
* @throws InterpreterError on duplicates or unsupported types
*/
@Override
void scanJavaMethods(Class<?> definingClass)
throws InterpreterError {

// Collect exposed functions (Java methods)
for (Method m : definingClass.getDeclaredMethods()) {
PythonMethod a =
m.getDeclaredAnnotation(PythonMethod.class);
if (a != null) { addMethodSpec(m, a); }
PythonStaticMethod sm =
m.getDeclaredAnnotation(PythonStaticMethod.class);
if (sm != null) { addStaticMethodSpec(m, sm); }
}
}
}
Loading