Skip to content

Implementation of the data model for v3.8#107

Merged
jeff5 merged 14 commits intojython:mainfrom
jeff5:datamodel-3.8
Aug 26, 2021
Merged

Implementation of the data model for v3.8#107
jeff5 merged 14 commits intojython:mainfrom
jeff5:datamodel-3.8

Conversation

@jeff5
Copy link
Copy Markdown
Member

@jeff5 jeff5 commented Jul 20, 2021

This is a proposed implementation for objects in the core of Jython 3. It is the model I talked about at the on-line Python Language Summit 2021, applied to the single Python type int. It is foundational for, but does not include, the use of dynamic call sites for performance.

It is draft as I'd like to push more onto it before it gets into the main repo. I thought it good to have it out there as a PR for those that want to follow along.

It is somewhat unsatisfactory as a PR because it includes a lot of code that has no purpose without parts I intend to leave to later PRs. I'll add more tests, I think, and maybe the exposure mechanism, but avoid the trap of piling everything in one massive PR.

jeff5 added 10 commits July 12, 2021 00:12
This change begins a shift to a "plain Java objects" approach to the
data model. We provide PyType and Operations classes, the marker
interfaces and a base Python object implementation. The elaborate Slots
enum describes a system of handles for special functions used by the
run-time. Other classes, although numerous, are present to support these
and errors they raise.

There is a unit test that exercises creation and lookup of each broad
sub-class of Operations object.
Move these Jython 2 source files unmodified to the Jython 3 source tree,
preparatory to conversion for Jython 3. (Accept the build fails.)

As a rule, each committed state should build without error. As an
exception, we commit pure file moves from 2 to 3 to provide an
unambiguous pedigree to git (and code archaeologists).
The changes to convert PyLong from 2 to 3 are large. Formatting code in
stringlib can be re-used with relatively minor changes. This change set
also brings in new classes for exceptions, and stubs for certain core
classes to satisfy dependencies.

We preserve legacy annotations in PyLong as comments so that the git
diff can align the old and new implementations of corresponding methods.
We will use Python to generate some of our code, and it has to be a 3.8.
We adjust the Travis CI build to support that, and insert a gratuitous
use of Python as a test.
bool provides a simple case of inheritance but it is rather a special
one since Java Boolean is its canonical implementation. We extend the
Operations formation test to bool and int.
Allow code to be wide, but keep comments at a readable width. This aids
comparison with the VSJ original, while we need it.
We provide an exposure mechanism sufficient to populate the dictionary
of the type object for any built-in, and from that the slots of
Operations objects. We apply this to int and bool.

We test that we can invoke a few unary special methods for int and bool.
The refactoring extracts common code between the unary and binary tests
(and later ones to be developed). The testing is at the level of
descriptors: we are not yet caching these as method handles in the
Operations object, although much of the code is present.
This change adds the code to Slot.java that causes the slots of
Operations objects to be filled with handles to implementation methods.
The unary and binary slot wrapper tests are extended to invoke these
slots and test the result against the same criteria as for the
corresponding descriptor.
This change imports Java code generation in Python and applies it to the
repetitive task of creating a version of each PyLong method specialised
for the class 'self'. Implementations inherited from Jython 2, still
present for now, are ignored where a class-specific duplicate exists.
jeff5 added 4 commits August 1, 2021 12:28
These are evolved versions of the Jython 2 code, moved to PyLongMethods,
and called from generated wrappers. These operations are not simply
invocations of the Java counterparts but quite complex, so we add a
specific test.
Slots are added to Slot and OPerations based on CPython slots and how
Jython works. Many of these are unused, but where they match methods in
the ported PyLong, the signature will be validated. Since documentation
is a focus, this change also fixes the javadoc so it builds cleanly.
Many of the special methods were already in the generated PyLongMethods,
but now we remove them absolutely from PyLong. We add tests that call a
few of them, to prove this works.

We implement __(r)pow__ and the __*shift__ methods as hand-crafted
additions to PyLongMethods, rather than generating them, as they are
irregular.
We reproduce on the new code the style of copyright notice found
elsewhere in Jython, with the addition of "Licensed to PSF ...",
requested in the  contributor agreement we all signed.

The copyright label is probably not necessary, but we follow precedent.
@jeff5
Copy link
Copy Markdown
Member Author

jeff5 commented Aug 5, 2021

I think this is ready for inspection now.

Obviously it is nowhere near a Python interpreter, but only illustrates an aspect of the proposed new implementation, namely how we would invoke special methods (slots) on built-in types. This, however, is fundamental.

And not that easy to follow, I should think, so here is a little code-walk of the idea.

The best way to appreciate what's going on may be to run in your IDE the unit test AbstractAPITest, then single step one of its cases under the debugger, let's say the test of Abstract.repr. Now, you should reach a breakpoint you set in PyLong.repr. You can maybe see how that got called.

For a more complicated case, consider debugging the test of PyNumber.negative in AbstractNumberAPITest that tests __neg__. You would have to set breakpoints in one of the implementations of PyLongMethods.__neg__ in the generated code. (There is no source for that until you run the build.) How does Jython select the signature matching either Integer or BigInteger arguments?

The action depends on Jython finding MethodHandles for the implementation methods in structures equivalent to the CPython type object, so before that can work, we must populate those slots from the object implementations. This code finds the implementations:
https://github.com/jeff5/jython/blob/8952c3e9afb375357157257ad48bcd1035fdd02b/core/src/main/java/org/python/core/TypeExposer.java#L84-L95
and this code creates descriptors in the dictionary of the type (here int):
https://github.com/jeff5/jython/blob/8952c3e9afb375357157257ad48bcd1035fdd02b/core/src/main/java/org/python/core/TypeExposer.java#L66-L74.
Back in the type object itself, the slots are filled here:
https://github.com/jeff5/jython/blob/8952c3e9afb375357157257ad48bcd1035fdd02b/core/src/main/java/org/python/core/PyType.java#L586-L591

@jeff5 jeff5 marked this pull request as ready for review August 5, 2021 22:04
// Same configuiration all sub-projects
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
options.debug = true
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps you want to set --release 11 too?

--release release

Compiles against the public, supported and documented API for a specific VM version. Supported release targets are 6, 7, 8, 9, 10, and 11.

Note:

When using --release for a version of the Java Platform that supports modules, you can’t use --add-modules to access internal JDK modules, nor can you use --add-exports to access internal JDK APIs in the modules.

https://docs.oracle.com/en/java/javase/11/tools/javac.html#GUID-AEEC9F07-CB49-4E96-8BC7-BCC2C7F725C9

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems a useful discipline to accept. The magic is:

    tasks.withType(JavaCompile) {
        // Use only public API
        options.compilerArgs.addAll(['--release', '11'])
        ...
    }

I'll slip it into main later.

@jeff5
Copy link
Copy Markdown
Member Author

jeff5 commented Aug 26, 2021

Everyone seems content with this, so I'll rebase-merge, in this case keeping the commits as distinct, rather than squash. I expect this to "fast-forward", but the big green button always feels like a leap into the unknown.

(I intend to take @thetric's suggestion as a separate change.)

@jeff5 jeff5 merged commit aa95cf5 into jython:main Aug 26, 2021
@jeff5
Copy link
Copy Markdown
Member Author

jeff5 commented Aug 26, 2021

Process note: GitHub has not created a needless merge commit, but it did needlessly reproduce all the commits as new objects. :( Harmless in the long run, but I believe I could have avoided this at the prompt.

@jeff5 jeff5 deleted the datamodel-3.8 branch December 30, 2021 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants