diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
new file mode 100644
index 000000000..da604b693
--- /dev/null
+++ b/.github/workflows/unit-tests.yml
@@ -0,0 +1,44 @@
+# Run unit tests (GitHub action)
+
+name: unit tests
+
+on: [push, pull_request]
+
+permissions:
+ contents: read
+
+jobs:
+
+ unit-tests-Ubuntu-jdk-17:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - run: echo "Branch ${{ github.ref }} of repository ${{ github.repository }}."
+
+ - uses: actions/checkout@v3
+ - uses: gradle/wrapper-validation-action@v1
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+
+ - name: Set up Python 3.11
+ uses: actions/setup-python@v4
+ with:
+ # This has to match the language version we're targeting
+ python-version: '3.11'
+
+ - name: Unit test with Gradle
+ run: ./gradlew --no-daemon test
+
+ - name: Clean up Gradle cache
+ # Remove some files from the Gradle cache, so they aren't cached
+ # by GitHub Actions. Restoring these files from a GitHub Actions
+ # cache might cause problems for future builds.
+ #https://docs.github.com/en/actions/guides/building-and-testing-java-with-gradle#caching-dependencies
+ run: |
+ rm -f ~/.gradle/caches/modules-2/modules-2.lock
+ rm -f ~/.gradle/caches/modules-2/gc.properties
diff --git a/.gitignore b/.gitignore
index 636281998..a875f80a9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,43 +1,62 @@
+# .gitignore: things not to put under source control
+
+# Places and extensions for unmanaged tools and scraps
+/local/
+/scraps/
+/temp/
+*.sav*
+*.patch
+
+# Project specific structure
+**/bin/
+**/venv/
+
+# Generated during build
+**/build/
+**/_build/
+**/_static/
+**/_templates/
+
+# Droppings of various tools, not for the record
+**/.gradle/
+**/__pycache__/
+.AppleDouble
+.DS_Store
*.class
+*.log
*.pyc
*.pyd
*.pyo
*.orig
*.rej
*.swp
+*.tmp
\#*
*~
-.gradle
+#
+# IDE Files. (We don't check them in: make your own!)
+#
+
+# Eclipse
+.classpath
+.externalToolBuilders/
+.project
+.settings/
+.pydevproject
# IntelliJ files
*.eml
*.ipr
*.iml
*.iws
-.idea/*
+.idea/
-# Eclipse files
-.classpath
-.externalToolBuilders/*
-.project
-.pydevproject
-
-# Netbeans files
-nbproject
-nbbuild.xml
-
-.vagrant
-
-.AppleDouble
-.DS_Store
-.settings
-__pycache__
-bin
-
-# Jython specific
+#
+# Jython 2 specific: retire these when we can (or promote them to 3)
+#
+.hg*
ant.properties
-build
build2
cachedir
.jython_cache
@@ -49,3 +68,15 @@ $test_*
profile.txt
out
+
+# Stuff dropped by bug tests (just in case that ant target is used)
+bugtests/support_config.py
+bugtests/test392m.py
+bugtests/*.err
+bugtests/*.out
+bugtests/*.zip
+bugtests/**/*.jar
+bugtests/test*jar
+bugtests/test*cache/
+bugtests/test*javapackage/
+
diff --git a/.hgignore b/.hgignore
deleted file mode 100644
index d02b1ae6c..000000000
--- a/.hgignore
+++ /dev/null
@@ -1,45 +0,0 @@
-syntax: glob
-*.class
-*.pyc
-*.pyd
-*.pyo
-*.orig
-*.rej
-*.swp
-\#*
-*~
-
-.gradle
-
-# IntelliJ files
-*.eml
-*.ipr
-*.iml
-*.iws
-.idea/*
-
-# Eclipse files
-.classpath
-.externalToolBuilders/*
-.project
-.pydevproject
-
-.AppleDouble
-.DS_Store
-.settings
-__pycache__
-bin
-
-# Jython specific
-ant.properties
-build
-build2
-cachedir
-.jython_cache
-dist
-publications
-reports
-jffi*.dll
-
-profile.txt
-out
diff --git a/.hgsub b/.hgsub
deleted file mode 100644
index e69de29bb..000000000
diff --git a/.hgsubstate b/.hgsubstate
deleted file mode 100644
index e69de29bb..000000000
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 792a10bf2..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-language: java
-
-addons:
- hostname: jyshort
-
- homebrew: # only active on osx
- update: true
- packages:
- - ant
-
-
-matrix:
- include:
- - os: linux
- dist: xenial
- addons:
- apt:
- packages:
- - ant
- jdk: openjdk8
-
- - os: linux
- dist: xenial
- addons:
- apt:
- packages:
- - ant
- jdk: openjdk11
-
- - os: linux
- dist: xenial
- addons:
- apt:
- packages:
- - ant
- jdk: openjdk12
-
- - os: osx
- osx_image: xcode9.3 # Last supporting Java 8
- jdk: oraclejdk8
-
-
-install:
- - ant developer-build
-
-
-script:
- - ant regrtest-travis
-
-notifications:
- email:
- recipients:
- - jython-dev@lists.sourceforge.net
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index 36a45e9d4..0c01b30a1 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -409,25 +409,44 @@ def test_universal_newlines_communicate(self):
# Interpreter without universal newline support
self.assertEqual(stdout, "line1\nline2\rline3\r\nline4\r\nline5\nline6")
+ @unittest.skipIf(jython, "file descriptor limit not reached on Jython")
def test_no_leaking(self):
# Make sure we leak no resources
- if not hasattr(test_support, "is_resource_enabled") \
- or test_support.is_resource_enabled("subprocess") and not mswindows \
- and not jython:
+ if not mswindows:
max_handles = 1026 # too much for most UNIX systems
else:
- # Settle for 65 on jython: spawning jython processes takes a
- # long time
- max_handles = 65
- for i in range(max_handles):
- p = subprocess.Popen([sys.executable, "-c",
- "import sys;sys.stdout.write(sys.stdin.read())"],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- data = p.communicate("lime")[0]
- self.assertEqual(data, "lime")
-
+ max_handles = 2050 # too much for (at least some) Windows setups
+ handles = []
+ try:
+ for i in range(max_handles):
+ try:
+ handles.append(os.open(test_support.TESTFN,
+ os.O_WRONLY | os.O_CREAT))
+ except OSError as e:
+ if e.errno != errno.EMFILE:
+ raise
+ break
+ else:
+ self.skipTest("failed to reach the file descriptor limit "
+ "(tried %d)" % max_handles)
+ # Close a couple of them (should be enough for a subprocess)
+ for i in range(10):
+ os.close(handles.pop())
+ # Loop creating some subprocesses. If one of them leaks some fds,
+ # the next loop iteration will fail by reaching the max fd limit.
+ for i in range(15):
+ p = subprocess.Popen([sys.executable, "-c",
+ "import sys;"
+ "sys.stdout.write(sys.stdin.read())"],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ data = p.communicate(b"lime")[0]
+ self.assertEqual(data, b"lime")
+ finally:
+ for h in handles:
+ os.close(h)
+ test_support.unlink(test_support.TESTFN)
def test_list2cmdline(self):
self.assertEqual(subprocess.list2cmdline(['a b c', 'd', 'e']),
diff --git a/NEWS b/NEWS
index b615b07a7..e15177fef 100644
--- a/NEWS
+++ b/NEWS
@@ -11,13 +11,15 @@ For more details, three sources are available according to type:
Jython 2.7.3a1
Bugs fixed
+ - [ GH-35 ] Travis CI on JDKs 8, 11, 13 and add Windows to OSes
+ - [ GH-50 ] (First) migration from Mercurial corrupted project history
- [ GH-27 ] -Q new always fails
- [ 2892 ] Migrate from hg.python.org to GitHub
- [ GH-4 ] Swap from Mercurial to Git as our SCM tool
- [ GH-2 ] Transfer closed-fixed issues in NEWS from frozen-mirror
New Features
- - The project has moved its home to GitHub.
+ - The project has moved its home to GitHub (twice).
Jython 2.7.2
same as 2.7.2rc1
diff --git a/README.md b/README.md
index db4ca0cd5..21084ffbe 100644
--- a/README.md
+++ b/README.md
@@ -1,92 +1,62 @@
-# Jython: Python for the Java Platform
-[](https://search.maven.org/artifact/org.python/jython-standalone/)
-[](https://www.javadoc.io/doc/org.python/jython-standalone)
+[](https://apidia.net/java/Jython/3)
+
+# Jython 3: Python 3 for the Java Platform
This is the development repository of Jython,
the implementation of Python in Java.
-Only version 2.7 of Python can be supported at present
-(but watch this space for a 3.x version).
-Along with good (not perfect!) language
-and runtime compatibility with CPython 2.7,
-Jython 2.7 provides substantial support of the Python ecosystem.
-This includes built-in support of *pip/setuptools*
-(you can use `bin/pip` if the targets do not include `C` extensions)
-and a native launcher for Windows (`bin/jython.exe`)
-that works essentially as the `python` command.
+You are looking at the branch intended to support version 3.8 of Python:
+it doesn't build anything useful right now.
+Jython 3.x is not yet a viable product you can use in applications.
+Head over to [the 2.7 branch](https://github.com/jython/jython/tree/master)
+to find the current release.
+
+
+## Target
-Jim Baker presented a talk at PyCon 2015 about Jython 2.7,
-including demos of new features: https://www.youtube.com/watch?v=hLm3garVQFo
+Along with good language and runtime compatibility with CPython 3.8,
+Jython 3.8 is intended to provide substantial support of the Python ecosystem,
+and solid Java integration.
+
+For more about the target see the
+[Jython 3 MVP](https://www.jython.org/jython-3-mvp)
+page.
See [ACKNOWLEDGMENTS](ACKNOWLEDGMENTS) for details about Jython's copyright,
license, contributors, and mailing lists.
Consult [NEWS](NEWS) for detailed release notes, including bugs fixed,
backwards breaking changes, and new features.
-We sincerely thank all who contribute to Jython, by bug reports, patches,
+We are sincerely grateful to all who contribute to Jython, by bug reports, patches,
pull requests, documentation changes and e-mail discussions.
-## How to build Jython
-The project uses Git for version-control,
-and the master repository is at https://github.com/jython/jython,
-You should clone this repository to create a buildable copy of the latest state
-of the Jython source.
-The previously authoritative repository at https://hg.python.org/jython is not now in use,
-remaining frozen at v2.7.2.
+## Current focus of work
-### Build using `ant` for development
+The current focus is to establish a foundation for the run-time
+that makes good use of the dynamic language features of the JVM.
+There are just enough classes here to illustrate the architectural ideas
+underpinning the new foundation.
-Jython is normally built using `ant`.
-It is necessary to have Ant and at least a Java 8 SDK on the path.
-To build Jython in development, we generally use the command:
-```
-ant
-```
-This leaves an executable in `dist/bin`
-that you may run from the check-out root with:
-```
-dist/bin/jython
-```
-Other `ant` targets exist, notably `clean`, and `jar`.
+The code of the Jython 2 implementation is also present on this branch,
+waiting to be shifted onto the new foundations (or definitively dropped),
+file by file. It does not participate in the build.
-You can test your build of Jython (by running the regression tests),
-with the command:
-```
-dist/bin/jython -m test.regrtest -e -m regrtest_memo.txt
-```
-### Build an installer using `ant`
+## How to build Jython
-If you want to install a snapshot build of Jython, use the command:
-```
-ant installer
-```
-This will leave you with a snapshot installer JAR in `dist`,
-that you can run with:
-```
-java -jar jython-installer.jar
-```
-for the graphical installer, or:
+### Build using `Gradle` for development
+
+Jython may be built using `Gradle`.
```
-java -jar jython-installer.jar --console
+$ ./gradlew build
```
-For the console version. (A `--help` option gives you the full story.)
+In its present state, no executable is built, although there is a JAR,
+that in principle could be used in sample programs.
-### Build a JAR using Gradle
+Jython is normally built only to run the unit tests (the `core:test` target).
+The documentation built by the `core:javadoc` target may also be interesting.
+Running the unit tests in `core/src/test/java`,
+under a debugger in an IDE,
+is perhaps the best way to explore how the code works.
-Experimentally, we have a Gradle build that results in a family of JARs,
-and a POM.
-This is intended to provide the Jython core in a form that Gradle and Maven
-users can consume as a dependency.
-Invoke this with:
-```
-PS> .\gradlew publish
-```
-and a JAR and POM are delivered to ` .build2\repo`
-
-Whereas the JARs delivered by the installer are somewhat "fat",
-embedding certain dependencies in shaded (renamed) form,
-the JAR from the Gradle build is "spare"
-and cites its dependencies externally through a POM.
-The project would like to know if this is being done suitably
-for downstream use.
+Watch this space for further developments.
diff --git a/b/.idea/libraries/extlibs.xml b/b/.idea/libraries/extlibs.xml
deleted file mode 100644
index 2e5e50203..000000000
--- a/b/.idea/libraries/extlibs.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/b/.idea/libraries/jar.xml b/b/.idea/libraries/jar.xml
deleted file mode 100644
index 10beca9d8..000000000
--- a/b/.idea/libraries/jar.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/b/.idea/libraries/svnant_jars.xml b/b/.idea/libraries/svnant_jars.xml
deleted file mode 100644
index 36cddb560..000000000
--- a/b/.idea/libraries/svnant_jars.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/b/.idea/libraries/test.xml b/b/.idea/libraries/test.xml
deleted file mode 100644
index 926b5e97d..000000000
--- a/b/.idea/libraries/test.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/bugtests/README.txt b/bugtests/README.txt
deleted file mode 100644
index 7e86be369..000000000
--- a/bugtests/README.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-
-This directory contains small tests that attempt to ensure that old fixed
-bugs do not reappear.
-
-These tests are not actively maintained and while many pass, others have been
-broken by changes in the main codebase and now fail for what might be spurious
-reasons.
-
-As the regression tests have grown, the need for these has largely ceased.
-It is likely these tests will be removed eventually.
-
-
-Running
-=======
-
-Start a command prompt in this ("bugtests") directory. Make sure that
-the "classes" subdirectory is included in the CLASSPATH environment
-variable and that the "bugtests" directory is *not* included in in CLASSPATH.
-
-Create a file called "support_config.py" which contains the following three
-entries:
-
------
-java_home = ""
-jython_home = ""
-classpath = ""
------
-
-This is used to make the tests more platform independent. My file for OS X
-looks like:
-
------
-java_home = "/Library/Java/Home"
-jython_home = "/Users/bzimmer/Development/sourceforge/jython/dist"
-classpath = jython_home + "/jython-dev.jar:classes"
------
-
-Run the script "driver.py".
-
-After running the tests the applets should be tested by starting the
-appletviewer on all the *.html files. Both MS and javasoft's appletviewer
-should be tested.
-
-Adding new tests
-================
-
-The tests follow a strict naming scheme that ensure that we always know
-which files that belong to each test. The main script of each test is called
-"testNNN" where NNN is a 3-digit number. All other files that belong to this
-test also starts with testNNN. There are no exceptions to this rule!
-
-Dependent file normally follow a simple naming
-
-testNNNp a python package
-testNNNm a python module
-testNNNj a java class
-testNNNi a java interface
-testNNNc a python module meant for compilation with jythonc.
-testNNNa an applet, compiled with jythonc.
-testNNNs modules with deliberate syntax errors.
-
-The tests should always complete without throwing exceptions or errors. Since
-these tests also cover bugs which may not have been fixed yet, the test should
-instead throw a TestWarning exception. When the bug is fixed the TestWarning
-should be removed and replaced with a TestError instead.
-
-
-
-
diff --git a/bugtests/classes/test292j.java b/bugtests/classes/test292j.java
deleted file mode 100644
index 105864796..000000000
--- a/bugtests/classes/test292j.java
+++ /dev/null
@@ -1,18 +0,0 @@
-
-import org.python.core.*;
-
-public class test292j {
- public static void main(String[] args) {
- PySystemState.initialize();
- Py.getSystemState().path = new PyList();
- Py.getSystemState().path.append(new PyString("."));
-
- try {
- __builtin__.__import__("test292j1");
- } catch (PyException exc) {
- if (!exc.match(Py.ImportError))
- throw exc;
- }
- }
-}
-
diff --git a/bugtests/classes/test301p/A.java b/bugtests/classes/test301p/A.java
deleted file mode 100644
index 06d679fde..000000000
--- a/bugtests/classes/test301p/A.java
+++ /dev/null
@@ -1,7 +0,0 @@
-
-package test301p;
-
-public class A {
- public B b; //b never used
-}
-
diff --git a/bugtests/classes/test301p/B.java b/bugtests/classes/test301p/B.java
deleted file mode 100644
index db39fe600..000000000
--- a/bugtests/classes/test301p/B.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package test301p;
-
-public class B {
-
-}
-
diff --git a/bugtests/classes/test336p/Data.java b/bugtests/classes/test336p/Data.java
deleted file mode 100644
index 9892669f4..000000000
--- a/bugtests/classes/test336p/Data.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package test336p;
-
-public class Data { }
-
diff --git a/bugtests/classes/test336p/data/MyData.java b/bugtests/classes/test336p/data/MyData.java
deleted file mode 100644
index 9b0ef90f4..000000000
--- a/bugtests/classes/test336p/data/MyData.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package test336p.data;
-
-public class MyData { }
-
diff --git a/bugtests/driver.py b/bugtests/driver.py
deleted file mode 100644
index 8b7eacb68..000000000
--- a/bugtests/driver.py
+++ /dev/null
@@ -1,76 +0,0 @@
-
-import sys, string, traceback, getopt, support, os, glob
-
-failures = {}
-warnings = {}
-skipped = {}
-
-
-def runTests(seq):
- def report(msg, errors_dict, loud=1):
- print n, msg
- errors_dict[n] = 1
- if loud:
- if m and hasattr(m, "__doc__"):
- print m.__doc__.strip()
- print " ", sys.exc_info()[0]
- print " ", sys.exc_info()[1]
- traceback.print_tb(sys.exc_info()[2], file=sys.stdout)
-
- for n in seq:
- m = None
- try:
- stdout = sys.stdout
- if os.path.isfile(n + ".py"):
- m = __import__(n)
- sys.stdout = stdout
- print n, "OK"
- else:
- print n, "Skipped"
- except support.TestWarning:
- sys.stdout = stdout
- report("Warning", warnings, loud=loud_warnings)
- except support.TestSkip:
- report("Skipped", skipped, loud=0)
- except:
- sys.stdout = stdout
- report("Failed", failures)
-
- summarize(failures, "failures")
- summarize(warnings, "warnings")
- summarize(skipped, "skipped")
-
-def summarize(errors_dict, description):
- t = errors_dict.keys()
- t.sort()
- print "%d %s" % (len(t), description)
- print t
-
-if __name__ == '__main__':
- opts, args = getopt.getopt(sys.argv[1:], 'wc', 'runjythonc')
- loud_warnings = ('-w',"") in opts
- support.test_jythonc = ('--runjythonc', '') in opts
-
- if loud_warnings:
- print "LOUD warnings"
- if support.test_jythonc:
- print 'Running jythonc tests'
-
- sys.path[:0] = ['classes']
-
- if len(args) > 0:
- tests = [int(test) for test in args[0].split(',')]
- else:
- testfiles = glob.glob('test???.py')
- testfiles.sort()
- lastTest = testfiles[-1]
- tests = range(int(lastTest[4:7]) + 1)# upper bound: last test + 1
- runTests(["test%3.3d" % i for i in tests])
-
- if len(failures) + len(warnings) > 0:
- rc = 1
- else:
- rc = 0
-
- sys.exit(rc)
-
diff --git a/bugtests/jarmaker.py b/bugtests/jarmaker.py
deleted file mode 100644
index a6efdd689..000000000
--- a/bugtests/jarmaker.py
+++ /dev/null
@@ -1,24 +0,0 @@
-import support
-import sys
-import os
-
-from java.io import File
-
-package = "javapackage"
-clazz = "JavaClass"
-jardir = "simplejar"
-jarfn = "simple.jar"
-clazzfile = File(jardir + '/'+ package, "%s.class" % clazz) # java.io.File
-
-def mkjar():
- jarfile = File(jardir, jarfn)
- # create a .jar file containing a .class file
- if not jarfile.exists():
- support.compileJava("%s/%s/%s.java" % (jardir, package, clazz))
- jarPacker = support.JarPacker(jarfile, bufsize=128)
- jarPacker.addFile(clazzfile, parentDirName=package)
- jarPacker.close()
- return jardir + '/' + jarfn, package, clazz
-
-
-
diff --git a/bugtests/simplejar/javapackage/JavaClass.java b/bugtests/simplejar/javapackage/JavaClass.java
deleted file mode 100644
index c201b93d6..000000000
--- a/bugtests/simplejar/javapackage/JavaClass.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package javapackage;
-
-public class JavaClass {}
diff --git a/bugtests/stdtest.py b/bugtests/stdtest.py
deleted file mode 100644
index 8d223a30f..000000000
--- a/bugtests/stdtest.py
+++ /dev/null
@@ -1,183 +0,0 @@
-
-
-import test.regrtest
-
-import os, sys
-
-
-
-skipped = [
- 'test_al',
- 'test_asynchat',
- 'test_audioop',
- 'test_b1',
- 'test_b2',
- 'test_bastion',
- 'test_bsddb',
- 'test_capi',
- 'test_cd',
- 'test_cl',
- 'test_cmath',
- 'test_commands',
- 'test_crypt',
- 'test_curses',
- 'test_dbm',
- 'test_dl',
- 'test_email_codecs', # Needs asian codecs.
- 'test_fcntl',
- 'test_fork1',
- 'test_frozen',
- 'test_future1', # called as part of test_future
- 'test_future2', # called as part of test_future
- 'test_future3', # called as part of test_future
- 'test_gc',
- 'test_gettext',
- 'test_getargs', # Test a python bug, this throws a different exc in jy.
- 'test_gdbm',
- 'test_gl',
- 'test_grp',
- 'test_hotshot',
- 'test_imageop',
- 'test_imgfile',
- 'test_linuxaudiodev',
- 'test_locale',
- 'test_longexp',
- 'test_minidom',
- 'test_mmap',
- 'test_nis',
- 'test_openpty',
- 'test_parser',
- 'test_poll',
- 'test_pty',
- 'test_pwd',
- 'test_regex',
- 'test_rgbimg',
- 'test_rotor',
- 'test_sax',
- 'test_select',
- 'test_signal',
- 'test_socketserver',
- 'test_socket_ssl',
- 'test_strop',
- 'test_sundry',
- 'test_sunaudiodev',
- 'test_symtable',
- 'test_timing',
- 'test_unicodedata',
- 'test_wave',
- 'test_winreg',
- 'test_winsound',
-]
-
-failures = [
- 'test_array',
- 'test_binop',
- 'test_codeop',
- 'test_compare',
- 'test_cookie',
- 'test_cpickle',
- 'test_descr',
- 'test_descrtut',
- 'test_doctest2',
- 'test_email',
- 'test_extcall',
- 'test_fpformat',
- 'test_funcattrs',
- 'test_generators',
- 'test_getargs',
- 'test_hmac',
- 'test_inspect',
- 'test_iter',
- 'test_largefile',
- 'test_long',
- 'test_long_future',
- 'test_mailbox',
- 'test_marshal',
- 'test_mhlib',
- 'test_mutants',
- 'test_ntpath',
- 'test_os',
- 'test_operations',
- 'test_pickle',
- 'test_pkgimport',
- 'test_popen2',
- 'test_profile',
- 'test_profilehooks',
- 'test_pyclbr',
- 'test_pyexpat',
- 'test_repr',
- 'test_richcmp',
- 'test_scope',
- 'test_socket',
- 'test_struct',
- 'test_tempfile',
- 'test_threaded_import',
- 'test_threadedtempfile',
- 'test_trace',
- 'test_types',
- 'test_ucn',
- 'test_unary',
- 'test_unicode',
- 'test_unicode_file',
- 'test_urllib2',
- 'test_userlist',
- 'test_uu',
- 'test_weakref',
- 'test_zlib',
-]
-
-
-
-
-def usage():
- print "jython stdtest.py [options] [tests]"
- print " -h, --help : print this help"
- print " -v, --verbose : turn on verbosity"
- print " -s, --skipped : Run the tests that is normally skipped"
- print " -f, --failures : Run the tests that normally fails"
- print " -t, --test : Run the tests listed as arguments"
-
-def main():
- import getopt
- try:
- opts, args = getopt.getopt(sys.argv[1:], "hvsft:", [
- "help", "verbose", "skipped", "failures", "test="])
- except getopt.GetoptError:
- # print help information and exit:
- usage()
- sys.exit(2)
-
-
- alltests = [ f[:-3] for f in os.listdir("../dist/Lib/test")
- if f.startswith("test_") and f.endswith(".py") ]
- tests = [s for s in alltests if s not in failures and s not in skipped]
- verbose = 0
-
- for o, a in opts:
- if o in ("-h", "--help"):
- usage()
- sys.exit()
- if o in ("-v", "--verbose"):
- verbose = 1
- if o in ("-s", "--skipped"):
- tests = skipped
- if o in ("-f", "--failures"):
- tests = failures
- if o in ("-t", "--test"):
- tests = a.split(",")
-
- sys.argv = []
-
- if tests.count("test_largefile") > 0:
- tests.remove("test_largefile")
-
- test.regrtest.main(tests, verbose=verbose)
-
-
-if __name__ == "__main__":
- main()
-
-
-#test.regrtest.main(tests, verbose=0)
-#test.regrtest.main(skipped, verbose=0)
-test.regrtest.main(failures, verbose=0)
diff --git a/bugtests/support.py b/bugtests/support.py
deleted file mode 100644
index 04a2669c2..000000000
--- a/bugtests/support.py
+++ /dev/null
@@ -1,249 +0,0 @@
-import sys
-is_jython = sys.platform[:4] == "java"
-
-import re, exceptions, thread, os, shutil
-import support_config as cfg
-
-if is_jython:
- import jarray
- from java.io import FileInputStream
- from java.io import FileOutputStream
- from java.util.jar import JarEntry
- from java.util.jar import JarFile
- from java.util.jar import JarInputStream
- from java.util.jar import JarOutputStream
- from java.util.jar import Manifest
-
-UNIX = os.pathsep == ":"
-WIN = os.pathsep == ";"
-test_jythonc = 1
-
-if not UNIX ^ WIN:
- raise TestError("Unknown platform")
-
-class TestError(exceptions.Exception):
- def __init__(self, args):
- exceptions.Exception.__init__(self, args)
-
-class TestWarning(exceptions.Exception):
- def __init__(self, args):
- exceptions.Exception.__init__(self, args)
-
-class TestSkip(exceptions.Exception):
- def __init__(self, args):
- exceptions.Exception.__init__(self, args)
-
-def compare(s, pattern):
- m = re.search(pattern, str(s))
- if m is None:
- raise TestError("string compare error\n '" + str(s) + "'\n '" + pattern + "'")
-
-def StreamReader(instream, outstream):
- while 1:
- ch = instream.read()
- if ch == -1: break
- outstream.write(ch)
-
-def execCmd(cmd, kw):
- __doc__ = """execute a command, and wait for its results
-returns 0 if everything was ok
-raises a TestError if the command did not end normally"""
- if kw.has_key("verbose") and kw["verbose"]:
- print cmd
- import java
- r = java.lang.Runtime.getRuntime()
- e = getattr(r, "exec")
- p = e(cmd)
-
- if kw.has_key("output"):
- outstream = java.io.FileOutputStream(kw['output'])
- else:
- outstream = java.lang.System.out
- if kw.has_key("error"):
- errstream = java.io.FileOutputStream(kw['error'])
- else:
- errstream = java.lang.System.out
-
- thread.start_new_thread(StreamReader, (p.inputStream, outstream))
- thread.start_new_thread(StreamReader, (p.errorStream, errstream))
-
- ret = p.waitFor()
- if ret != 0 and not kw.has_key("expectError"):
- raise TestError, "%s failed with %d" % (cmd, ret)
-
- return ret
-
-def compileJava(src, **kw):
- classfile = src.replace('.java', '.class')
- if not 'force' in kw and os.path.exists(classfile) and os.stat(src).st_mtime < os.stat(classfile).st_mtime:
- return 0
- classpath = cfg.classpath
- if "classpath" in kw:
- classpath = os.pathsep.join([cfg.classpath, kw["classpath"]])
- if UNIX:
- cmd = "%s/bin/javac -classpath %s %s" % (cfg.java_home, classpath, src)
- elif WIN:
- src = src.replace("/", "\\")
- cmd = 'cmd /C "%s/bin/javac.exe" -classpath %s %s' % (cfg.java_home, classpath, src)
- return execCmd(cmd, kw)
-
-def runJava(cls, **kw):
- classpath = cfg.classpath
- if "classpath" in kw:
- classpath = os.pathsep.join([cfg.classpath, kw["classpath"]])
- if kw.get('pass_jython_home', 0):
- defs = "-Dpython.home=%s" % cfg.jython_home
- else:
- defs = ''
- if UNIX:
- cmd = ['/bin/sh', '-c', "%s/bin/java -classpath %s %s %s" % (cfg.java_home, classpath, defs, cls)]
- elif WIN:
- cmd = 'cmd /C "%s/bin/java.exe" -classpath %s %s %s' % (cfg.java_home, classpath, defs, cls)
- return execCmd(cmd, kw)
-
-def runJavaJar(jar, *args, **kw):
- argString = " ".join(args)
- if UNIX:
- cmd = ['/bin/sh', '-c', "%s/bin/java -jar %s %s" % (cfg.java_home, jar, argString)]
- elif WIN:
- cmd = 'cmd /C "%s/bin/java.exe" -jar %s %s' % (cfg.java_home, jar, argString)
- return execCmd(cmd, kw)
-
-def runJython(cls, **kw):
- javaargs = ''
- if 'javaargs' in kw:
- javaargs = kw['javaargs']
- classpath = cfg.classpath
- if "classpath" in kw:
- classpath = os.pathsep.join([cfg.classpath, kw["classpath"]])
- if UNIX:
- cmd = "%s/bin/java -classpath %s %s -Dpython.home=%s org.python.util.jython %s" % (cfg.java_home, classpath, javaargs, cfg.jython_home, cls)
- elif WIN:
- cmd = 'cmd /C "%s/bin/java.exe" -classpath %s %s -Dpython.home=%s org.python.util.jython %s' % (cfg.java_home, classpath, javaargs, cfg.jython_home, cls)
- return execCmd(cmd, kw)
-
-def compileJPythonc(*files, **kw):
- if not test_jythonc:
- raise TestSkip('Skipping pythonc')
- if os.path.isdir("jpywork") and not kw.has_key("keep"):
- shutil.rmtree("jpywork", 1)
-
- cmd = "-i "
- if kw.has_key("core"):
- cmd = cmd + "--core "
- if kw.has_key("deep"):
- cmd = cmd + "--deep "
- if kw.has_key("all"):
- cmd = cmd + "--all "
- if kw.has_key("package"):
- cmd = cmd + "--package %s " % kw['package']
- if kw.has_key("addpackages"):
- cmd = cmd + "--addpackages %s " % kw['addpackages']
- if kw.has_key("jar"):
- cmd = cmd + "--jar %s " % kw['jar']
- if os.path.isfile(kw['jar']):
- os.remove(kw['jar'])
- cmd = cmd + " ".join(files)
-
- classpath = cfg.classpath
- if "classpath" in kw:
- classpath = os.pathsep.join([cfg.classpath, kw["classpath"]])
-
- jythonc = "%s/Tools/jythonc/jythonc.py %s" % (cfg.jython_home, cmd)
- if UNIX:
- cmd = "%s/bin/java -classpath %s -Dpython.home=%s org.python.util.jython %s" % (cfg.java_home, classpath, cfg.jython_home, jythonc)
- elif WIN:
- cmd = 'cmd /C "%s/bin/java.exe" -classpath "%s" -Dpython.home=%s org.python.util.jython %s' % (cfg.java_home, classpath, cfg.jython_home, jythonc)
- return execCmd(cmd, kw)
-
-def grep(file, text, count=0):
- f = open(file, "r")
- lines = f.readlines()
- f.close()
-
- result = []
- for line in lines:
- if re.search(text, line):
- result.append(line)
-
- if count:
- return len(result)
- return result
-
-class JarPacker:
- __doc__ = """helper class to pack stuff into a jar file -
- the terms 'file' and 'dir' mean java.io.File here """
-
- def __init__(self, jarFile, bufsize=1024):
- self._jarFile = jarFile
- self._bufsize = bufsize
- self._manifest = None
- self._jarOutputStream = None
-
- def close(self):
- self.getJarOutputStream().close()
-
- def addManifestFile(self, manifestFile):
- __doc__ = """only one manifest file can be added"""
- self.addManifest(Manifest(FileInputStream(manifestFile)))
-
- def addManifest(self, manifest):
- if not self._manifest:
- self._manifest = manifest
-
- def addFile(self, file, parentDirName=None):
- buffer = jarray.zeros(self._bufsize, 'b')
- inputStream = FileInputStream(file)
- jarEntryName = file.getName()
- if parentDirName:
- jarEntryName = parentDirName + "/" + jarEntryName
- self.getJarOutputStream().putNextEntry(JarEntry(jarEntryName))
- read = inputStream.read(buffer)
- while read <> -1:
- self.getJarOutputStream().write(buffer, 0, read)
- read = inputStream.read(buffer)
- self.getJarOutputStream().closeEntry()
- inputStream.close()
-
- def addDirectory(self, dir, parentDirName=None):
- if not dir.isDirectory():
- return
- filesInDir = dir.listFiles()
- for currentFile in filesInDir:
- if currentFile.isFile():
- if parentDirName:
- self.addFile(currentFile, parentDirName + "/" + dir.getName())
- else:
- self.addFile(currentFile, dir.getName())
- else:
- if parentDirName:
- newParentDirName = parentDirName + "/" + dir.getName()
- else:
- newParentDirName = dir.getName()
- self.addDirectory(currentFile, newParentDirName)
-
- def addJarFile(self, jarFile):
- __doc__ = """if you want to add a .jar file with a MANIFEST, add it first"""
- jarJarFile = JarFile(jarFile)
- self.addManifest(jarJarFile.getManifest())
- jarJarFile.close()
-
- jarInputStream = JarInputStream(FileInputStream(jarFile))
- jarEntry = jarInputStream.getNextJarEntry()
- while jarEntry:
- self.getJarOutputStream().putNextEntry(jarEntry)
- buffer = jarray.zeros(self._bufsize, 'b')
- read = jarInputStream.read(buffer)
- while read <> -1:
- self.getJarOutputStream().write(buffer, 0, read)
- read = jarInputStream.read(buffer)
- self.getJarOutputStream().closeEntry()
- jarEntry = jarInputStream.getNextJarEntry()
-
- def getJarOutputStream(self):
- if not self._jarOutputStream:
- if self._manifest:
- self._jarOutputStream = JarOutputStream(FileOutputStream(self._jarFile), self._manifest)
- else:
- self._jarOutputStream = JarOutputStream(FileOutputStream(self._jarFile))
- return self._jarOutputStream
diff --git a/bugtests/test238p/__init__.py b/bugtests/test238p/__init__.py
deleted file mode 100644
index 139597f9c..000000000
--- a/bugtests/test238p/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/bugtests/test238p/test238j.java b/bugtests/test238p/test238j.java
deleted file mode 100644
index efe6b5fa8..000000000
--- a/bugtests/test238p/test238j.java
+++ /dev/null
@@ -1,6 +0,0 @@
-
-package test238p;
-public class test238j {
- public static String spam() { return "bar"; }
- public static void bar() {}
-}
diff --git a/bugtests/test239j1.java b/bugtests/test239j1.java
deleted file mode 100644
index 5b12c94b8..000000000
--- a/bugtests/test239j1.java
+++ /dev/null
@@ -1,4 +0,0 @@
-public class test239j1 {
- public int theInt = 0;
- public int theSleepTime = 0;
-}
diff --git a/bugtests/test239j2.java b/bugtests/test239j2.java
deleted file mode 100644
index 12702301c..000000000
--- a/bugtests/test239j2.java
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-public class test239j2 implements Runnable {
- int myTestInt = 0;
- int mySleepTime = 0;
-
- public test239j2(test239j1 config) {
- myTestInt = config.theInt;
- mySleepTime = config.theSleepTime;
- }
-
- public void run() { }
-
-}
diff --git a/bugtests/test240p/__init__.py b/bugtests/test240p/__init__.py
deleted file mode 100644
index 8b1378917..000000000
--- a/bugtests/test240p/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/bugtests/test240p/test240j1.java b/bugtests/test240p/test240j1.java
deleted file mode 100644
index f2cfbc2b8..000000000
--- a/bugtests/test240p/test240j1.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package test240p;
-public class test240j1 {
- int theInt = 0;
- int theSleepTime = 0;
-}
diff --git a/bugtests/test240p/test240j2.java b/bugtests/test240p/test240j2.java
deleted file mode 100644
index 9405c0baf..000000000
--- a/bugtests/test240p/test240j2.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package test240p;
-public class test240j2 implements Runnable {
- int myTestInt = 0;
- int mySleepTime = 0;
-
- public test240j2(test240j1 config) {
- myTestInt = config.theInt;
- mySleepTime = config.theSleepTime;
- }
-
- public void run() { }
-
-}
diff --git a/bugtests/test241.py b/bugtests/test241.py
deleted file mode 100644
index 18aba26ac..000000000
--- a/bugtests/test241.py
+++ /dev/null
@@ -1,14 +0,0 @@
-import support
-
-support.compileJava("test241p/test241i.java")
-
-import test241p
-class A(test241p.test241i):
- def foo(self, i):
- return i
-
-a = A()
-v = a.foo(44)
-
-if v != 44:
- raise support.TestError, "Wrong return value %d" % v
diff --git a/bugtests/test241p/__init__.py b/bugtests/test241p/__init__.py
deleted file mode 100644
index 8b1378917..000000000
--- a/bugtests/test241p/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/bugtests/test241p/test241i.java b/bugtests/test241p/test241i.java
deleted file mode 100644
index 8995e7ccf..000000000
--- a/bugtests/test241p/test241i.java
+++ /dev/null
@@ -1,5 +0,0 @@
-
-package test241p;
-public interface test241i {
- public void foo(int i);
-}
\ No newline at end of file
diff --git a/bugtests/test243.py b/bugtests/test243.py
deleted file mode 100644
index 364f6df0a..000000000
--- a/bugtests/test243.py
+++ /dev/null
@@ -1,12 +0,0 @@
-
-import java, support
-
-import test243p
-#print test243p
-try:
- import test243p.P
-except (ImportError, java.lang.NoClassDefFoundError):
- pass
-else:
- raise support.TestError, "Should raise an exception"
-
diff --git a/bugtests/test243p/__init__.py b/bugtests/test243p/__init__.py
deleted file mode 100644
index f3d96f263..000000000
--- a/bugtests/test243p/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-__path__[0]=__path__[0]+'/real'
diff --git a/bugtests/test243p/real/A.java b/bugtests/test243p/real/A.java
deleted file mode 100644
index 98692c558..000000000
--- a/bugtests/test243p/real/A.java
+++ /dev/null
@@ -1 +0,0 @@
-package test243p; public class A { int v; }
\ No newline at end of file
diff --git a/bugtests/test243p/real/P.java b/bugtests/test243p/real/P.java
deleted file mode 100644
index 80a27f4b7..000000000
--- a/bugtests/test243p/real/P.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package test243p;
-
-public class P {
- public static void p(A a) { System.out.println(a.v); }
-}
diff --git a/bugtests/test244.py b/bugtests/test244.py
deleted file mode 100644
index 5fb8cef32..000000000
--- a/bugtests/test244.py
+++ /dev/null
@@ -1,8 +0,0 @@
-
-import support
-
-support.compileJava("test244p/A.java")
-
-import test244p.A
-
-a=test244p.A()
diff --git a/bugtests/test244p/A.java b/bugtests/test244p/A.java
deleted file mode 100644
index e15fa7b4d..000000000
--- a/bugtests/test244p/A.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package test244p;
-
-public class A { }
diff --git a/bugtests/test244p/__init__.py b/bugtests/test244p/__init__.py
deleted file mode 100644
index 8b1378917..000000000
--- a/bugtests/test244p/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/bugtests/test267.py b/bugtests/test267.py
deleted file mode 100644
index 997233c0d..000000000
--- a/bugtests/test267.py
+++ /dev/null
@@ -1,16 +0,0 @@
-"""
-Make sure that ImportAll and ExecStmt can modify the locals
-"""
-
-import support
-
-
-def f1():
- from stat import *
- v1 = ST_ATIME
- assert v1 == 7
- exec "foo=22"
- v2 = foo
- assert v2 == 22
-
-f1()
diff --git a/bugtests/test305.py b/bugtests/test305.py
deleted file mode 100644
index fb0a46205..000000000
--- a/bugtests/test305.py
+++ /dev/null
@@ -1,21 +0,0 @@
-"""
-Test that the case of the module extension does not matter.
-As the registry documentation says, this will only work if options.caseok is true.
-"""
-
-import support
-
-if support.UNIX:
- raise support.TestWarning("this will fail on unix platforms")
-
-from org.python.core import Options
-switchedCase = 0
-if not Options.caseok:
- switchedCase = 1
- Options.caseok = 1
-try:
- import test305m # the file is named test305m.PY
-finally:
- if switchedCase:
- Options.caseok = 0
-
diff --git a/bugtests/test305m.PY b/bugtests/test305m.PY
deleted file mode 100644
index 236df7905..000000000
--- a/bugtests/test305m.PY
+++ /dev/null
@@ -1,3 +0,0 @@
-
-foo = 1
-
diff --git a/bugtests/test306.py b/bugtests/test306.py
deleted file mode 100644
index 4a00aacff..000000000
--- a/bugtests/test306.py
+++ /dev/null
@@ -1,12 +0,0 @@
-"""
-Test normcase.
-"""
-
-import support
-import os
-
-if os.sep == '\\': #only do this test on windows.
- p1 = os.path.normpath('e:\\someDir\\packag/modul.py')
- if p1 != 'e:\\someDir\\packag\\modul.py':
- raise support.TestError('Wrong normpath %s' % p1)
-
diff --git a/bugtests/test321.py b/bugtests/test321.py
deleted file mode 100644
index b1b404b22..000000000
--- a/bugtests/test321.py
+++ /dev/null
@@ -1,32 +0,0 @@
-"""
-[ #475666 ] __nonzero__ exceptions must be ignored
-"""
-
-import support
-
-
-msgs = []
-
-class Foo:
- def __getattr__(self, key):
- msgs.append('getting %s' % key)
- raise KeyError, key
-
-foo = Foo()
-if not foo: print 'no foo'
-
-class Foo:
- def __nonzero__(self):
- msgs.append("called __nonzero__")
- raise KeyError
-
-foo = Foo()
-try:
- if not foo: print 'no foo'
-except KeyError:
- pass
-else:
- raise support.TestError('Must raise a keyerror')
-
-support.compare(msgs, "['getting __nonzero__', 'getting __len__', 'called __nonzero__']")
-
diff --git a/bugtests/test322.py b/bugtests/test322.py
deleted file mode 100644
index 8a4ad287c..000000000
--- a/bugtests/test322.py
+++ /dev/null
@@ -1,14 +0,0 @@
-"""
-[ #448398 ] open('test.txt','w').write('test') fails
-"""
-
-import support
-
-support.runJython("test322m.py")
-
-import os
-
-l = os.stat("test322.out")[6]
-
-if l != 7:
- raise support.TestWarning('The file should have been auto flushed')
diff --git a/bugtests/test322m.py b/bugtests/test322m.py
deleted file mode 100644
index a0a9add68..000000000
--- a/bugtests/test322m.py
+++ /dev/null
@@ -1,4 +0,0 @@
-f = open('test322.out','w')
-f.write("xvavava")
-#f.flush()
-
diff --git a/bugtests/test323.py b/bugtests/test323.py
deleted file mode 100644
index 917f07b74..000000000
--- a/bugtests/test323.py
+++ /dev/null
@@ -1,29 +0,0 @@
-"""
-Tests using a path inside a zip file for zip imports
-"""
-
-import support
-import zipfile, time
-
-def addZipEntry(zip, name, data):
- entry = zipfile.ZipInfo()
- entry.filename = name
- entry.date_time = time.gmtime(time.time())
- zip.writestr(entry, data)
-
-
-zip = zipfile.ZipFile("test323.zip", "w", zipfile.ZIP_DEFLATED)
-
-addZipEntry(zip, "Lib/test323m.py", """
-assert __name__ == 'test323m', " __name__ should've been test323m but was %s" % __name__
-from java.io import File
-expected = "test323.zip%sLib/test323m.py" % (File.separator)
-assert expected in __file__, "%s should've been in __file__ but was %s" % (expected, __file__)
-""")
-
-zip.close()
-
-import sys
-sys.path.append("test323.zip/Lib")
-
-import test323m
diff --git a/bugtests/test324.py b/bugtests/test324.py
deleted file mode 100644
index 9ef69e9ec..000000000
--- a/bugtests/test324.py
+++ /dev/null
@@ -1,14 +0,0 @@
-"""
-[ #467826 ] SHA digest() method doesn't work
-"""
-
-import support
-
-import sha
-s = sha.sha()
-s.update("foo")
-r = s.digest()
-
-support.compare(len(r), "20")
-
-
diff --git a/bugtests/test325.py b/bugtests/test325.py
deleted file mode 100644
index b8d88bc76..000000000
--- a/bugtests/test325.py
+++ /dev/null
@@ -1,35 +0,0 @@
-"""
-Verify a bug in pickle reported by mailling list.
-"""
-
-import support
-
-# This program failed with pickle due to the id() problem.
-import pickle
-
-pfile=open("test325.out","wb")
-p=pickle.Pickler(pfile)
-for l in range (1,10000):
- row=[str(l),str(l)]
- p.dump(row)
-pfile.close()
-
-#print "reading"
-n=1
-try:
- pfile=open("test325.out","rb")
- l=pickle.load(pfile)
- while l:
- comp = [str(n),str(n)]
- if l != comp:
- print "Pickle error"
- print str(l) + " should be " + str(comp)
- raise support.TestError("pickle is not working")
- n=n+1
- l=pickle.load(pfile)
- pfile.close()
-
-except EOFError:
- #print "End reached, well done"
- pfile.close()
-
diff --git a/bugtests/test326.py b/bugtests/test326.py
deleted file mode 100644
index a80bb1f33..000000000
--- a/bugtests/test326.py
+++ /dev/null
@@ -1,24 +0,0 @@
-"""
-[ #473676 ] cStringIO bug
-"""
-
-import support
-
-import cStringIO
-
-s = cStringIO.StringIO()
-r = s.read(1)
-
-if len(r) != 0:
- raise support.TestError('EOF must be the empty string')
-
-
-s = cStringIO.StringIO("abc")
-r = s.read(2)
-assert len(r) == 2
-r = s.read(1)
-assert len(r) == 1
-r = s.read(1)
-if len(r) != 0:
- raise support.TestError('EOF must be the empty string #2')
-
diff --git a/bugtests/test327.py b/bugtests/test327.py
deleted file mode 100644
index b3c2b9c05..000000000
--- a/bugtests/test327.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""
-[ #458945 ] Missing 'lastindex' on match objects
-"""
-
-import support
-
-import re
-
-m = re.match(r"(\w*) (\w*) (\w*)", "word1 word2 word3")
-if m.lastindex != 3:
- raise support.TestError('Wrong lastindex value#1 : %d' % m.lastindex)
-
-m = re.match(r"((\w*) )+", "word1 word2 word3 ")
-if m.lastindex != 2:
- raise support.TestError('Wrong lastindex value#2 : %d' % m.lastindex)
-
-m = re.match(r"abc", "abc")
-if m.lastindex != None:
- raise support.TestError('Wrong lastindex value#3 : %d' % m.lastindex)
-
diff --git a/bugtests/test328.py b/bugtests/test328.py
deleted file mode 100644
index fc94455aa..000000000
--- a/bugtests/test328.py
+++ /dev/null
@@ -1,15 +0,0 @@
-"""
-[ #462280 ] builtin method as a class variable
-"""
-
-import support
-
-class Foo:
- mylistIndex = ['a', 'b', 'c', 'd', 'e'].index
-
-a = Foo()
-if a.mylistIndex.__self__ != ['a', 'b', 'c', 'd', 'e']:
- raise support.TestError('Builtin func with wrong self')
-assert a.mylistIndex('c') == 2
-assert Foo.mylistIndex('c') == 2
-
diff --git a/bugtests/test329.py b/bugtests/test329.py
deleted file mode 100644
index 2e987a561..000000000
--- a/bugtests/test329.py
+++ /dev/null
@@ -1,12 +0,0 @@
-"""
-[ #475445 ] incompatibility with python
-
-Check comment handling when reading source from stdin.
-"""
-
-import support
-
-support.runJava("org.python.util.jython -S < test329s1.py > test329.out")
-support.runJava("org.python.util.jython -S < test329s2.py > test329.out")
-support.runJava("org.python.util.jython -S < test329s3.py > test329.out")
-
diff --git a/bugtests/test329s1.py b/bugtests/test329s1.py
deleted file mode 100644
index 853154428..000000000
--- a/bugtests/test329s1.py
+++ /dev/null
@@ -1,6 +0,0 @@
-import sys
-for t in range(10):
- # a comment
- print t; sys.exit(0)
-
-sys.exit(22)
diff --git a/bugtests/test329s2.py b/bugtests/test329s2.py
deleted file mode 100644
index 3c0517886..000000000
--- a/bugtests/test329s2.py
+++ /dev/null
@@ -1,6 +0,0 @@
-import sys
-for t in range(10):
-# a comment
- print t; sys.exit(0)
-
-sys.exit(22)
diff --git a/bugtests/test329s3.py b/bugtests/test329s3.py
deleted file mode 100644
index b107488d8..000000000
--- a/bugtests/test329s3.py
+++ /dev/null
@@ -1,6 +0,0 @@
-import sys
-for t in range(10):
- a = 1 # a comment
- print t; sys.exit(0)
-
-sys.exit(22)
diff --git a/bugtests/test330.py b/bugtests/test330.py
deleted file mode 100644
index dd068a9c4..000000000
--- a/bugtests/test330.py
+++ /dev/null
@@ -1,19 +0,0 @@
-"""
-[ #477793 ] os.utime() is missing
-"""
-
-import support
-
-import os
-f = open("test330.out", "w")
-f.close()
-
-m = os.stat("test330.out")[8]
-os.utime("test330.out", (0, 0))
-if os.stat("test330.out")[8] != 0:
- raise support.TestWarning("Modification time not changed #1")
-
-os.utime("test330.out", (m, m))
-if os.stat("test330.out")[8] != m:
- raise support.TestError("Modification time not changed #2")
-
diff --git a/bugtests/test331.py b/bugtests/test331.py
deleted file mode 100644
index f9416efe8..000000000
--- a/bugtests/test331.py
+++ /dev/null
@@ -1,15 +0,0 @@
-"""
-[ #477608 ] os.path.getmtime() missing
-"""
-
-import support
-import os
-
-s = os.stat("test331.py")
-
-if s[8] != os.path.getmtime("test331.py"):
- raise support.TestWarning("Modification time was wrong")
-
-if s[7] != os.path.getatime("test331.py"):
- raise support.TestWarning("Access time was wrong")
-
diff --git a/bugtests/test332.py b/bugtests/test332.py
deleted file mode 100644
index da2144fd9..000000000
--- a/bugtests/test332.py
+++ /dev/null
@@ -1,38 +0,0 @@
-"""
-[ #438297 ] SimpleHTTPServer does not work
-"""
-
-import support
-
-import sys
-import SimpleHTTPServer
-import BaseHTTPServer
-
-def test(HandlerClass = SimpleHTTPServer.SimpleHTTPRequestHandler,
- ServerClass = BaseHTTPServer.HTTPServer):
- server_address = ('', 8000)
- # Silense the server
- HandlerClass.log_message = lambda x, b, *arg: None
- httpd = ServerClass(server_address, HandlerClass)
- # do just one request.
- httpd.handle_request()
-
-import thread
-thread.start_new_thread(test, ())
-
-import httplib
-import time
-time.sleep(5)
-
-h = httplib.HTTP()
-h.connect("localhost", 8000)
-h.putrequest('GET', "/")
-h.endheaders()
-status, reason, headers = h.getreply()
-if status != 200:
- raise support.TestError("Wrong status: %d" % status)
-if reason != "OK":
- raise support.TestError("Wrong status: %d" % status)
-h.getfile().read()
-
-
diff --git a/bugtests/test333.py b/bugtests/test333.py
deleted file mode 100644
index 12994fc2e..000000000
--- a/bugtests/test333.py
+++ /dev/null
@@ -1,52 +0,0 @@
-"""
-[ #476772 ] shutdowns in jython / atexit
-"""
-
-import support
-import os
-
-def check(filename, result):
- f = open(filename)
- l = f.readlines()
- f.close()
- if l != result:
- raise support.TestError("Result was wrong: %s" % l)
-
-# Different exit situations in the interpreter.
-
-support.runJython("test333s1.py", output="test333s1.out")
-check("test333s1.out", [ "myfunc\n" ])
-
-ret = support.runJython("test333s2.py", output="test333s2.out", expectError=1)
-if ret != 42:
- raise support.TestError("Return code was wrong: %d" % ret)
-check("test333s2.out", [ "myfunc\n" ])
-
-support.runJython("test333s3.py",
- output="test333s3.out", error="test333s3.err", expectError=1)
-check("test333s3.out", [ "myfunc\n" ])
-check("test333s3.err", [
- 'Traceback (innermost last):\n',
- ' File "test333s3.py", line 8, in ?\n',
- 'Exc\n',
-])
-
-# Different exit situations in compiled applications.
-
-support.compileJPythonc("test333s1.py", output="test333s1.err")
-support.runJava("test333s1", classpath="jpywork", output="test333s1.out")
-check("test333s1.out", [ "myfunc\n" ])
-
-support.compileJPythonc("test333s1.py", output="test333s3.err")
-support.runJava("test333s1", classpath="jpywork", output="test333s1.out")
-check("test333s1.out", [ "myfunc\n" ])
-
-support.compileJPythonc("test333s3.py", output="test333s3.err")
-support.runJava("test333s3", classpath="jpywork", output="test333s3.out",
- error="test333s3.err", expectError=1)
-check("test333s3.out", [ "myfunc\n" ])
-f = open("test333s3.err")
-lines = f.readlines();
-f.close()
-if "Exc\n" not in lines:
- raise support.TestError("Should raise a 'Exc' exception")
diff --git a/bugtests/test333s1.py b/bugtests/test333s1.py
deleted file mode 100644
index 3f9896c01..000000000
--- a/bugtests/test333s1.py
+++ /dev/null
@@ -1,7 +0,0 @@
-
-import sys
-
-def myfunc():
- print "myfunc"
-
-sys.exitfunc = myfunc
diff --git a/bugtests/test333s2.py b/bugtests/test333s2.py
deleted file mode 100644
index 7fc74bbe5..000000000
--- a/bugtests/test333s2.py
+++ /dev/null
@@ -1,9 +0,0 @@
-
-import sys
-
-def myfunc():
- print "myfunc"
-
-sys.exitfunc = myfunc
-
-sys.exit(42)
diff --git a/bugtests/test333s3.py b/bugtests/test333s3.py
deleted file mode 100644
index 2757e633a..000000000
--- a/bugtests/test333s3.py
+++ /dev/null
@@ -1,8 +0,0 @@
-
-import sys
-
-def myfunc():
- print "myfunc"
-
-sys.exitfunc = myfunc
-raise "Exc"
diff --git a/bugtests/test334.py b/bugtests/test334.py
deleted file mode 100644
index 5300ab8d7..000000000
--- a/bugtests/test334.py
+++ /dev/null
@@ -1,11 +0,0 @@
-"""
-[ #477768 ] ord([123]) 21a3
-"""
-
-import support
-
-try:
- ord([123])
-except TypeError:
- pass
-
diff --git a/bugtests/test335.py b/bugtests/test335.py
deleted file mode 100644
index fbecd145e..000000000
--- a/bugtests/test335.py
+++ /dev/null
@@ -1,17 +0,0 @@
-"""
-[ #476580 ] 'del obj.non_member' : wrong exception
-"""
-
-import support
-class C : pass
-
-o = C()
-try:
- o.foo
-except AttributeError:
- pass
-
-try:
- del o.foo
-except AttributeError:
- pass
diff --git a/bugtests/test336.py b/bugtests/test336.py
deleted file mode 100644
index b05025331..000000000
--- a/bugtests/test336.py
+++ /dev/null
@@ -1,11 +0,0 @@
-"""
-[ #451552 ] case insensitivity on import causes prob
-"""
-
-import support
-import java
-
-support.compileJava("classes/test336p/Data.java")
-support.compileJava("classes/test336p/data/MyData.java")
-
-from test336p.data import MyData
diff --git a/bugtests/test338.py b/bugtests/test338.py
deleted file mode 100644
index a4f1f5321..000000000
--- a/bugtests/test338.py
+++ /dev/null
@@ -1,14 +0,0 @@
-"""
-[ #480017 ] Proxy supers are loaded from syspath
-Running test338j will throw a ClassCastException if a proxy's superclass is loaded
-by the syspath classloader.
-"""
-
-import support
-
-support.compileJava("test338cl.java", classpath=".")
-support.compileJava("test338j1.java", classpath=".")
-support.compileJava("test338j.java", classpath=".")
-
-support.runJava("test338j", classpath=".")
-
diff --git a/bugtests/test338cl.java b/bugtests/test338cl.java
deleted file mode 100644
index 95ac2a897..000000000
--- a/bugtests/test338cl.java
+++ /dev/null
@@ -1,30 +0,0 @@
-
-import java.io.*;
-
-public class test338cl extends ClassLoader {
-
- protected Class loadClass(String name, boolean resolve)
- throws ClassNotFoundException
- {
-//System.out.println("MyLoadClass " + name);
- Class c = findLoadedClass(name);
- if (c != null)
- return c;
-
- try {
- FileInputStream fis = new FileInputStream(name.replace('.', '/') + ".class");
- int size = fis.available();
- byte[] buf = new byte[size];
- fis.read(buf);
- fis.close();
-
- c = defineClass(name, buf, 0, buf.length);
- if (resolve)
- resolveClass(c);
- return c;
- } catch (IOException exc) {
- return super.loadClass(name, resolve);
- }
- }
-
-}
diff --git a/bugtests/test338j.java b/bugtests/test338j.java
deleted file mode 100644
index 6aab2cd30..000000000
--- a/bugtests/test338j.java
+++ /dev/null
@@ -1,26 +0,0 @@
-
-import org.python.util.*;
-import org.python.core.*;
-
-public class test338j implements Runnable {
- public static void main(String[] args) throws Exception {
- //new Main().run();
- Runnable r = (Runnable)Class.forName("test338j", true, new test338cl()).newInstance();
- r.run();
- }
-
- public void run() {
- String brob = "test338m";
- PythonInterpreter interp = new PythonInterpreter();
- interp.set("test338j1", test338j1.class);
- interp.execfile(brob + ".py");
- interp.exec("cl = " + brob + "()");
- Object newobj = interp.get("cl", Object.class);
- //System.out.println(newobj.getClass().getClassLoader());
- //System.out.println(newobj.getClass().getSuperclass().hashCode() + " " + test338j1.class.hashCode());
- //System.out.println(newobj.getClass().getSuperclass().getClassLoader());
- test338j1 boobj = (test338j1) newobj;
- }
-}
-
-
diff --git a/bugtests/test338j1.java b/bugtests/test338j1.java
deleted file mode 100644
index cb45c1bfe..000000000
--- a/bugtests/test338j1.java
+++ /dev/null
@@ -1,6 +0,0 @@
-
-public class test338j1 {
- public String getDescription() {
- return "a test338j1 description";
- }
-}
diff --git a/bugtests/test338m.py b/bugtests/test338m.py
deleted file mode 100644
index 644db5f3c..000000000
--- a/bugtests/test338m.py
+++ /dev/null
@@ -1,8 +0,0 @@
-#import test338j1
-
-class test338m(test338j1):
- def getDescription(self):
- desc = test338j1.getDescription(self) # Superclass call
- return "Foo_" + desc
-
-
diff --git a/bugtests/test339.py b/bugtests/test339.py
deleted file mode 100644
index 0f63687ca..000000000
--- a/bugtests/test339.py
+++ /dev/null
@@ -1,10 +0,0 @@
-"""
-[ #449956 ] jythonc 2.1a3 --package problem
-
-"""
-
-import support
-
-support.compileJPythonc("test339c.py", package="test339p", output="test339.err")
-
-#raise support.TestError("" + `x`)
diff --git a/bugtests/test339c.py b/bugtests/test339c.py
deleted file mode 100644
index dddb0e839..000000000
--- a/bugtests/test339c.py
+++ /dev/null
@@ -1,12 +0,0 @@
-import java
-import test339c
-
-class spam(java.lang.Object):
- pass
-
-class eggs1(spam):
- pass
-
-class eggs2(test339c.spam):
- pass
-
diff --git a/bugtests/test340.py b/bugtests/test340.py
deleted file mode 100644
index ba39d622e..000000000
--- a/bugtests/test340.py
+++ /dev/null
@@ -1,15 +0,0 @@
-"""
-[ #480390 ] main() does not throw exceptions
-"""
-
-import support
-
-support.compileJPythonc("test340c.py", core=1, jar="test340.jar",
- output="test340.err")
-support.compileJava("test340j.java")
-
-rc = support.runJava("test340j", classpath=".", expectError=1)
-if rc != 42:
- support.TestError("Did not catch exception correctly %d" % rc)
-
-
diff --git a/bugtests/test340c.py b/bugtests/test340c.py
deleted file mode 100644
index 6d56b45a5..000000000
--- a/bugtests/test340c.py
+++ /dev/null
@@ -1,5 +0,0 @@
-
-import java.lang.Exception
-raise java.lang.Exception
-
-
diff --git a/bugtests/test340j.java b/bugtests/test340j.java
deleted file mode 100644
index eb962aab2..000000000
--- a/bugtests/test340j.java
+++ /dev/null
@@ -1,24 +0,0 @@
-
-import java.net.*;
-import java.lang.reflect.*;
-
-public class test340j {
- public static void main(String[] args) {
- try {
- String jar = "./test340.jar";
- URLClassLoader theLoader = new URLClassLoader(new URL[] {
- new URL("file:" + jar)});
- Object theLoadedClass = Class.forName("test340c", true, theLoader).
- newInstance();
- String[] array = new String[] {};
- Method main = theLoadedClass.getClass().
- getMethod("main", new Class[] { array.getClass() });
- main.invoke(theLoadedClass, new Object[] {new String[] {}});
- }
- catch (Throwable t) {
- System.exit(42);
- }
- System.exit(43);
- }
-}
-
diff --git a/bugtests/test341.py b/bugtests/test341.py
deleted file mode 100644
index 88765edbf..000000000
--- a/bugtests/test341.py
+++ /dev/null
@@ -1,10 +0,0 @@
-"""
-[ #451746 ] jythonc --deep jpy$packages problem
-
-"""
-
-import support
-
-support.compileJPythonc("test341c1.py", deep=1 , output="test341.err")
-
-#raise support.TestError("" + `x`)
diff --git a/bugtests/test341c1.py b/bugtests/test341c1.py
deleted file mode 100644
index 1efad1a35..000000000
--- a/bugtests/test341c1.py
+++ /dev/null
@@ -1,7 +0,0 @@
-
-from test341c2 import test341c2
-
-class bar(test341c2):
- pass
-
-
diff --git a/bugtests/test341c2.py b/bugtests/test341c2.py
deleted file mode 100644
index 367e7a95e..000000000
--- a/bugtests/test341c2.py
+++ /dev/null
@@ -1,5 +0,0 @@
-import java
-
-class test341c2(java.lang.Object):
- pass
-
diff --git a/bugtests/test342.py b/bugtests/test342.py
deleted file mode 100644
index b90bf1f44..000000000
--- a/bugtests/test342.py
+++ /dev/null
@@ -1,11 +0,0 @@
-"""
-__import__(_) does an unwanted relative search
-"""
-
-import support
-
-from test342p import doimp
-
-#support.compare(doimp.kind,"absolute")
-if doimp.kind != "absolute":
- raise support.TestError("Should be absolute")
diff --git a/bugtests/test342m.py b/bugtests/test342m.py
deleted file mode 100644
index 0653c5533..000000000
--- a/bugtests/test342m.py
+++ /dev/null
@@ -1 +0,0 @@
-kind = "absolute"
diff --git a/bugtests/test342p/__init__.py b/bugtests/test342p/__init__.py
deleted file mode 100644
index 139597f9c..000000000
--- a/bugtests/test342p/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/bugtests/test342p/doimp.py b/bugtests/test342p/doimp.py
deleted file mode 100644
index a2d9f6d68..000000000
--- a/bugtests/test342p/doimp.py
+++ /dev/null
@@ -1,3 +0,0 @@
-kind = __import__('test342m').kind
-
-
diff --git a/bugtests/test342p/test342m.py b/bugtests/test342p/test342m.py
deleted file mode 100644
index d8413ddfc..000000000
--- a/bugtests/test342p/test342m.py
+++ /dev/null
@@ -1 +0,0 @@
-kind = "relative"
diff --git a/bugtests/test343.py b/bugtests/test343.py
deleted file mode 100644
index 923cdb20f..000000000
--- a/bugtests/test343.py
+++ /dev/null
@@ -1,9 +0,0 @@
-"""
-[ #485558 ] Synchronization bug in sys.initialize
-"""
-
-import support
-
-support.compileJava("test343j.java")
-support.runJava("test343j", classpath=".")
-
diff --git a/bugtests/test343j.java b/bugtests/test343j.java
deleted file mode 100644
index 9684791f9..000000000
--- a/bugtests/test343j.java
+++ /dev/null
@@ -1,16 +0,0 @@
-import org.python.util.*;
-
-public class test343j implements Runnable {
- public static void main(String[] args) {
- new Thread(new test343j()).start();
- new Thread(new test343j()).start();
- new Thread(new test343j()).start();
- new Thread(new test343j()).start();
- new Thread(new test343j()).start();
- }
-
- public void run() {
- new PythonInterpreter();
- new PythonInterpreter();
- }
-}
diff --git a/bugtests/test344.py b/bugtests/test344.py
deleted file mode 100644
index 7cf99cb05..000000000
--- a/bugtests/test344.py
+++ /dev/null
@@ -1,18 +0,0 @@
-"""
-[ #485968 ] cStringIO.softspace is not assignable.
-"""
-
-import support
-
-import sys, cStringIO
-
-h = cStringIO.StringIO()
-
-sys.stdout = h
-print "line1"
-print "line2",
-print "line3",
-sys.stdout = sys.__stdout__
-
-if h.getvalue() != "line1\nline2 line3":
- raise support.TestError('Wrong softspace handling in cStringIO"')
diff --git a/bugtests/test345.py b/bugtests/test345.py
deleted file mode 100644
index ddf47b0e2..000000000
--- a/bugtests/test345.py
+++ /dev/null
@@ -1,9 +0,0 @@
-"""
-[ #489836 ] Private names is not mangled
-"""
-
-import support
-
-support.compileJPythonc("test345c.py", jar="test345.jar", core=1,
- output="test345.out")
-support.runJava("-jar test345.jar")
diff --git a/bugtests/test345c.py b/bugtests/test345c.py
deleted file mode 100644
index fb09e5331..000000000
--- a/bugtests/test345c.py
+++ /dev/null
@@ -1,28 +0,0 @@
-"""
-
-"""
-
-import support
-
-class A:
- def __init__(self):
- self.__stop("A")
- self.__x = 1
- self.__y = 1
- del self.__x
-
- def __stop(self, s):
- pass
-
- __c = 1
-
-def simpledir(obj):
- l = obj.__dict__.keys()
- l.sort()
- return l
-
-if simpledir(A) != ['_A__c', '_A__stop', '__doc__', '__init__', '__module__']:
- raise support.TestError("bug in private class var mangling %s" % dir(A))
-if simpledir(A()) != ['_A__y']:
- raise support.TestError("bug in private var mangling %s" % dir(A()))
-
diff --git a/bugtests/test346.py b/bugtests/test346.py
deleted file mode 100644
index cd4a2e048..000000000
--- a/bugtests/test346.py
+++ /dev/null
@@ -1,9 +0,0 @@
-"""
-[ #488632 ] -c sys.argv diff
-"""
-
-import support
-
-support.runJython(
- """-c "import sys; assert sys.argv == ['-c', '-v', 'args']" -v args""")
-
diff --git a/bugtests/test347.py b/bugtests/test347.py
deleted file mode 100644
index 1ac7d8e91..000000000
--- a/bugtests/test347.py
+++ /dev/null
@@ -1,11 +0,0 @@
-"""
-[ #490157 ] string.splitlines() - incorrectly splits
-"""
-
-import support
-
-r = 'This is a\n multiline string\n'.splitlines()
-
-if r != ['This is a', ' multiline string']:
- raise support.TestError("Wrong splitlines(): %s" % r)
-
diff --git a/bugtests/test348.py b/bugtests/test348.py
deleted file mode 100644
index 3d17c948d..000000000
--- a/bugtests/test348.py
+++ /dev/null
@@ -1,13 +0,0 @@
-"""
-[ #490230 ] NotImplemented not implemented
-"""
-
-import support
-
-class Z:
- def __le__(self,o):
- return NotImplemented
-
-z=Z()
-assert z<="a"
-
diff --git a/bugtests/test349.py b/bugtests/test349.py
deleted file mode 100644
index 174954b56..000000000
--- a/bugtests/test349.py
+++ /dev/null
@@ -1,24 +0,0 @@
-"""
-[ #494514 ] Python object not gc()'d
-"""
-
-import support
-import java, time, sys, cStringIO
-
-class A:
- def __del__(self):
- raise KeyError, "dummy"
-
-try:
- sys.stderr = cStringIO.StringIO()
- A()
-
- java.lang.System.gc()
- time.sleep(2)
-finally:
- v = sys.stderr.getvalue()
- sys.stderr = sys.__stderr__
-
- support.compare(v, "Exception KeyError: .* ignored")
-
-
diff --git a/bugtests/test350.py b/bugtests/test350.py
deleted file mode 100644
index 5026c7dbd..000000000
--- a/bugtests/test350.py
+++ /dev/null
@@ -1,33 +0,0 @@
-"""
-[ #495458 ] multi level import from .zip file
-"""
-
-import support
-import zipfile, time
-
-def addZipEntry(zip, name, data):
- entry = zipfile.ZipInfo()
- entry.filename = name
- entry.date_time = time.gmtime(time.time())
- zip.writestr(entry, data)
-
-zip = zipfile.ZipFile("test350.zip", "w")
-
-addZipEntry(zip, "Lib/aaa/__init__.py", "")
-addZipEntry(zip, "Lib/aaa/bbb/__init__.py", "")
-addZipEntry(zip, "Lib/aaa/bbb/ccc/__init__.py", "")
-addZipEntry(zip, "Lib/aaa/bbb/ccc/yyy.py", "")
-addZipEntry(zip, "Lib/aaa/bbb/xxx.py", "")
-
-zip.close()
-
-import sys
-sys.path.append("test350.zip/Lib")
-
-import aaa
-import aaa.bbb
-import aaa.bbb.ccc
-import aaa.bbb.ccc.yyy
-import aaa.bbb.xxx
-
-sys.path.pop()
diff --git a/bugtests/test351.py b/bugtests/test351.py
deleted file mode 100644
index f19554aa8..000000000
--- a/bugtests/test351.py
+++ /dev/null
@@ -1,11 +0,0 @@
-"""
-[ #489168 ] Parse error, java traceback
-"""
-
-import support
-
-a = 1
- # indented comment
-a = 2
-
-
diff --git a/bugtests/test352.py b/bugtests/test352.py
deleted file mode 100644
index 3a8747802..000000000
--- a/bugtests/test352.py
+++ /dev/null
@@ -1,78 +0,0 @@
-"""
-[ #495602 ] os.path.dirname() can result in an NPE
-"""
-
-import support
-import os
-
-try:
- os.path.dirname(None)
-except TypeError:
- pass
-
-try:
- os.path.basename(None)
-except TypeError:
- pass
-
-try:
- os.path.exists(None)
-except TypeError:
- pass
-
-try:
- os.path.isabs(None)
-except TypeError:
- pass
-
-try:
- os.path.isfile(None)
-except TypeError:
- pass
-
-try:
- os.path.isdir(None)
-except TypeError:
- pass
-
-try:
- os.path.join(None)
-except TypeError:
- pass
-
-try:
- os.path.join(None, None)
-except TypeError:
- pass
-
-try:
- os.path.normcase(None)
-except (TypeError, AttributeError):
- pass
-
-try:
- if hasattr(os.path, "samefile"):
- os.path.samefile(None, None)
-except TypeError:
- pass
-
-try:
- os.path.abspath(None)
-except TypeError:
- pass
-
-try:
- os.path.getsize(None)
-except TypeError:
- pass
-
-try:
- os.path.getmtime(None)
-except TypeError:
- pass
-
-try:
- os.path.getatime(None)
-except TypeError:
- pass
-
diff --git a/bugtests/test353.py b/bugtests/test353.py
deleted file mode 100644
index 3afef6ec3..000000000
--- a/bugtests/test353.py
+++ /dev/null
@@ -1,9 +0,0 @@
-"""
-[ #495604 ] imp.find_module fails when None is 2 arg
-"""
-
-import support
-
-import imp
-imp.find_module("re", None)
-
diff --git a/bugtests/test354.py b/bugtests/test354.py
deleted file mode 100644
index f0a844e06..000000000
--- a/bugtests/test354.py
+++ /dev/null
@@ -1,13 +0,0 @@
-"""
-[ 522423 ] cStringIO has no reset() method
-"""
-
-import support
-
-import cStringIO
-s = cStringIO.StringIO("abcdef")
-s.read(3)
-s.reset()
-support.compare(s.read(3), "abc")
-support.compare(s.read(3), "def")
-
diff --git a/bugtests/test355.py b/bugtests/test355.py
deleted file mode 100644
index edc72273f..000000000
--- a/bugtests/test355.py
+++ /dev/null
@@ -1,13 +0,0 @@
-"""
-[ 522558 ] list() is broken
-"""
-
-import support
-
-L = [1, 2, 3]
-L2 = list(L)
-L2.insert(0, 4)
-if L == L2:
- raise support.TestError('list() should create a copy')
-
-
diff --git a/bugtests/test356.py b/bugtests/test356.py
deleted file mode 100644
index bfd07ba2b..000000000
--- a/bugtests/test356.py
+++ /dev/null
@@ -1,10 +0,0 @@
-"""
-[ 522828 ] struct.pack('>NNs', v) fails for NN > 20
-"""
-
-import support
-
-import struct
-a = 'abcd' * 8
-struct.pack('>32s', a)
-
diff --git a/bugtests/test357.py b/bugtests/test357.py
deleted file mode 100644
index 9380c3f3c..000000000
--- a/bugtests/test357.py
+++ /dev/null
@@ -1,10 +0,0 @@
-"""
-[ 517237 ] Binary ops with int and long fail
-"""
-
-import support
-
-5&7L
-5|7L
-5^7L
-
diff --git a/bugtests/test358.py b/bugtests/test358.py
deleted file mode 100644
index c8e56f308..000000000
--- a/bugtests/test358.py
+++ /dev/null
@@ -1,10 +0,0 @@
-"""
-[ 515894 ] Behaviour of "+=" stm. is different from
-"""
-
-import support
-
-a = [1, 2, 3]
-a += "456"
-if a != [1, 2, 3, '4', '5', '6']:
- raise support.TestError('list += not working')
diff --git a/bugtests/test360.py b/bugtests/test360.py
deleted file mode 100644
index 4eef9dc9e..000000000
--- a/bugtests/test360.py
+++ /dev/null
@@ -1,10 +0,0 @@
-"""
-[ 532747 ] for i in iter(d)
-"""
-
-import support
-
-d = {1:2,3:4}
-l = []
-for i in iter(d): l.append(i)
-
diff --git a/bugtests/test361.py b/bugtests/test361.py
deleted file mode 100644
index 92774d13c..000000000
--- a/bugtests/test361.py
+++ /dev/null
@@ -1,15 +0,0 @@
-"""
-Test for [ 551888 ] Opening utf-8 files with codecs fails
-"""
-
-import support
-
-f = open("test361.out", "w")
-f.write("hello")
-f.close()
-
-import codecs
-f = codecs.open("test361.out", "r", "utf-8")
-print f.read()
-f.close()
-
diff --git a/bugtests/test362.py b/bugtests/test362.py
deleted file mode 100644
index 3268737e2..000000000
--- a/bugtests/test362.py
+++ /dev/null
@@ -1,14 +0,0 @@
-"""
-[ 545235 ] unexpected match with re
-"""
-
-import support
-
-
-import re
-rt = re.compile(r'c[^a]*t', re.IGNORECASE)
-if rt.match("cat") is not None:
- raise support.TestError('Should not match #1')
-rs = re.compile(r'c[^a]t', re.IGNORECASE)
-if rs.match('cat') is not None:
- raise support.TestError('Should not match #2')
diff --git a/bugtests/test363.py b/bugtests/test363.py
deleted file mode 100644
index 716ac1780..000000000
--- a/bugtests/test363.py
+++ /dev/null
@@ -1,10 +0,0 @@
-"""
-[ 533354 ] bug in xml.dom.minidom.parseString
-"""
-
-import support
-
-import xml.dom.minidom
-DOM = xml.dom.minidom.parseString("")
-
-#raise support.TestWarning('A test of TestWarning. It is not an error')
diff --git a/bugtests/test364.py b/bugtests/test364.py
deleted file mode 100644
index 2c414f2b4..000000000
--- a/bugtests/test364.py
+++ /dev/null
@@ -1,18 +0,0 @@
-"""
-[ 531256 ] Constructor problem using newInstance()
-"""
-
-import support
-
-support.compileJava("test364p/X.java")
-support.compileJava("test364p/Y.java")
-
-from test364p import X,Y
-class PyX(X): pass
-class PyY(Y): pass
-PyX.useClass(PyY)
-X() # OK
-try:
- PyX() # Not OK prints 'TypeError: Proxy instance reused'
-except TypeError:
- raise support.TestWarning('Class ctor should mix with newInstance()')
diff --git a/bugtests/test364p/X.java b/bugtests/test364p/X.java
deleted file mode 100644
index 51f3e3b62..000000000
--- a/bugtests/test364p/X.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package test364p;
-public class X {
- static Class myClass;
- // register a class to construct
- public static void useClass(Class cls) {
- myClass=cls;
- }
- Object o;
- public X() throws Exception {
- o=myClass.newInstance();
- }
-}
-
diff --git a/bugtests/test364p/Y.java b/bugtests/test364p/Y.java
deleted file mode 100644
index 5cd68b1ab..000000000
--- a/bugtests/test364p/Y.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package test364p;
-public class Y {
- public Y() {
- }
-}
-
diff --git a/bugtests/test365.py b/bugtests/test365.py
deleted file mode 100644
index f14440c72..000000000
--- a/bugtests/test365.py
+++ /dev/null
@@ -1,9 +0,0 @@
-"""
-[ 508111 ] jythonc generates invalid statements
-"""
-
-import support
-
-support.compileJPythonc("test365c.py", output="test365.err")
-
-#raise support.TestWarning('A test of TestWarning. It is not an error')
diff --git a/bugtests/test365c.py b/bugtests/test365c.py
deleted file mode 100644
index c31883f58..000000000
--- a/bugtests/test365c.py
+++ /dev/null
@@ -1,8 +0,0 @@
-
-bar = 1
-def foo():
- return 2
-
-bar and foo()
-
-
diff --git a/bugtests/test366i.java b/bugtests/test366i.java
deleted file mode 100644
index 42ca09b5a..000000000
--- a/bugtests/test366i.java
+++ /dev/null
@@ -1,4 +0,0 @@
-
-public interface test366i {
- public void foo();
-}
diff --git a/bugtests/test366j.java b/bugtests/test366j.java
deleted file mode 100644
index 238db4b1a..000000000
--- a/bugtests/test366j.java
+++ /dev/null
@@ -1,4 +0,0 @@
-public class test366j {
- public void foo() { }
-}
-
diff --git a/bugtests/test367.py b/bugtests/test367.py
deleted file mode 100644
index a7aead262..000000000
--- a/bugtests/test367.py
+++ /dev/null
@@ -1,15 +0,0 @@
-"""
-Test raising a tuple.
-"""
-
-import support
-
-error = "anerror";
-
-try:
- raise (error,), "value"
-except error:
- pass
-except:
- raise support.TestError('Should have been caught by except clause')
-
diff --git a/bugtests/test368.py b/bugtests/test368.py
deleted file mode 100644
index 477e319dc..000000000
--- a/bugtests/test368.py
+++ /dev/null
@@ -1,45 +0,0 @@
-"""
-[ 529242 ] Python singletons deserialization bug
-"""
-
-import support
-
-from java.io import *
-from org.python.util import *
-
-
-SINGL= None
-#SINGL= Ellipsis
-
-class Test(Serializable):
- def __init__(self):
- self.attr = SINGL
- def test(self):
- if self.attr is not SINGL:
- raise support.TestError("Singleton not unique")
- if self.attr != SINGL:
- raise support.TestError("Singleton not unique")
-
-def load(path):
- file = File(path)
- fileIn = FileInputStream(file)
- pyIn = PythonObjectInputStream(fileIn)
- pyObj = pyIn.readObject()
- pyIn.close()
- return pyObj
-
-def save(obj, path):
- fileOut = FileOutputStream(path)
- objOut = ObjectOutputStream(fileOut)
- objOut.writeObject(obj)
- objOut.flush()
- objOut.close()
-
-#print "Testing initial object..."
-a = Test()
-a.test()
-save(a, "test368.out")
-b = load("test368.out")
-#print "Testing deserialized object..."
-b.test()
-
diff --git a/bugtests/test370.py b/bugtests/test370.py
deleted file mode 100644
index 0cd2b970d..000000000
--- a/bugtests/test370.py
+++ /dev/null
@@ -1,13 +0,0 @@
-"""
-[ 562943 ] os.path.getmtime misbehaves on nonfile
-"""
-
-import support
-
-import os.path
-try:
- print os.path.getmtime('nonfile')
-except OSError:
- pass
-else:
- raise support.TestError('Should raise an OSError')
diff --git a/bugtests/test371.py b/bugtests/test371.py
deleted file mode 100644
index 8fc9b78c4..000000000
--- a/bugtests/test371.py
+++ /dev/null
@@ -1,338 +0,0 @@
-"""
-[ 577395 ] Outer finally not executed at return.
-break/continue through finally.
-"""
-# Local name: bugtests/test371.py
-
-
-import support
-
-# Some glue to do all tests defined in this module,
-# and fail only at end in finalTestReport()
-totalTestFailures = 0
-totalTests = 0
-
-def testFail(mes):
- global totalTestFailures
- global totalTests
- print 'Fail:', mes
- totalTestFailures += 1
- totalTests += 1
-
-def testPass(mes):
- global totalTests
- #print 'Ok:', mes
- totalTests += 1
-
-def testEq(val, expected, mes):
- if val != expected:
- testFail('%s: expected %s, got %s' % (mes, repr(expected), repr(val)))
- else:
- testPass('%s: %s' % (mes, repr(val)))
-
-def finalTestReport():
- global totalTestFailures
- global totalTests
- if totalTestFailures > 0:
- raise support.TestError('%d of %d test(s) failed in this module'
- % (totalTestFailures, totalTests))
- else:
- print 'All %d test(s) passed in this module.' % totalTests
-
-
-retval = 'rql'
-
-
-x = []
-def tryfinallyreturn1():
- try:
- x.append(1)
- return retval
- finally:
- x.append(2)
-
-r = tryfinallyreturn1()
-testEq(x, [1,2], 'tryfinallyreturn1 side effect')
-testEq(r, retval, 'tryfinallyreturn1 return value')
-
-x = []
-def tryfinallyreturn2(): # fails in jython 2.1, x == [1,2] afterwards
- try:
- try:
- x.append(1)
- return retval
- finally:
- x.append(2)
- finally:
- x.append(3)
-
-r = tryfinallyreturn2()
-testEq(x, [1,2,3], 'tryfinallyreturn2 side effect')
-testEq(r, retval, 'tryfinallyreturn2 return value')
-
-x = []
-def tryfinallyreturn3(): # fails in jython 2.1, x == [1,2] afterwards
- try:
- try:
- try:
- x.append(1)
- return retval
- finally:
- x.append(2)
- finally:
- x.append(3)
- finally:
- x.append(4)
-
-r = tryfinallyreturn3()
-testEq(x, [1,2,3,4], 'tryfinallyreturn3 side effect')
-testEq(r, retval, 'tryfinallyreturn3 return value')
-
-
-x = []
-def tryfinallyraise1():
- try:
- x.append(1)
- raise Exception
- finally:
- x.append(2)
-
-try:
- tryfinallyraise1()
-except Exception:
- testEq(x, [1,2], 'tryfinallyraise1 side effect')
-else:
- testFail('tryfinallyraise1 did not trow Exception')
-
-
-x = []
-def tryfinallyraise2():
- try:
- try:
- x.append(1)
- raise Exception
- finally:
- x.append(2)
- finally:
- x.append(3)
-
-try:
- tryfinallyraise2()
-except Exception:
- testEq(x, [1,2,3], 'tryfinallyraise2 side effect')
-else:
- testFail('tryfinallyraise2 did not trow Exception')
-
-x = []
-def tryfinallyraise3():
- try:
- try:
- try:
- x.append(1)
- raise Exception
- finally:
- x.append(2)
- finally:
- x.append(3)
- finally:
- x.append(4)
-
-try:
- tryfinallyraise3()
-except Exception:
- testEq(x, [1,2,3,4], 'tryfinallyraise3 side effect')
-else:
- testFail('tryfinallyraise3 did not trow Exception')
-
-
-x = []
-def fortryfinallycontinuereturn1():
- for i in range(3):
- try:
- x.append(2 * i)
- if i == 0:
- continue
- return retval
- finally:
- x.append(2 * i + 1)
-
-r = fortryfinallycontinuereturn1()
-testEq(x, [0,1,2,3], 'fortryfinallycontinuereturn1 side effect')
-testEq(r, retval, 'fortryfinallycontinuereturn1 return value')
-
-x = []
-def fortryfinallycontinuereturn2():
- for i in range(3):
- try:
- try:
- x.append(3 * i)
- if i == 0:
- continue
- return retval
- finally:
- x.append(3 * i + 1)
- finally:
- x.append(3 * i + 2)
-
-r = fortryfinallycontinuereturn2()
-testEq(x, [0,1,2,3,4,5], 'fortryfinallycontinuereturn2 side effect')
-testEq(r, retval, 'fortryfinallycontinuereturn2 return value')
-
-x = []
-def fortryfinallycontinuereturn3(): # fails in jython 2.1, x == [1,2] afterwards
- for i in range(3):
- try:
- try:
- try:
- x.append(4 * i)
- if i == 0:
- continue
- return retval
- finally:
- x.append(4 * i + 1)
- finally:
- x.append(4 * i + 2)
- finally:
- x.append(4 * i + 3)
-
-
-r = fortryfinallycontinuereturn3()
-testEq(x, [0,1,2,3,4,5,6,7], 'fortryfinallycontinuereturn3 side effect')
-testEq(r, retval, 'fortryfinallycontinuereturn3 return value')
-
-
-x = []
-def fortryfinallybreak1():
- for i in range(3):
- try:
- x.append(2 * i)
- if i == 1:
- break
- finally:
- x.append(2 * i + 1)
- return retval
-
-r = fortryfinallybreak1()
-testEq(x, [0,1,2,3], 'fortryfinallybreak1 side effect')
-testEq(r, retval, 'fortryfinallybreak1 return value')
-
-x = []
-def fortryfinallybreak2():
- for i in range(3):
- try:
- try:
- x.append(3 * i)
- if i == 1:
- break
- finally:
- x.append(3 * i + 1)
- finally:
- x.append(3 * i + 2)
- return retval
-
-r = fortryfinallybreak2()
-testEq(x, [0,1,2,3,4,5], 'fortryfinallybreak2 side effect')
-testEq(r, retval, 'fortryfinallybreak2 return value')
-
-
-x = []
-def fortryfinallycontinueraise1():
- for i in range(3):
- try:
- x.append(2 * i)
- if i == 0:
- continue
- raise Exception
- finally:
- x.append(2 * i + 1)
-
-try:
- fortryfinallycontinueraise1()
-except Exception:
- testEq(x, [0,1,2,3], 'fortryfinallycontinueraise1 side effect')
-else:
- testFail('fortryfinallycontinueraise1 did not trow Exception')
-
-x = []
-def fortryfinallycontinueraise2():
- for i in range(3):
- try:
- try:
- x.append(3 * i)
- if i == 0:
- continue
- raise Exception
- finally:
- x.append(3 * i + 1)
- finally:
- x.append(3 * i + 2)
-
-try:
- fortryfinallycontinueraise2()
-except Exception:
- testEq(x, [0,1,2,3,4,5], 'fortryfinallycontinueraise2 side effect')
-else:
- testFail('fortryfinallycontinueraise2 did not trow Exception')
-
-
-x = []
-def tryfortrycontinueraise1():
- try:
- for i in range(3):
- try:
- x.append(2 * i)
- if i == 0:
- continue
- raise Exception
- finally:
- x.append(2 * i + 1)
- finally:
- x.append('last')
-
-try:
- tryfortrycontinueraise1()
-except Exception:
- testEq(x, [0,1,2,3,'last'], 'tryfortrycontinueraise1 side effect')
-else:
- testFail('tryfortrycontinueraise1 did not trow Exception')
-
-
-x = []
-def tryfortrybreak1():
- try:
- for i in range(3):
- try:
- x.append(2 * i)
- if i == 1:
- break
- finally:
- x.append(2 * i + 1)
- return retval
- finally:
- x.append('last')
-
-r = tryfortrybreak1()
-testEq(x, [0,1,2,3,'last'], 'tryfortrybreak1 side effect')
-testEq(r, retval, 'tryfortrybreak1 return value')
-
-
-x = []
-def tryfortrycontinuereturn1():
- try:
- for i in range(3):
- try:
- x.append(2 * i)
- if i == 0:
- continue
- return retval
- finally:
- x.append(2 * i + 1)
- finally:
- x.append('last')
-
-r = tryfortrycontinuereturn1()
-testEq(x, [0,1,2,3,'last'], 'tryfortryfinallyreturn1 side effect')
-testEq(r, retval, 'tryfortrycontinuereturn1 return value')
-
-finalTestReport()
-
diff --git a/bugtests/test372.py b/bugtests/test372.py
deleted file mode 100644
index 4cc8923b2..000000000
--- a/bugtests/test372.py
+++ /dev/null
@@ -1,88 +0,0 @@
-"""
-Test for patch "[ 577728 ] struct.java now accepts 64bits ints"
-"""
-
-import support
-
-from struct import *
-
-#unsigned long check
-try:
- pack('Q',-1)
- raise support.TestError('Error: unsigned long should not work')
-except (TypeError, error):
- pass
-
-big_long=0x10000000000000000L
-#oversized unsigned long check
-try:
- print pack('64bits ints'
-except OverflowError:
- pass
-
-try:
- print pack('>Q',big_long)
- print 'Error, should not try to pack >64bits ints'
-except OverflowError:
- pass
-
-#oversized positive signed long check
-try:
- print pack('64bits ints'
-except OverflowError:
- pass
-
-try:
- print pack('>q',big_long)
- print 'Error, should not try to pack >64bits ints'
-except OverflowError:
- pass
-
-#oversized negative signed long check
-big_long=-big_long
-try:
- print pack('64bits ints'
-except OverflowError:
- pass
-
-try:
- print pack('>q',big_long)
- print 'Error, should not try to pack >64bits ints'
-except OverflowError:
- pass
-
-u_data=(0x1L,0x10000L,0x100000000L)
-s_data=(0x1L,-0x10000L,0x0FFFFFFFFL,-0x100000000L)
-#internal pack-unpack coherence check
-
-s=pack(' %s ==> %s"%(`u_data`,`s`,`unpack('QQQ',u_data[0],u_data[1],u_data[2])
-if u_data!=unpack('>QQQ',s):
- raise support.TestError("internal coherence error: %s ==> %s ==> %s"%(`u_data`,`s`,`unpack('>QQQ',s)`))
-
-s=pack(' %s ==> %s"%(`s_data`,`s`,`unpack('qqqq',s_data[0],s_data[1],s_data[2],s_data[3])
-if s_data!=unpack('>qqqq',s):
- raise support.TestError("internal coherence error: %s ==> %s ==> %s"%(`s_data`,`s`,`unpack('>qqqq',s)`))
-
-#external unpack coherence check
-string_from_CPython='\x00\x00\x00\x00\x00\x01\x11p\xff\xff\xff\xff\xff\xfe\xc7\x80\xff\xff\xff\xff\xff\xff\xff\xfb\x00\x00\x00\x00\x00\x018\x80'
-if (70000,-80000,-5,80000)!=unpack('!Qqqq',string_from_CPython):
- raise support.TestError('Error unpacking from CPython !')
-
diff --git a/bugtests/test374.py b/bugtests/test374.py
deleted file mode 100644
index 6b2ecaaff..000000000
--- a/bugtests/test374.py
+++ /dev/null
@@ -1,19 +0,0 @@
-"""
-[ 631017 ] Private fields mismangled
-"""
-
-import support
-
-class _A:
- __value = 1
-
-class B(_A):
- _initial_value = 2
- def foo(self):
- assert self._A__value == 1
- assert self._initial_value == 2
-
-
-B().foo()
-
-#raise support.TestWarning('A test of TestWarning. It is not an error')
diff --git a/bugtests/test375.py b/bugtests/test375.py
deleted file mode 100644
index 6d5445103..000000000
--- a/bugtests/test375.py
+++ /dev/null
@@ -1,9 +0,0 @@
-"""
-[ 631017 ] Private fields mismangled
-"""
-
-import support
-
-support.compileJPythonc("test375c.py", output="test375.err",
- jar="test375.jar", core=1)
-support.runJava("test375c", classpath="test375.jar")
diff --git a/bugtests/test375c.py b/bugtests/test375c.py
deleted file mode 100644
index 6b2ecaaff..000000000
--- a/bugtests/test375c.py
+++ /dev/null
@@ -1,19 +0,0 @@
-"""
-[ 631017 ] Private fields mismangled
-"""
-
-import support
-
-class _A:
- __value = 1
-
-class B(_A):
- _initial_value = 2
- def foo(self):
- assert self._A__value == 1
- assert self._initial_value == 2
-
-
-B().foo()
-
-#raise support.TestWarning('A test of TestWarning. It is not an error')
diff --git a/bugtests/test376.py b/bugtests/test376.py
deleted file mode 100644
index 046e7a4f0..000000000
--- a/bugtests/test376.py
+++ /dev/null
@@ -1,8 +0,0 @@
-"""
-[ 631035 ] Negative repeat cause java exception.
-"""
-
-import support
-
-assert "0"*-1 == ""
-
diff --git a/bugtests/test377.py b/bugtests/test377.py
deleted file mode 100644
index 7d66c1901..000000000
--- a/bugtests/test377.py
+++ /dev/null
@@ -1,8 +0,0 @@
-"""
-[ 631047 ] %e formatting of float fails.
-"""
-
-import support
-
-assert '%.*e' % (0, float(1000)) == '1e+003'
-
diff --git a/bugtests/test378.py b/bugtests/test378.py
deleted file mode 100644
index ca4c0024c..000000000
--- a/bugtests/test378.py
+++ /dev/null
@@ -1,18 +0,0 @@
-"""
-[ 631430 ] read(-1) uses wrong fileposition.
-"""
-
-import support
-
-f = open("test378.out", "wb")
-f.write("123456789")
-f.close()
-
-f = open("test378.out")
-f.read(4)
-s = f.read();
-f.close();
-
-assert s == "56789"
-
-
diff --git a/bugtests/test379.py b/bugtests/test379.py
deleted file mode 100644
index d7bfebaad..000000000
--- a/bugtests/test379.py
+++ /dev/null
@@ -1,31 +0,0 @@
-"""
-test for
-[ 730156 ] java.lang.VerifyError with very simple Python source
-"""
-import support
-
-code = """
-def method():
- try:
- for dummy in [1,2,3]:
- try:
- return "result"
- except:
- pass
- finally:
- pass
-"""
-
-import java.lang
-
-try:
- c = compile(code,"","exec")
-except java.lang.VerifyError,e:
- raise support.TestWarning("try-for-try-finally still produces invalid bytecode")
-
-d = {}
-
-exec code in d
-
-if d['method']() != 'result':
- raise support.TestError("wrong result")
\ No newline at end of file
diff --git a/bugtests/test380.py b/bugtests/test380.py
deleted file mode 100644
index 5eefea08a..000000000
--- a/bugtests/test380.py
+++ /dev/null
@@ -1,44 +0,0 @@
-"""
-fixed broken id checks with pickle and copy that depends on id working correctly
-"""
-
-import support
-
-d = {}
-
-import java
-
-clash_id = java.lang.System.identityHashCode
-
-for i in xrange(100000):
- s = ['test',i]
- j = clash_id(s)
- if d.has_key(j):
- break
- d[j] = s
-
-s1 = s
-s0 = d[j]
-
-data = [s0,s1,s0]
-
-#print data
-
-import pickle
-import cPickle
-
-def check(ctxt,data0,data1):
- if data0 != data1:
- raise support.TestError,"data corrupted in %s because of id clashes: %s != %s" % (ctxt.__name__,data0,data1)
-
-def pik_test(pikmod,data):
- pik =pikmod.dumps(data,1)
- data1 = pikmod.loads(pik)
- check(pikmod,data,data1)
-
-pik_test(cPickle,data)
-pik_test(pickle,data)
-
-import copy
-
-check(copy.deepcopy,data,copy.deepcopy(data))
diff --git a/bugtests/test381.py b/bugtests/test381.py
deleted file mode 100644
index 953cd43ca..000000000
--- a/bugtests/test381.py
+++ /dev/null
@@ -1,72 +0,0 @@
-"""
-namespace (PyStringMap) deletion-confused insert bug
-"""
-
-#==============================================
-# we need some stuff to find a pair of keys
-# with the same initial index in hash table
-
-
-from java.lang.System import identityHashCode
-from java.lang import String
-
-
-def hashCode(key):
- return identityHashCode(String.intern(key))
-
-
-def scanKeys(base, r, sz, deep):
- for i in xrange(65,91): #chars 'A'-'Z'
- key = base+chr(i)
- #sz is hash table size
- if hashCode(key)%sz == r:
- break
- if deep:
- key = scanKeys(base, r, sz, deep-1)
- if key is not None:
- break
- return key
-
-
-# find a key with the same hash index as key1
-def findPairKey(key1, sz=7):
- #in empty PyStringMap hash table has size 7
- r=hashCode(key1)%sz
- base=""
- for deep in xrange(0,15):
- key = scanKeys(base, r, sz, deep)
- if key is not None: return key
-
-
-
-class AA: pass
-d = AA().__dict__
-
-
-# now d is an empty PyStringMap dict
-
-
-key1="key1"
-#find a pair key for key1
-key2 = findPairKey(key1)
-
-
-# key2 consists of upper case characters (by construction)
-# and always differs from key1
-#print "key1=",repr(key1)," key2=",repr(key2)
-
-
-d[key2] = "foo" #key2 occupies initial slot
-d[key1] = "value1" #key1 occupies next slot
-del d[key2] #initial slot is marked by ""
-d[key1] = "value2" #key1 replaces "" in the first
- #slot but not old key1 value!
-del d[key1]
-#we hope key1 is not in the dict any more...
-try:
- v=d[key1]
- #print "Oops! d[key1]=",repr(v) #Oops! Magically ressurected!
- raise support.TestError,"namespace deletion-confused insert bug"
-except KeyError:
- #print "OK"
- pass
\ No newline at end of file
diff --git a/bugtests/test382.py b/bugtests/test382.py
deleted file mode 100644
index cc0f4c1e3..000000000
--- a/bugtests/test382.py
+++ /dev/null
@@ -1,46 +0,0 @@
-"""
-catching frame wasn't captured in a traceback
-"""
-
-import sys
-
-def check(tb,expt_lines):
- assert tb.tb_frame is sys._getframe(1),"catching frame should be included"
- lines=[]
- while tb:
- lines.append(tb.tb_lineno)
- tb = tb.tb_next
- assert expt_lines==lines, "bogus line numbers: %s vs. expected %s" % (lines,expt_lines)
-
-def f():
- try:
- raise KeyError # 17
- except:
- raise
-
-try:
- f() # 22
-except:
- t,e,tb = sys.exc_info()
- check(tb,[22,17])
-
-try:
- f() # 28
-except KeyError,e:
- t,e,tb = sys.exc_info()
- check(tb,[28,17])
-
-try:
- 1/0 # 34
-except:
- t,e,tb = sys.exc_info()
- check(tb,[34])
-
-try:
- try:
- 1/0 # 41
- except:
- raise
-except:
- t,e,tb = sys.exc_info()
- check(tb,[41])
diff --git a/bugtests/test383.py b/bugtests/test383.py
deleted file mode 100644
index d900317f2..000000000
--- a/bugtests/test383.py
+++ /dev/null
@@ -1,9 +0,0 @@
-
-try:
- from java.util.regex import Pattern
- p = Pattern.compile("xxx")
- m = p.split("ABCDEFG")
-except ImportError, e:
- import support
- raise support.TestWarning("JVM version >= 1.4 needed to test PyString -> CharSequence")
-
diff --git a/bugtests/test384.py b/bugtests/test384.py
deleted file mode 100644
index 62ad38330..000000000
--- a/bugtests/test384.py
+++ /dev/null
@@ -1,18 +0,0 @@
-"""
-test for patch [ 1153003 ]
-"""
-
-import support
-import jarray
-import java
-from org.python.core import ArgParser, PyObject
-
-try:
- # test(1, arg1=2)
- args = jarray.array([1,2], PyObject)
- kwds = jarray.array(['arg1'], java.lang.String)
- ArgParser('test', args, kwds, 'arg1', 'arg2')
-except TypeError:
- pass
-else:
- raise support.TestError('Should raise a TypeError')
diff --git a/bugtests/test385.py b/bugtests/test385.py
deleted file mode 100644
index faae3b358..000000000
--- a/bugtests/test385.py
+++ /dev/null
@@ -1,25 +0,0 @@
-"""
-Try importing from a jar after sys.path.append(jar)
-
-This nails down a bug reported here:
- http://sourceforge.net/mailarchive/message.php?msg_id=14088259
-which only occurred on systems where java.io.File.separatorChar is not a forward slash ('/')
-
-since - at the moment - jython modules hide java packages with the same name from import,
-use a unique java package name for the sake of this test
-"""
-
-import jarmaker
-import support
-import sys
-
-jarfn, package, clazz = jarmaker.mkjar()
-# append this jar file to sys.path
-sys.path.append(jarfn)
-
-# try to import the class
-importStmt = "from %s import %s" % (package, clazz)
-try:
- exec(importStmt)
-finally:
- sys.path.remove(jarfn)
diff --git a/bugtests/test386.py b/bugtests/test386.py
deleted file mode 100644
index 062074378..000000000
--- a/bugtests/test386.py
+++ /dev/null
@@ -1,37 +0,0 @@
-"""
-Test the standalone starting (java -jar jython.jar some.py)
-"""
-
-import support
-import sys
-import os
-
-import support_config as cfg
-
-from java.io import File
-
-TESTDIR = "test386jar"
-JYTHON_DEV_JAR = "jython-dev.jar"
-TEST_PY_NAME = TESTDIR +"/test386called.py"
-
-def checkTestDir():
- if not os.path.exists(TESTDIR):
- raise AssertionError, TESTDIR + " does not exist"
- if not os.path.exists(TEST_PY_NAME):
- raise AssertionError, TEST_PY_NAME + " does not exist"
-
-# create a jython standalone jar file:
-# add the contents of jython-dev.jar and /Lib files to a new jython-dev.jar
-def mkjar():
- jarFile = File(TESTDIR, JYTHON_DEV_JAR)
- jarPacker = support.JarPacker(jarFile)
- jarPacker.addJarFile(File(cfg.jython_home + "/%s" % JYTHON_DEV_JAR))
- jarPacker.addDirectory(File(cfg.jython_home + "/Lib"))
- jarPacker.close()
- return jarFile
-
-
-checkTestDir()
-mkjar()
-jarFileName = "%s/%s" % (TESTDIR, JYTHON_DEV_JAR)
-support.runJavaJar(jarFileName, TEST_PY_NAME)
\ No newline at end of file
diff --git a/bugtests/test386jar/test386called.py b/bugtests/test386jar/test386called.py
deleted file mode 100644
index 8dcbee2be..000000000
--- a/bugtests/test386jar/test386called.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# make sure we are in 'standalone' mode, without package scan
-import sys
-skipName = "python.cachedir.skip"
-if not sys.registry.containsKey(skipName):
- raise AssertionError, skipName + " is missing"
-if not "true" == sys.registry.getProperty(skipName):
- raise AssertionError, skipName + " is not true"
-
-# import a non-builtin module which is not imported by default on startup
-# this verifies that /Lib .py files can be imported
-# this fixes bug [ 1194650 ]
-import getopt
-
-# an early java import # (only works since java.util is an already loaded package)
-from java import util
-util # used to give a NameError
-
-# import java specific py modules
-import os
-
-# now do some java imports which previously failed without a package scan
-# this (most of the time) solves the famous 'no module named java' problem
-import java # (only works since java is an already loaded package)
-import java.lang # (only works since java.lang is an already loaded package)
-
-# explicit imports
-from java.math import BigDecimal
-from java.math import BigDecimal, BigInteger
-from java.lang.reflect import Method
-
-# verify the self healing
-try:
- # assume package javax.imageio.event was never touched before
- import javax.imageio.event
- raise AssertionError, "ImportError expected when executing 'import javax.imageio.event'"
-except ImportError:
- pass
-from javax.imageio.event import IIOReadProgressListener
-
-# importing this twice was a problem
-from org.python.core import PySystemState
-from org.python.core import PySystemState
-
-# verify explicit imports of the form 'import java.net.URL'
-import javax.security.auth.Policy
-javax
-javax.security
-javax.security.auth
-javax.security.auth.Policy
diff --git a/bugtests/test387.py b/bugtests/test387.py
deleted file mode 100644
index ede638bc0..000000000
--- a/bugtests/test387.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import support
-
-import test387p.test387m
-
-import sys
-
-if not 'test387p.difflib' in sys.modules:
- raise support.TestError, 'Cached module for sibling module import miss should exist in sys.modules'
-if not sys.modules['test387p.difflib'] is None:
- raise support.TestError, 'Cached module for sibling module import miss should be None in sys.modules'
diff --git a/bugtests/test387p/__init__.py b/bugtests/test387p/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/bugtests/test387p/test387m.py b/bugtests/test387p/test387m.py
deleted file mode 100644
index 3c9179c47..000000000
--- a/bugtests/test387p/test387m.py
+++ /dev/null
@@ -1 +0,0 @@
-import difflib
diff --git a/bugtests/test388.py b/bugtests/test388.py
deleted file mode 100644
index c4ce72a50..000000000
--- a/bugtests/test388.py
+++ /dev/null
@@ -1,14 +0,0 @@
-'''
-This checks that Python imports precedes the loading of Java directories from the
-classpath.
-
-Reported in bug 1421812.
-'''
-import support
-
-import test388m
-
-if not hasattr(test388m, 'x'):
- raise support.TestError, 'Python modules should be imported before directories for Java'
-
-
diff --git a/bugtests/test388m.py b/bugtests/test388m.py
deleted file mode 100644
index 0553d3a2e..000000000
--- a/bugtests/test388m.py
+++ /dev/null
@@ -1 +0,0 @@
-x = 7
diff --git a/bugtests/test390.py b/bugtests/test390.py
deleted file mode 100644
index 3ac4f7d59..000000000
--- a/bugtests/test390.py
+++ /dev/null
@@ -1,19 +0,0 @@
-'''
-Checks that exceptions imported in import * will catch thrown subclass excetions
-in an except statement.
-
-Reported in bugs 1531644 and 1269872.
-'''
-import support
-import sys
-
-from java.net import Socket
-from java.io import *
-
-try:
- # Do a connection that will yield a ECONNREFUSED -> ConnectException.
- conn = Socket('localhost', 8342)
-except IOException, e:
- pass
-except:
- raise support.TestError, "A %s was raised which is an IOExcption but except IOException above didn't catch it" % sys.exc_info()[0]
diff --git a/bugtests/test392.py b/bugtests/test392.py
deleted file mode 100644
index 6160f0dc7..000000000
--- a/bugtests/test392.py
+++ /dev/null
@@ -1,24 +0,0 @@
-'''
-From bug #1284344
-import a compiled module moved to a new location and check that its __file__
-matches its new spot.
-'''
-
-fname = 'test392m.py'
-
-open(fname, 'w').close()#touch!
-
-import test392m
-del test392m
-import os
-compiledName = 'test392m$py.class'
-os.rename(compiledName, 'test392LibDir/%s' % compiledName)
-os.remove(fname)
-
-import support
-ret = support.runJython('test392importer.py', expectError=True)
-if ret == 1:
- raise support.TestError, '__file__ on test392m reflected where it was compiled, not where it was imported.'
-elif ret != 0:
- raise support.TestError, 'running test392importer.py exited with an unexpected code'
-
diff --git a/bugtests/test392LibDir/__init__.py b/bugtests/test392LibDir/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/bugtests/test392importer.py b/bugtests/test392importer.py
deleted file mode 100644
index ef650a976..000000000
--- a/bugtests/test392importer.py
+++ /dev/null
@@ -1,5 +0,0 @@
-import sys
-sys.path.append('test392LibDir')
-import test392m
-
-assert 'test392LibDir' in test392m.__file__, "test392m.__file__ doesn't contain test392LibDir, the directory it's in"
diff --git a/bugtests/test393.py b/bugtests/test393.py
deleted file mode 100644
index 2e3f33e8f..000000000
--- a/bugtests/test393.py
+++ /dev/null
@@ -1,6 +0,0 @@
-'''
-From bug #1548501
-Check that __file__ is set on a file run directly from jython.
-'''
-import support
-support.runJython('test393m.py')
diff --git a/bugtests/test393m.py b/bugtests/test393m.py
deleted file mode 100644
index 42750504c..000000000
--- a/bugtests/test393m.py
+++ /dev/null
@@ -1 +0,0 @@
-assert __file__ == "test393m.py"
diff --git a/bugtests/test394.py b/bugtests/test394.py
deleted file mode 100644
index 0bf6a89ad..000000000
--- a/bugtests/test394.py
+++ /dev/null
@@ -1,78 +0,0 @@
-"""
-Test standalone starting,
-where the manifest of a .jar refers to jython.jar
-
-
-This used to give an error importing site, as follows:
-
-error importing site
-Traceback (innermost last):
- File "C:\workspace\jython\bugtests\test394jar\jython-dev.jar\Lib/site.py", line 210, in ?
-TypeError: unsupported operand type(s) for +: 'NoneType' and 'str'
-Traceback (innermost last):
- File "C:/workspace/jython/bugtests/test394.py", line 71, in ?
- File "C:\workspace\jython\bugtests\support.py", line 100, in runJavaJar
- File "C:\workspace\jython\bugtests\support.py", line 65, in execCmd
-TestError: cmd /C "C:/Programme/Java/jdk1.5.0_09/bin/java.exe -jar test394jar/run.jar " failed with -1
-
-"""
-
-import support
-import sys
-import os
-
-import support_config as cfg
-
-from java.io import File
-
-TESTDIR = "test394jar"
-JYTHON_DEV_JAR = "jython-dev.jar"
-RUN_JAR = "run.jar"
-TEST_PY_NAME = TESTDIR +"/test394called.py"
-CLAZZ = "Runner"
-MANIFEST = "MANIFEST.MF"
-
-def checkTestDir():
- if not os.path.exists(TESTDIR):
- raise AssertionError, TESTDIR + " does not exist"
- if not os.path.exists(TEST_PY_NAME):
- raise AssertionError, TEST_PY_NAME + " does not exist"
- javaFileName = TESTDIR + "/" + CLAZZ + ".java"
- if not os.path.exists(javaFileName):
- raise AssertionError, javaFileName + " does not exist"
- manifestFileName = TESTDIR + "/" + MANIFEST
- if not os.path.exists(manifestFileName):
- raise AssertionError, manifestFileName + " does not exist"
-
-
-# create a jython standalone jar file:
-# add the contents of jython-dev.jar and /Lib files to a new jython-dev.jar
-def mkJythonJar():
- jarFile = File(TESTDIR, JYTHON_DEV_JAR)
- jarPacker = support.JarPacker(jarFile)
- jarPacker.addJarFile(File(cfg.jython_home + "/%s" % JYTHON_DEV_JAR))
- jarPacker.addDirectory(File(cfg.jython_home + "/Lib"))
- jarPacker.close()
- return jarFile
-
-# make a java class calling jython main
-def mkJavaClass():
- support.compileJava("%s/%s.java" % (TESTDIR, CLAZZ))
-
-# create a runnable jar file with a manifest referring to jython-dev.jar
-def mkRunJar():
- jarFile = File(TESTDIR, RUN_JAR)
- manifestFile = File(TESTDIR, MANIFEST)
- jarPacker = support.JarPacker(jarFile)
- jarPacker.addManifestFile(manifestFile)
- jarPacker.addFile(File(TESTDIR, CLAZZ+".class"), TESTDIR)
- jarPacker.close()
-
-
-
-checkTestDir()
-mkJythonJar()
-mkJavaClass()
-mkRunJar()
-jarFileName = "%s/%s" % (TESTDIR, RUN_JAR)
-support.runJavaJar(jarFileName)
\ No newline at end of file
diff --git a/bugtests/test394jar/MANIFEST.MF b/bugtests/test394jar/MANIFEST.MF
deleted file mode 100644
index 2ae770cf8..000000000
--- a/bugtests/test394jar/MANIFEST.MF
+++ /dev/null
@@ -1,4 +0,0 @@
-Manifest-Version: 1.0
-Class-Path: . jython-dev.jar
-Main-Class: test394jar.Runner
-
diff --git a/bugtests/test394jar/Runner.java b/bugtests/test394jar/Runner.java
deleted file mode 100644
index df1c28cbe..000000000
--- a/bugtests/test394jar/Runner.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package test394jar;
-
-import org.python.util.jython;
-
-public class Runner {
-
- public static void main(String args[]) {
- jython.main(new String[] { "test394jar/test394called.py" });
- }
-
-}
diff --git a/bugtests/test394jar/test394called.py b/bugtests/test394jar/test394called.py
deleted file mode 100644
index d25d49e0f..000000000
--- a/bugtests/test394jar/test394called.py
+++ /dev/null
@@ -1 +0,0 @@
-a = 1
\ No newline at end of file
diff --git a/bugtests/test397.py b/bugtests/test397.py
deleted file mode 100644
index 031f1f2bd..000000000
--- a/bugtests/test397.py
+++ /dev/null
@@ -1,41 +0,0 @@
-'''
-Checks that files are closed in three situations:
-1. Garbage collection/finalization close
-2. Regular close
-3. Shutdown time, close out open PyFiles
-'''
-
-import os
-import support
-
-from java.io import File
-from java.lang import System, Thread
-
-def check(fn='test.txt'):
- f = File(fn)
- if not f.exists():
- raise support.TestError('"%s" should exist' % fn)
- if not f.length():
- raise support.TestError('"%s" should have contents' % fn)
- os.remove(fn)
-
-
-open("garbagecollected", "w").write("test")
-
-#Wait up to 2 seconds for garbage collected to disappear
-System.gc()
-for i in range(10):
- if not os.path.exists('garbagecollected'):
- break
- Thread.sleep(200)
-
-check("garbagecollected")
-
-f = open("normalclose", "w")
-f.write("test")
-f.close()
-check("normalclose")
-
-#test397m writes to "shutdown" and exits
-support.runJython('test397m.py')
-check('shutdown')
diff --git a/bugtests/test397m.py b/bugtests/test397m.py
deleted file mode 100644
index 3d8547b98..000000000
--- a/bugtests/test397m.py
+++ /dev/null
@@ -1 +0,0 @@
-open("shutdown", "w").write('exiting')
diff --git a/bugtests/test398.py b/bugtests/test398.py
deleted file mode 100644
index 3f626d1a9..000000000
--- a/bugtests/test398.py
+++ /dev/null
@@ -1,71 +0,0 @@
-"""
-test fix for bug #1642285
-
-Try importing from a jar which contains a .class file which is completely empty (0bytes).
-Make sure that the bad class file is skipped while good class file is processed.
-
-Although this is an aberrant .class file, it has been seen in the wild (see bug report, found in a weblogic
-jar).
-
-"""
-
-import support
-import sys
-import os
-
-from java.io import File
-from java.lang import String
-from java.util import Properties
-from org.python.core.packagecache import SysPackageManager
-
-PACKAGE = "test398javapackage"
-CACHEDIR = "test398cache"
-BAD_CLAZZ = "test398j1"
-GOOD_CLAZZ = "test398j2"
-JARDIR = "test398jar"
-JARFILE = "test398.jar"
-GOOD_CLAZZ_FILE = File(PACKAGE, "%s.class" % GOOD_CLAZZ) # java.io.File
-BAD_CLAZZ_FILE = File(PACKAGE, "%s.class" % BAD_CLAZZ) # java.io.File
-
-def mkdir(dir):
- if not os.path.exists(dir):
- os.mkdir(dir)
-
-def mkjavaclass():
- mkdir(PACKAGE)
- f = open("%s/%s.java" % (PACKAGE, GOOD_CLAZZ), "w")
- f.write("""
-package %s;
-public class %s {
-}
-""" % (PACKAGE, GOOD_CLAZZ))
- f.close()
- support.compileJava("%s/%s.java" % (PACKAGE, GOOD_CLAZZ))
-
-def mkbadclass():
- mkdir(PACKAGE)
- f = open("%s/%s.class" % (PACKAGE, BAD_CLAZZ), "w")
- f.close()
-
-def mkjar():
- mkdir(JARDIR)
- jarFile = File(JARDIR, JARFILE)
- jarPacker = support.JarPacker(jarFile, bufsize=128)
- jarPacker.addFile(GOOD_CLAZZ_FILE, parentDirName=PACKAGE)
- jarPacker.addFile(BAD_CLAZZ_FILE, parentDirName=PACKAGE)
- jarPacker.close()
- return jarFile
-
-def mkprops():
- props = Properties()
- props.setProperty("java.ext.dirs", String(JARDIR));
- props.setProperty("python.security.respectJavaAccessibility", String("true"));
- return props
-
-# create a .jar file containing a .class file
-mkjavaclass()
-mkbadclass()
-jarFile = mkjar()
-props = mkprops()
-man = SysPackageManager(File(CACHEDIR, "packages"), props)
-assert os.path.exists(os.path.join(CACHEDIR, "packages", "test398.pkc"))
diff --git a/bugtests/test400.py b/bugtests/test400.py
deleted file mode 100644
index 1993c657a..000000000
--- a/bugtests/test400.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import support
-
-try:
- import x
- raise support.TestError, "x shouldn't be on sys.path until after this"
-except:
- pass
-import sys
-sys.path.append('test400')
-import x
diff --git a/bugtests/test400/x/__init__.py b/bugtests/test400/x/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/bugtests/test402.py b/bugtests/test402.py
deleted file mode 100644
index 8940bec3d..000000000
--- a/bugtests/test402.py
+++ /dev/null
@@ -1,24 +0,0 @@
-'''
-
-test402m adds a jar file to sys.path and imports a package from it. The first
-run ensures that, by default, package scanning is enabled for jars added to
-sys.path. The second run turns off package scanning, so it checks that the
-package is unimportable without the scan. Finally, we run test402n which adds
-the same jar to its sys.path and imports a fully qualified class from it. We
-run it with package scanning off to make sure that even without package
-scanning, jars are correctly added to sys.path and fully qualified class
-imports work on them.
-
-'''
-
-import support
-import jarmaker
-
-jarmaker.mkjar()
-
-support.runJython('test402m.py')
-ret = support.runJython('test402m.py', error='test402.err',
- javaargs='-Dpython.cachedir.skip=true', expectError=1)
-if ret == 0:
- raise support.TestError('Successfully imported a package from a jar on sys.path without caching!')
-support.runJython('test402n.py', javaargs='-Dpython.cachedir.skip=true')
diff --git a/bugtests/test402m.py b/bugtests/test402m.py
deleted file mode 100644
index 615b4ac69..000000000
--- a/bugtests/test402m.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import sys
-sys.path.append('simplejar/simple.jar')
-
-import javapackage
diff --git a/bugtests/test402n.py b/bugtests/test402n.py
deleted file mode 100644
index 750c84c56..000000000
--- a/bugtests/test402n.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import sys
-sys.path.append('simplejar/simple.jar')
-
-from javapackage import JavaClass
diff --git a/build-tools/build-tools.gradle b/build-tools/build-tools.gradle
new file mode 100644
index 000000000..7836e2900
--- /dev/null
+++ b/build-tools/build-tools.gradle
@@ -0,0 +1,27 @@
+/*
+ * build-tools.gradle
+ *
+ * Build the tools on which the Jython Project depends at *build time*.
+ * This is structured as an independent build. It is included in the main
+ * Jython build through its settings file, and its products aa dependencies.
+ */
+
+plugins {
+ id 'java-library'
+}
+
+description = 'Tools for building Jython'
+group = 'org.python'
+//version = '0.4.0-SNAPSHOT'
+
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+}
+
+dependencies {
+ testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.+'
+ testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.+'
+}
+
diff --git a/build-tools/python/lib/compile_examples.py b/build-tools/python/lib/compile_examples.py
new file mode 100644
index 000000000..27ebf11e0
--- /dev/null
+++ b/build-tools/python/lib/compile_examples.py
@@ -0,0 +1,169 @@
+# Reading compiled Python files
+
+import sys, os.path
+import marshal, py_compile, dis
+
+# Normally you don't get a .pyc file if you just run a program.
+# You do get a .pyc file from compiling a module.
+# It is written in ./__pycache__ and called NAME.cpython-311.pyc
+
+CACHE = '__pycache__'
+COMPILER = 'cpython-311'
+
+
+HELP = """Command: compile_examples srcdir dstdir
+
+ where:
+ srcdir is the root of the Python examples in the project source
+ typically ./src/test/pythonExample
+ dstdir is the root of the Python examples in the build tree
+ typically ./build/generated/sorces/pythonExample/test
+ """
+
+
+
+def getcode(filename):
+ "Read a compiled file and return the code object"
+ with open(filename, 'rb') as f:
+ # Skip header. See run_pyc_file() in pythonrun.c
+ f.read(16)
+ return marshal.load(f)
+
+
+def getobj(filename):
+ "Read an object from a file"
+ with open(filename, 'rb') as f:
+ return marshal.load(f)
+
+
+def filetime(path_elements, name_elements):
+ """Compose a file path and report when last modified
+
+ path_elements: file path elements (will be os.path.join'd)
+ name_elements: file name elements (will be '.'.join'd)
+
+ The arguments are used to locate a file (which need not exist)
+ and the function returns the name (always) and the last modified
+ time (or zero if the file does not exist).
+ """
+
+ file = os.path.join(*path_elements, '.'.join(name_elements))
+ try:
+ time = os.path.getmtime(file)
+ except OSError:
+ time = 0
+ #print(f"{file:>40s}: {time:15.3f}")
+ return file, time
+
+
+def copy(srcfile, dstfile):
+ "Copy one text file to another"
+ print(f" Copy: {os.path.basename(srcfile)}")
+ with open(srcfile, 'rt', encoding='utf-8') as s:
+ ensure_dir(os.path.dirname(dstfile))
+ with open(dstfile, 'wt', encoding='utf-8') as d:
+ for line in s:
+ d.write(line)
+
+
+def execute(pycfile, varfile, disfile):
+ "Execute a program and save the local variables"
+ print(f" Generate: {os.path.basename(disfile)}")
+ co = getcode(pycfile)
+ with open(disfile, 'wt', encoding='utf-8') as f:
+ # Dumps code blocks of nested functions
+ dis.dis(co, file=f)
+ print(f" Generate: {os.path.basename(varfile)}")
+ gbl = dict()
+ exec(co, gbl)
+ # Remove items forced in by exec
+ del gbl['__builtins__']
+ # try:
+ # print(" ", list(gbl.keys()))
+ # except UnicodeEncodeError:
+ # pass
+ with open(varfile, 'wb') as f:
+ marshal.dump(gbl, f)
+
+
+def generate(reldir, name, source, generated):
+ """Generate test reldir/name.py and results
+
+ reldir: the relative directory path (from source/generated)
+ name: just the name part of the Python file
+ source: directory of the source files
+ generated: directory of the compiled/generated files
+ """
+ srcfile, srctime = filetime([source, reldir], [name, 'py'])
+ #print(f" {name}.py")
+ #print(f" source: {srctime:15.3f}")
+
+ dstfile, dsttime = filetime([generated, reldir], [name, 'py'])
+ #print(f" build: {dsttime:15.3f}")
+
+ pycfile, pyctime = filetime([generated, reldir, CACHE],
+ [name, COMPILER, 'pyc'])
+ #print(f" .pyc: {pyctime:15.3f}")
+
+ varfile, vartime = filetime([generated, reldir, CACHE],
+ [name, COMPILER, 'var'])
+ #print(f" .var: {vartime:15.3f}")
+
+ disfile, distime = filetime([generated, reldir, CACHE],
+ [name, COMPILER, 'dis'])
+ #print(f" .dis: {distime:15.3f}")
+
+ if dsttime < srctime:
+ # Copy, compile, run and store
+ copy(srcfile, dstfile)
+ dsttime = srctime
+
+ if pyctime < dsttime:
+ # Compile, run and store
+ print(f" Compile: {os.path.basename(pycfile)}")
+ py_compile.compile(dstfile)
+ pyctime = os.path.getmtime(pycfile)
+
+ if vartime < pyctime or distime < pyctime:
+ # Run and store
+ execute(pycfile, varfile, disfile)
+
+
+def ensure_dir(d):
+ if not os.path.exists(d):
+ os.makedirs(d)
+ if not (ok:=os.path.isdir(d)):
+ print(f"Not a directory '{d}'", file=sys.stderr)
+ return ok
+
+
+def main(source, generated):
+
+ for dirpath, _, files in os.walk(source):
+ #print(f"{dirpath}:")
+ reldir = os.path.relpath(dirpath, source)
+ for file in files:
+ parts = file.rsplit('.', 1)
+ if len(parts) > 1 and parts[1] == "py":
+ name = parts[0]
+ #print(f" {name}:")
+ generate(reldir, name, source, generated)
+
+def show_help():
+ print(HELP, file=sys.stderr)
+
+# --------------------------------------------------------------------
+
+if len(sys.argv) == 3:
+ source, generated = sys.argv[1:]
+ if ensure_dir(source) and ensure_dir(generated):
+ cwd = os.getcwd()
+ source = os.path.relpath(source)
+ generated = os.path.relpath(generated)
+ main(source, generated)
+ else:
+ show_help()
+else:
+ show_help()
+
+
diff --git a/build-tools/python/lib/core/PyFloat.py b/build-tools/python/lib/core/PyFloat.py
new file mode 100644
index 000000000..704bf233f
--- /dev/null
+++ b/build-tools/python/lib/core/PyFloat.py
@@ -0,0 +1,362 @@
+# PyFloat.py: A generator for Java files that define the Python float
+
+# Copyright (c)2021 Jython Developers.
+# Licensed to PSF under a contributor agreement.
+
+# This generator writes PyFloatMethods.java and PyFloatBinops.java .
+
+from dataclasses import dataclass
+from typing import Callable
+
+from . import ImplementationGenerator, TypeInfo, WorkingType, OpInfo
+
+@dataclass
+class FloatTypeInfo(TypeInfo):
+ "Information about a type and templates for conversion to float types"
+ # There is a template (a function) to generate an expression
+ # that converts *from* this type to each named Java type that may be
+ # a "working type" when implementing an operation.
+
+ # That's only 'double', but conceivably primitive 'float' later.
+
+ # Template for expression that converts to primitive double
+ as_double: str = None
+
+
+# Useful in cases where an argument is already the right type
+itself = lambda x: x
+
+# A constant FloatTypeInfo for each argument type that we might have to
+# convert to a "working type" when implementing an operation.
+# Arguments are: name, min_working_type,
+# as_double
+PY_FLOAT_CLASS = FloatTypeInfo('PyFloat', WorkingType.DOUBLE,
+ lambda x: f'{x}.value')
+OBJECT_CLASS = FloatTypeInfo('Object', WorkingType.OBJECT,
+ lambda x: f'toDouble({x})')
+DOUBLE_CLASS = FloatTypeInfo('Double', WorkingType.DOUBLE,
+ itself)
+
+# Accepted types that may appear as the other operand in binary
+# operations specialised to both types.
+PY_LONG_CLASS = FloatTypeInfo('PyLong', WorkingType.DOUBLE,
+ lambda x: f'convertToDouble({x}.value)')
+BIG_INTEGER_CLASS = FloatTypeInfo('BigInteger', WorkingType.DOUBLE,
+ lambda x: f'convertToDouble({x})')
+INTEGER_CLASS = FloatTypeInfo('Integer', WorkingType.DOUBLE,
+ lambda x: f'{x}.doubleValue()')
+BOOLEAN_CLASS = FloatTypeInfo('Boolean', WorkingType.DOUBLE,
+ lambda x: f'({x} ? 1.0 : 0.0)')
+
+# A constant FloatTypeInfo for types appearing as return types only.
+#(No conversion to a working type is expected.)
+# convert to a "working type" when implementing an operation.
+PRIMITIVE_BOOLEAN = FloatTypeInfo('boolean', WorkingType.BOOLEAN)
+PRIMITIVE_INT = FloatTypeInfo('int', WorkingType.INT)
+PRIMITIVE_DOUBLE = FloatTypeInfo('double', WorkingType.DOUBLE)
+
+
+@dataclass
+class UnaryOpInfo(OpInfo):
+ # There is a template (a function) to generate an expression for
+ # each Java working type in which the result may be evaluated.
+
+ # That's only 'double', but conceivably primitive 'float' later.
+
+ # Template for when the working type is Java double
+ double_op: Callable
+
+
+@dataclass
+class BinaryOpInfo(OpInfo):
+ # There is a template (a function) to generate the body
+ body_method: Callable
+
+ # There is a template (a function) to generate an expression for
+ # each Java working type in which the result may be evaluated.
+ # That's only 'double', but conceivably primitive 'float' later.
+
+ # Template for when the working type is Java double
+ double_op: Callable
+
+ # Also create class-specific binop specialisations
+ class_specific: bool = False
+
+
+def unary_method(op:UnaryOpInfo, t:FloatTypeInfo):
+ "Template generating the body of a unary operation."
+ # Decide the width at which to work with this type and op
+ iw = max(op.min_working_type.value, t.min_working_type.value)
+ w = WorkingType(iw)
+ if w == WorkingType.DOUBLE:
+ return _unary_method_double(op, t)
+ else:
+ raise ValueError(
+ f"Cannot make method body for {op.name} and {w}")
+
+def _unary_method_double(op:UnaryOpInfo, t:FloatTypeInfo):
+ "Template for unary methods when the working type is DOUBLE"
+ return f'''
+ return {op.double_op(t.as_double("self"))};
+ '''
+
+
+def binary_floatmethod(op:BinaryOpInfo,
+ t1:FloatTypeInfo, n1,
+ t2:FloatTypeInfo, n2):
+ """Template for a binary operation with float result.
+
+ Argument coercions are made according to their static type then
+ the operation is applied which must yield a result in the working
+ type. This is only appropriate where the return from the generated
+ method should be a Python float (e.g. not comparisons, __divmod__).
+ """
+ # Decide the width at which to work with these types and op
+ iw = max(op.min_working_type.value,
+ t1.min_working_type.value,
+ t2.min_working_type.value)
+ w = WorkingType(iw)
+ if w == WorkingType.DOUBLE:
+ return _binary_floatmethod_double(op, t1, n1, t2, n2)
+ elif w == WorkingType.OBJECT:
+ return _binary_floatmethod_obj(op, t1, n1, t2, n2)
+ else:
+ raise ValueError(
+ f"Cannot make method body for {op.name} and {w}")
+
+def _binary_floatmethod_double(op:BinaryOpInfo,
+ t1:FloatTypeInfo, n1,
+ t2:FloatTypeInfo, n2):
+ "Template for binary float methods when the working type is DOUBLE"
+ return f'''
+ return {op.double_op(t1.as_double(n1), t2.as_double(n2))};
+ '''
+
+def _binary_floatmethod_obj(op:BinaryOpInfo,
+ t1:FloatTypeInfo, n1,
+ t2:FloatTypeInfo, n2):
+ "Template for binary float methods when the working type is OBJECT"
+ return f'''
+ try {{
+ return {op.double_op(t1.as_double(n1), t2.as_double(n2))};
+ }} catch (NoConversion e) {{
+ return Py.NotImplemented;
+ }}
+ '''
+
+
+def binary_method(op:BinaryOpInfo,
+ t1:FloatTypeInfo, n1,
+ t2:FloatTypeInfo, n2):
+ """Template for a binary operation with any result type.
+
+ Argument coercions are made according to their static type then
+ the operation is applied and the result returned without further
+ processing."""
+ # Decide the width at which to work with these types and op
+ iw = max(op.min_working_type.value,
+ t1.min_working_type.value,
+ t2.min_working_type.value)
+ w = WorkingType(iw)
+ if w == WorkingType.DOUBLE:
+ return _binary_method_double(op, t1, n1, t2, n2)
+ elif w == WorkingType.OBJECT:
+ return _binary_method_obj(op, t1, n1, t2, n2)
+ else:
+ raise ValueError(
+ f"Cannot make method body for {op.name} and {w}")
+
+def _binary_method_double(op:BinaryOpInfo,
+ t1:FloatTypeInfo, n1,
+ t2:FloatTypeInfo, n2):
+ "Template for binary methods when the working type is DOUBLE"
+ return f'''
+ return {op.double_op(t1.as_double(n1), t2.as_double(n2))};
+ '''
+
+def _binary_method_obj(op:BinaryOpInfo,
+ t1:FloatTypeInfo, n1,
+ t2:FloatTypeInfo, n2):
+ "Template for binary methods when the working type is OBJECT"
+ return f'''
+ try {{
+ return {op.double_op(t1.as_double(n1), t2.as_double(n2))};
+ }} catch (NoConversion e) {{
+ return Py.NotImplemented;
+ }}
+ '''
+
+
+class PyFloatGenerator(ImplementationGenerator):
+
+ # The canonical and adopted implementations in PyFloat.java.
+ ACCEPTED_CLASSES = [PY_FLOAT_CLASS, DOUBLE_CLASS]
+
+ # These classes may occur as the second operand in binary
+ # operations. Order is not significant.
+ OPERAND_CLASSES = ACCEPTED_CLASSES + [
+ # XXX Consider *not* specialising ...
+ # Although PyLong and BigInteger are accepted operands, we
+ # decline to specialise, since the implementation would be
+ # equivalent to the one in PyFloatMethods.
+ PY_LONG_CLASS,
+ BIG_INTEGER_CLASS,
+ INTEGER_CLASS,
+ BOOLEAN_CLASS,
+ ]
+
+ # Operations may simply be codified as a return expression, since
+ # all operand types may be converted to primitive double.
+
+ UNARY_OPS = [
+ # Arguments are: name, return_type, min_working_type,
+ # double_op
+ UnaryOpInfo('__abs__', OBJECT_CLASS, WorkingType.DOUBLE,
+ lambda x: f'Math.abs({x})'),
+ UnaryOpInfo('__neg__', OBJECT_CLASS, WorkingType.DOUBLE,
+ lambda x: f'-{x}'),
+ UnaryOpInfo('__pos__', OBJECT_CLASS, WorkingType.DOUBLE,
+ lambda x: f'{x}'),
+ UnaryOpInfo('__bool__', PRIMITIVE_BOOLEAN, WorkingType.DOUBLE,
+ lambda x: f'{x} != 0.0'),
+ UnaryOpInfo('__hash__', PRIMITIVE_INT, WorkingType.DOUBLE,
+ lambda x: f'Double.hashCode({x})'),
+ ]
+ BINARY_OPS = [
+ # Arguments are: name, return_type, working_type,
+ # body_method,
+ # double_op,
+ # with_class_specific_binops
+ BinaryOpInfo('__add__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_floatmethod,
+ lambda x, y: f'{x} + {y}',
+ True),
+ BinaryOpInfo('__radd__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_floatmethod,
+ lambda x, y: f'{y} + {x}',
+ True),
+ BinaryOpInfo('__sub__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_floatmethod,
+ lambda x, y: f'{x} - {y}',
+ True),
+ BinaryOpInfo('__rsub__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_floatmethod,
+ lambda x, y: f'{y} - {x}',
+ True),
+ BinaryOpInfo('__mul__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_floatmethod,
+ lambda x, y: f'{x} * {y}',
+ True),
+ BinaryOpInfo('__rmul__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_floatmethod,
+ lambda x, y: f'{y} * {x}',
+ True),
+
+ BinaryOpInfo('__truediv__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_floatmethod,
+ lambda x, y: f'{x} / nonzero({y})',
+ True),
+ BinaryOpInfo('__rtruediv__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_floatmethod,
+ lambda x, y: f'{y} / nonzero({x})',
+ True),
+
+ BinaryOpInfo('__floordiv__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_floatmethod,
+ lambda x, y: f'floordiv({x}, {y})',
+ False),
+ BinaryOpInfo('__rfloordiv__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_floatmethod,
+ lambda x, y: f'floordiv({y}, {x})',
+ False),
+ BinaryOpInfo('__mod__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_floatmethod,
+ lambda x, y: f'mod({x}, {y})',
+ False),
+ BinaryOpInfo('__rmod__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_floatmethod,
+ lambda x, y: f'mod({y}, {x})',
+ False),
+
+ BinaryOpInfo('__divmod__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_method,
+ lambda x, y: f'divmod({x}, {y})',
+ False),
+ BinaryOpInfo('__rdivmod__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_method,
+ lambda x, y: f'divmod({y}, {x})',
+ False),
+
+ BinaryOpInfo('__lt__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_method,
+ lambda x, y: f'{x} < {y}'),
+ BinaryOpInfo('__le__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_method,
+ lambda x, y: f'{x} <= {y}'),
+ BinaryOpInfo('__eq__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_method,
+ lambda x, y: f'{x} == {y}'),
+ BinaryOpInfo('__ne__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_method,
+ lambda x, y: f'{x} != {y}'),
+ BinaryOpInfo('__gt__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_method,
+ lambda x, y: f'{x} > {y}'),
+ BinaryOpInfo('__ge__', OBJECT_CLASS, WorkingType.DOUBLE,
+ binary_method,
+ lambda x, y: f'{x} >= {y}'),
+ ]
+
+ # Emit methods selectable by a single type
+ def special_methods(self, e):
+
+ # Emit the unary operations
+ for op in self.UNARY_OPS:
+ self.emit_heading(e, op.name)
+ for t in self.ACCEPTED_CLASSES:
+ self.special_unary(e, op, t)
+
+ # Emit the binary operations op(T, Object)
+ for op in self.BINARY_OPS:
+ self.emit_heading(e, op.name)
+ for vt in self.ACCEPTED_CLASSES:
+ self.special_binary(e, op, vt, OBJECT_CLASS)
+
+ # Emit methods selectable by a pair of types (for call sites)
+ def special_binops(self, e):
+
+ # Emit the binary operations and comparisons
+ for op in self.BINARY_OPS:
+ if op.class_specific:
+ self.emit_heading(e, op.name)
+ for vt in self.ACCEPTED_CLASSES:
+ for wt in self.OPERAND_CLASSES:
+ self.special_binary(e, op, vt, wt)
+
+ def special_unary(self, e, op:UnaryOpInfo, t):
+ e.emit('static ').emit(op.return_type.name).emit(' ')
+ e.emit(op.name).emit('(').emit(t.name).emit(' self) {')
+ with e.indentation():
+ method = unary_method(op, t)
+ method = self.left_justify(method)
+ e.emit_lines(method)
+ e.emit_line('}').emit_line()
+
+ # Emit one binary operation, for example:
+ # private static Object __add__(Double v, Integer w) {
+ # return v.doubleValue() + w.doubleValue();
+ # }
+ def special_binary(self, e, op:BinaryOpInfo, t1, t2):
+ reflected = op.name.startswith('__r') and \
+ op.name not in ("__rshift__", "__round__", "__repr__")
+ n1, n2 = 'vw' if not reflected else 'wv'
+ e.emit('static ').emit(op.return_type.name).emit(' ')
+ e.emit(op.name).emit('(')
+ e.emit(t1.name).emit(' ').emit(n1).emit(', ')
+ e.emit(t2.name).emit(' ').emit(n2).emit(') {')
+ with e.indentation():
+ method = op.body_method(op, t1, n1, t2, n2)
+ method = self.left_justify(method)
+ e.emit_lines(method)
+ e.emit_line('}').emit_line()
+
diff --git a/build-tools/python/lib/core/PyLong.py b/build-tools/python/lib/core/PyLong.py
new file mode 100644
index 000000000..8ef1b6f03
--- /dev/null
+++ b/build-tools/python/lib/core/PyLong.py
@@ -0,0 +1,517 @@
+# PyLong.py: A generator for Java files that define the Python int
+
+# Copyright (c)2021 Jython Developers.
+# Licensed to PSF under a contributor agreement.
+
+# This generator writes PyLongMethods.java and PyLongBinops.java .
+
+from dataclasses import dataclass
+from typing import Callable
+
+from . import ImplementationGenerator, TypeInfo, WorkingType, OpInfo
+
+
+@dataclass
+class IntTypeInfo(TypeInfo):
+ "Information about a type and templates for conversion to int types"
+ # There is a template (a function) to generate an expression
+ # that converts *from* this type to each named Java type that may be
+ # a "working type" when implementing an operation.
+
+ # Template for expression that converts to BigInteger
+ as_big: Callable = None
+ # Template for expression that converts to primitive Java long
+ as_long: Callable = None
+ # Template for expression that converts to primitive Java int
+ as_int: Callable = None
+
+
+# Useful in cases where an argument is already the right type
+itself = lambda x: x
+
+# A constant IntTypeInfo for each argument type that we might have to
+# convert to a "working type" when implementing an operation.
+# Arguments are: name, min_working_type,
+# as_big, as_long, as_int
+PY_LONG_CLASS = IntTypeInfo('PyLong', WorkingType.BIG,
+ lambda x: f'{x}.value')
+OBJECT_CLASS = IntTypeInfo('Object', WorkingType.OBJECT,
+ lambda x: f'toBig({x})')
+BIG_INTEGER_CLASS = IntTypeInfo('BigInteger', WorkingType.BIG,
+ itself)
+INTEGER_CLASS = IntTypeInfo('Integer', WorkingType.INT,
+ lambda x: f'BigInteger.valueOf({x})',
+ lambda x: f'((long) {x})',
+ itself)
+BOOLEAN_CLASS = IntTypeInfo('Boolean', WorkingType.INT,
+ lambda x: f'({x} ? ONE : ZERO)',
+ lambda x: f'({x} ? 1L : 0L)',
+ lambda x: f'({x} ? 1 : 0)')
+DOUBLE_CLASS = IntTypeInfo('Double', WorkingType.OBJECT)
+
+PRIMITIVE_BOOLEAN = IntTypeInfo('boolean', WorkingType.BOOLEAN)
+PRIMITIVE_INT = IntTypeInfo('int', WorkingType.INT)
+
+
+@dataclass
+class UnaryOpInfo(OpInfo):
+ # There is a template (a function) to generate an expression for
+ # each Java working type in which the result may be evaluated.
+
+ # Working type is Java BigInteger
+ big_op: Callable
+ # Working type is Java long
+ long_op: Callable
+ # Working type is Java int
+ int_op: Callable
+
+
+@dataclass
+class BinaryOpInfo(OpInfo):
+ # There is a template (a function) to generate the method body
+ body_method: Callable
+
+ # There is a template (a function) to generate an expression for
+ # each Java working type in which the result may be evaluated.
+
+ # Working type is Java BigInteger
+ big_op: Callable
+ # Working type is Java long
+ long_op: Callable
+ # Working type is Java int
+ int_op: Callable
+
+ # Also create class-specific binop specialisations
+ class_specific: bool = False
+
+
+def unary_method(op:UnaryOpInfo, t:IntTypeInfo):
+ "Template generating the body of a unary operation."
+ # Decide the width at which to work with this type and op
+ iw = max(op.min_working_type.value, t.min_working_type.value)
+ w = WorkingType(iw)
+ if w == WorkingType.INT:
+ return _unary_method_int(op, t)
+ elif w == WorkingType.LONG:
+ return _unary_method_long(op, t)
+ elif w == WorkingType.BIG:
+ return _unary_method_big(op, t)
+ else:
+ raise ValueError(
+ f"Cannot make method body for {op.name} and {w}")
+
+def _unary_method_int(op:UnaryOpInfo, t:IntTypeInfo):
+ "Template for unary methods when the working type is INT"
+ return f'''
+ return {op.int_op(t.as_int("self"))};
+ '''
+
+def _unary_method_long(op:UnaryOpInfo, t:IntTypeInfo):
+ "Template for unary methods when the working type is LONG"
+ return f'''
+ long r = {op.long_op(t.as_long("self"))};
+ int s = (int) r;
+ return s == r ? s : BigInteger.valueOf(r);
+ '''
+
+def _unary_method_big(op:UnaryOpInfo, t:IntTypeInfo):
+ "Template for unary methods when the working type is BIG"
+ return f'''
+ return {op.big_op(t.as_big("self"))};
+ '''
+
+
+def binary_intmethod(op:BinaryOpInfo,
+ t1:IntTypeInfo, n1,
+ t2:IntTypeInfo, n2):
+ """Template for a binary operation with int result.
+
+ Argument coercions are made according to their static type then
+ the operation is applied which must yield a result in the working
+ type. Processing is applied to that result to choose an integer
+ representation. This is only appropriate where the return from the
+ generated method should be a Python int (e.g. not comparisons)."""
+ # Decide the width at which to work with these types and op
+ iw = max(op.min_working_type.value,
+ t1.min_working_type.value,
+ t2.min_working_type.value)
+ w = WorkingType(iw)
+ if w == WorkingType.INT:
+ return _binary_intmethod_int(op, t1, n1, t2, n2)
+ elif w == WorkingType.LONG:
+ return _binary_intmethod_long(op, t1, n1, t2, n2)
+ elif w == WorkingType.BIG:
+ return _binary_intmethod_big(op, t1, n1, t2, n2)
+ elif w == WorkingType.OBJECT:
+ return _binary_intmethod_obj(op, t1, n1, t2, n2)
+ else:
+ raise ValueError(
+ f"Cannot make method body for {op.name} and {w}")
+
+def _binary_intmethod_int(op:BinaryOpInfo,
+ t1:IntTypeInfo, n1,
+ t2:IntTypeInfo, n2):
+ "Template for binary int methods when the working type is INT"
+ return f'''
+ return {op.int_op(t1.as_int(n1), t2.as_int(n2))};
+ '''
+
+def _binary_intmethod_long(op:BinaryOpInfo,
+ t1:IntTypeInfo, n1,
+ t2:IntTypeInfo, n2):
+ "Template for binary int methods when the working type is LONG"
+ return f'''
+ long r = {op.long_op(t1.as_long(n1), t2.as_long(n2))};
+ int s = (int) r;
+ return s == r ? s : BigInteger.valueOf(r);
+ '''
+
+def _binary_intmethod_big(op:BinaryOpInfo,
+ t1:IntTypeInfo, n1,
+ t2:IntTypeInfo, n2):
+ "Template for binary int methods when the working type is BIG"
+ return f'''
+ return toInt({op.big_op(t1.as_big(n1), t2.as_big(n2))});
+ '''
+
+def _binary_intmethod_obj(op:BinaryOpInfo,
+ t1:IntTypeInfo, n1,
+ t2:IntTypeInfo, n2):
+ "Template for binary int methods when the working type is OBJECT"
+ return f'''
+ try {{
+ return toInt({op.big_op(t1.as_big(n1), t2.as_big(n2))});
+ }} catch (NoConversion e) {{
+ return Py.NotImplemented;
+ }}
+ '''
+
+
+def binary_method(op:BinaryOpInfo,
+ t1:IntTypeInfo, n1,
+ t2:IntTypeInfo, n2):
+ """Template for a binary operation with any result type.
+
+ Argument coercions are made according to their static type then
+ the operation is applied and the result returned without further
+ processing."""
+ # Decide the width at which to work with these types and op
+ iw = max(op.min_working_type.value,
+ t1.min_working_type.value,
+ t2.min_working_type.value)
+ w = WorkingType(iw)
+ if w == WorkingType.INT:
+ return _binary_method_int(op, t1, n1, t2, n2)
+ elif w == WorkingType.LONG:
+ return _binary_method_long(op, t1, n1, t2, n2)
+ elif w == WorkingType.BIG:
+ return _binary_method_big(op, t1, n1, t2, n2)
+ elif w == WorkingType.OBJECT:
+ return _binary_method_obj(op, t1, n1, t2, n2)
+ else:
+ raise ValueError(
+ f"Cannot make method body for {op.name} and {w}")
+
+def _binary_method_int(op:BinaryOpInfo,
+ t1:IntTypeInfo, n1,
+ t2:IntTypeInfo, n2):
+ "Template for binary methods when the working type is INT"
+ return f'''
+ return {op.int_op(t1.as_int(n1), t2.as_int(n2))};
+ '''
+
+def _binary_method_long(op:BinaryOpInfo,
+ t1:IntTypeInfo, n1,
+ t2:IntTypeInfo, n2):
+ "Template for binary methods when the working type is LONG"
+ return f'''
+ return {op.long_op(t1.as_long(n1), t2.as_long(n2))};
+ '''
+
+def _binary_method_big(op:BinaryOpInfo,
+ t1:IntTypeInfo, n1,
+ t2:IntTypeInfo, n2):
+ "Template for binary methods when the working type is BIG"
+ return f'''
+ return {op.big_op(t1.as_big(n1), t2.as_big(n2))};
+ '''
+
+def _binary_method_obj(op:BinaryOpInfo,
+ t1:IntTypeInfo, n1,
+ t2:IntTypeInfo, n2):
+ "Template for binary methods when the working type is OBJECT"
+ return f'''
+ try {{
+ return {op.big_op(t1.as_big(n1), t2.as_big(n2))};
+ }} catch (NoConversion e) {{
+ return Py.NotImplemented;
+ }}
+ '''
+
+
+class PyLongGenerator(ImplementationGenerator):
+
+ # The canonical and adopted implementations in PyInteger.java,
+ # as there are no further accepted self-classes.
+ ACCEPTED_CLASSES = [
+ PY_LONG_CLASS,
+ BIG_INTEGER_CLASS,
+ INTEGER_CLASS,
+ BOOLEAN_CLASS,
+ ]
+ OPERAND_CLASSES = ACCEPTED_CLASSES + [
+ ]
+
+ # Operations have to provide versions in which long and
+ # BigInteger are the common type to which arguments are converted.
+
+ UNARY_OPS = [
+ # Arguments are: name, return_type, min_working_type,
+ # big_op, long_op, int_op
+ UnaryOpInfo('__abs__', OBJECT_CLASS, WorkingType.LONG,
+ lambda x: f'{x}.abs()',
+ lambda x: f'Math.abs({x})',
+ lambda x: f'Math.abs({x})'),
+ UnaryOpInfo('__index__', OBJECT_CLASS, WorkingType.INT,
+ itself,
+ itself,
+ itself),
+ UnaryOpInfo('__int__', OBJECT_CLASS, WorkingType.INT,
+ itself,
+ itself,
+ itself),
+ UnaryOpInfo('__invert__', OBJECT_CLASS, WorkingType.INT,
+ lambda x: f'{x}.not()',
+ lambda x: f'~{x}',
+ lambda x: f'~{x}'),
+ UnaryOpInfo('__neg__', OBJECT_CLASS, WorkingType.LONG,
+ lambda x: f'{x}.negate()',
+ lambda x: f'-{x}',
+ lambda x: f'-{x}'),
+ UnaryOpInfo('__float__', OBJECT_CLASS, WorkingType.INT,
+ lambda x: f'PyLong.convertToDouble({x})',
+ lambda x: f'((double) {x})',
+ lambda x: f'((double) {x})'),
+ UnaryOpInfo('__bool__', PRIMITIVE_BOOLEAN, WorkingType.BOOLEAN,
+ lambda x: f'{x}.signum() != 0',
+ lambda x: f'{x} != 0L',
+ lambda x: f'{x} != 0'),
+ UnaryOpInfo('__hash__', PRIMITIVE_INT, WorkingType.INT,
+ lambda x: f'{x}.hashCode()',
+ lambda x: f'{x}.hashCode()',
+ lambda x: f'{x}'),
+ ]
+
+ BINARY_OPS = [
+ # Arguments are: name, return_type, working_type,
+ # body_method,
+ # big_op, long_op, int_op,
+ # with_class_specific_binops
+ BinaryOpInfo('__add__', OBJECT_CLASS, WorkingType.LONG,
+ binary_intmethod,
+ lambda x, y: f'{x}.add({y})',
+ lambda x, y: f'{x} + {y}',
+ lambda x, y: f'{x} + {y}',
+ True),
+ BinaryOpInfo('__radd__', OBJECT_CLASS, WorkingType.LONG,
+ binary_intmethod,
+ lambda x, y: f'{y}.add({x})',
+ lambda x, y: f'{y} + {x}',
+ lambda x, y: f'{y} + {x}',
+ True),
+ BinaryOpInfo('__sub__', OBJECT_CLASS, WorkingType.LONG,
+ binary_intmethod,
+ lambda x, y: f'{x}.subtract({y})',
+ lambda x, y: f'{x} - {y}',
+ lambda x, y: f'{x} - {y}',
+ True),
+ BinaryOpInfo('__rsub__', OBJECT_CLASS, WorkingType.LONG,
+ binary_intmethod,
+ lambda x, y: f'{y}.subtract({x})',
+ lambda x, y: f'{y} - {x}',
+ lambda x, y: f'{y} - {x}',
+ True),
+ BinaryOpInfo('__mul__', OBJECT_CLASS, WorkingType.LONG,
+ binary_intmethod,
+ lambda x, y: f'{x}.multiply({y})',
+ lambda x, y: f'{x} * {y}',
+ lambda x, y: f'{x} * {y}',
+ True),
+ BinaryOpInfo('__rmul__', OBJECT_CLASS, WorkingType.LONG,
+ binary_intmethod,
+ lambda x, y: f'{y}.multiply({x})',
+ lambda x, y: f'{y} * {x}',
+ lambda x, y: f'{y} * {x}',
+ True),
+ BinaryOpInfo('__floordiv__', OBJECT_CLASS, WorkingType.INT,
+ binary_intmethod,
+ lambda x, y: f'divide({x}, {y})',
+ lambda x, y: f'divide({x}, {y})',
+ lambda x, y: f'divide({x}, {y})',
+ True),
+ BinaryOpInfo('__rfloordiv__', OBJECT_CLASS, WorkingType.INT,
+ binary_intmethod,
+ lambda x, y: f'divide({y}, {x})',
+ lambda x, y: f'divide({y}, {x})',
+ lambda x, y: f'divide({y}, {x})',
+ True),
+ BinaryOpInfo('__mod__', OBJECT_CLASS, WorkingType.INT,
+ binary_intmethod,
+ lambda x, y: f'modulo({x}, {y})',
+ lambda x, y: f'modulo({x}, {y})',
+ lambda x, y: f'modulo({x}, {y})',
+ True),
+ BinaryOpInfo('__rmod__', OBJECT_CLASS, WorkingType.INT,
+ binary_intmethod,
+ lambda x, y: f'modulo({y}, {x})',
+ lambda x, y: f'modulo({y}, {x})',
+ lambda x, y: f'modulo({y}, {x})',
+ True),
+
+ BinaryOpInfo('__divmod__', OBJECT_CLASS, WorkingType.INT,
+ binary_method,
+ lambda x, y: f'divmod({x}, {y})',
+ lambda x, y: f'divmod({x}, {y})',
+ lambda x, y: f'divmod({x}, {y})',
+ True),
+ BinaryOpInfo('__rdivmod__', OBJECT_CLASS, WorkingType.INT,
+ binary_method,
+ lambda x, y: f'divmod({y}, {x})',
+ lambda x, y: f'divmod({y}, {x})',
+ lambda x, y: f'divmod({y}, {x})',
+ True),
+
+ BinaryOpInfo('__truediv__', OBJECT_CLASS, WorkingType.INT,
+ binary_method,
+ lambda x, y: f'trueDivide({x}, {y})',
+ lambda x, y: f'trueDivide({x}, {y})',
+ lambda x, y: f'(double){x} / (double){y}',
+ True),
+ BinaryOpInfo('__rtruediv__', OBJECT_CLASS, WorkingType.INT,
+ binary_method,
+ lambda x, y: f'trueDivide({y}, {x})',
+ lambda x, y: f'trueDivide({y}, {x})',
+ lambda x, y: f'(double){y} / (double){x}',
+ True),
+
+ BinaryOpInfo('__and__', OBJECT_CLASS, WorkingType.INT,
+ binary_intmethod,
+ lambda x, y: f'{x}.and({y})',
+ lambda x, y: f'{x} & {y}',
+ lambda x, y: f'{x} & {y}',
+ True),
+ BinaryOpInfo('__rand__', OBJECT_CLASS, WorkingType.INT,
+ binary_intmethod,
+ lambda x, y: f'{y}.and({x})',
+ lambda x, y: f'{y} & {x}',
+ lambda x, y: f'{y} & {x}',
+ True),
+ BinaryOpInfo('__or__', OBJECT_CLASS, WorkingType.INT,
+ binary_intmethod,
+ lambda x, y: f'{x}.or({y})',
+ lambda x, y: f'{x} | {y}',
+ lambda x, y: f'{x} | {y}',
+ True),
+ BinaryOpInfo('__ror__', OBJECT_CLASS, WorkingType.INT,
+ binary_intmethod,
+ lambda x, y: f'{y}.or({x})',
+ lambda x, y: f'{y} | {x}',
+ lambda x, y: f'{y} | {x}'),
+ BinaryOpInfo('__xor__', OBJECT_CLASS, WorkingType.INT,
+ binary_intmethod,
+ lambda x, y: f'{x}.xor({y})',
+ lambda x, y: f'{x} ^ {y}',
+ lambda x, y: f'{x} ^ {y}',
+ True),
+ BinaryOpInfo('__rxor__', OBJECT_CLASS, WorkingType.INT,
+ binary_intmethod,
+ lambda x, y: f'{y}.xor({x})',
+ lambda x, y: f'{y} ^ {x}',
+ lambda x, y: f'{y} ^ {x}',
+ True),
+
+ BinaryOpInfo('__lt__', OBJECT_CLASS, WorkingType.INT,
+ binary_method,
+ lambda x, y: f'{x}.compareTo({y}) < 0',
+ lambda x, y: f'{x} < {y}',
+ lambda x, y: f'{x} < {y}'),
+ BinaryOpInfo('__le__', OBJECT_CLASS, WorkingType.INT,
+ binary_method,
+ lambda x, y: f'{x}.compareTo({y}) <= 0',
+ lambda x, y: f'{x} <= {y}',
+ lambda x, y: f'{x} <= {y}'),
+ BinaryOpInfo('__eq__', OBJECT_CLASS, WorkingType.INT,
+ binary_method,
+ lambda x, y: f'{x}.compareTo({y}) == 0',
+ lambda x, y: f'{x} == {y}',
+ lambda x, y: f'{x} == {y}'),
+ BinaryOpInfo('__ne__', OBJECT_CLASS, WorkingType.INT,
+ binary_method,
+ lambda x, y: f'{x}.compareTo({y}) != 0',
+ lambda x, y: f'{x} != {y}',
+ lambda x, y: f'{x} != {y}'),
+ BinaryOpInfo('__gt__', OBJECT_CLASS, WorkingType.INT,
+ binary_method,
+ lambda x, y: f'{x}.compareTo({y}) > 0',
+ lambda x, y: f'{x} > {y}',
+ lambda x, y: f'{x} > {y}'),
+ BinaryOpInfo('__ge__', OBJECT_CLASS, WorkingType.INT,
+ binary_method,
+ lambda x, y: f'{x}.compareTo({y}) >= 0',
+ lambda x, y: f'{x} >= {y}',
+ lambda x, y: f'{x} >= {y}'),
+ ]
+
+ # Emit methods selectable by a single type
+ def special_methods(self, e):
+
+ # Emit the unary operations
+ for op in self.UNARY_OPS:
+ self.emit_heading(e, op.name)
+ for t in self.ACCEPTED_CLASSES:
+ self.special_unary(e, op, t)
+
+ # Emit the binary operations op(T, Object)
+ for op in self.BINARY_OPS:
+ self.emit_heading(e, op.name)
+ for vt in self.ACCEPTED_CLASSES:
+ self.special_binary(e, op, vt, OBJECT_CLASS)
+
+ # Emit methods selectable by a pair of types (for call sites)
+ def special_binops(self, e):
+
+ # Emit the binary operations and comparisons
+ for op in self.BINARY_OPS:
+ if op.class_specific:
+ self.emit_heading(e, op.name)
+ for vt in self.ACCEPTED_CLASSES:
+ for wt in self.OPERAND_CLASSES:
+ self.special_binary(e, op, vt, wt)
+
+ def special_unary(self, e, op:UnaryOpInfo, t):
+ e.emit('static ').emit(op.return_type.name).emit(' ')
+ e.emit(op.name).emit('(').emit(t.name).emit(' self) {')
+ with e.indentation():
+ method = unary_method(op, t)
+ method = self.left_justify(method)
+ e.emit_lines(method)
+ e.emit_line('}').emit_line()
+
+ # Emit one binary operation, for example:
+ # private static Object __add__(Integer v, BigInteger w) {
+ # return v + toInt(w);
+ # }
+ def special_binary(self, e, op:BinaryOpInfo, t1, t2):
+ reflected = op.name.startswith('__r') and \
+ op.name not in ("__rshift__", "__round__", "__repr__")
+ n1, n2 = 'vw' if not reflected else 'wv'
+ e.emit('static ').emit(op.return_type.name).emit(' ')
+ e.emit(op.name).emit('(')
+ e.emit(t1.name).emit(' ').emit(n1).emit(', ')
+ e.emit(t2.name).emit(' ').emit(n2).emit(') {')
+ with e.indentation():
+ method = op.body_method(op, t1, n1, t2, n2)
+ method = self.left_justify(method)
+ e.emit_lines(method)
+ e.emit_line('}').emit_line()
+
diff --git a/build-tools/python/lib/core/PyUnicode.py b/build-tools/python/lib/core/PyUnicode.py
new file mode 100644
index 000000000..56b71d3f6
--- /dev/null
+++ b/build-tools/python/lib/core/PyUnicode.py
@@ -0,0 +1,312 @@
+# PyUnicode.py: A generator for Java files that define the Python str
+
+# Copyright (c)2021 Jython Developers.
+# Licensed to PSF under a contributor agreement.
+
+# This generator writes PyUnicodeMethods.java and PyUnicodeBinops.java .
+
+# At the time of this writing, only the comparison operations are
+# generated. Unlike arithmetic types, str does not have a large
+# set of operations with a uniform pattern, so it is more effective
+# to hand-craft the small number of cases needed.
+
+from dataclasses import dataclass
+from typing import Callable
+
+from . import ImplementationGenerator, TypeInfo, WorkingType, OpInfo
+
+
+@dataclass
+class StrTypeInfo(TypeInfo):
+ "Information about a type and templates for conversion to str types"
+ # There is a template (a function) to generate an expression
+ # that converts *from* this type to each named Java type.
+ # Template for expression that converts to PySequence
+ as_seq: Callable = None
+ # Template for expression that converts to String
+ as_str: Callable = None
+
+# Useful in cases where an argument is already the right type
+itself = lambda x: x
+
+PY_UNICODE_CLASS = StrTypeInfo('PyUnicode', WorkingType.SEQ,
+ lambda x: f'{x}.adapt()',
+ itself)
+STRING_CLASS = StrTypeInfo('String', WorkingType.STRING,
+ lambda x: f'adapt({x})',
+ itself)
+OBJECT_CLASS = StrTypeInfo('Object', WorkingType.OBJECT,
+ lambda x: f'adapt({x})')
+
+
+@dataclass
+class UnaryOpInfo(OpInfo):
+ # There is a template (a function) to generate an expression
+ # for each working Java type to which argument may be converted.
+ # Working type is Java String
+ str_op: Callable
+
+
+@dataclass
+class BinaryOpInfo(OpInfo):
+ # There is a template (a function) to generate the body
+ body_method: Callable
+ # There is a template (a function) to generate an expression
+ # for each working Java type to which arguments may be converted.
+ # Working type is Java String
+ str_op: Callable
+ # Working type is PySequence
+ seq_op: Callable
+ # Also create class-specific binop specialisations
+ class_specific: bool = False
+
+
+def unary_method(op:UnaryOpInfo, t:StrTypeInfo):
+ "Template generating the body of a unary operation."
+ # Decide the width at which to work with this type and op
+ iw = max(op.min_working_type.value, t.min_working_type.value)
+ w = WorkingType(iw)
+ if w == WorkingType.STRING:
+ return _unary_method_str(op, t)
+ elif w == WorkingType.SEQ:
+ return _unary_method_seq(op, t)
+ elif w == WorkingType.OBJECT:
+ return _unary_method_obj(op, t)
+ else:
+ raise ValueError(
+ f"Cannot make method body for {op.name} and {w}")
+
+def _unary_method_str(op:UnaryOpInfo, t:StrTypeInfo):
+ "Template for unary methods when the working type is STRING"
+ return f'''
+ return {op.str_op(t.as_str("self"))};
+ '''
+
+def _unary_method_seq(op:UnaryOpInfo, t:StrTypeInfo):
+ "Template for unary methods when the working type is LONG"
+ return f'''
+ return {op.seq_op(t.as_seq("self"))};
+ '''
+
+def _unary_method_obj(op:UnaryOpInfo, t:StrTypeInfo):
+ "Template for unary methods when the working type is BIG"
+ return f'''
+ return {op.seq_op(t.as_seq("self"))};
+ '''
+
+
+def binary_method(op:BinaryOpInfo, t1:StrTypeInfo, n1, t2:StrTypeInfo, n2):
+ "Template generating the body of a binary operation."
+ # Decide the width at which to work with these typse and op
+ iw = max(op.min_working_type.value,
+ t1.min_working_type.value,
+ t2.min_working_type.value)
+ w = WorkingType(iw)
+ if w == WorkingType.STRING:
+ return _binary_method_str(op, t1, n1, t2, n2)
+ elif w == WorkingType.SEQ:
+ return _binary_method_seq(op, t1, n1, t2, n2)
+ elif w == WorkingType.OBJECT:
+ return _binary_method_obj(op, t1, n1, t2, n2)
+ else:
+ raise ValueError(
+ f"Cannot make method body for {op.name} and {w}")
+
+
+def _binary_method_str(op:BinaryOpInfo, t1:StrTypeInfo, n1, t2:StrTypeInfo, n2):
+ return f'''
+ return {op.str_op(t1.as_str(n1), t2.as_str(n2))};
+ '''
+
+def _binary_method_seq(op:BinaryOpInfo, t1:StrTypeInfo, n1, t2:StrTypeInfo, n2):
+ return f'''
+ return {op.seq_op(t1.as_seq(n1), t2.as_seq(n2))};
+ '''
+
+def _binary_method_obj(op:BinaryOpInfo, t1:StrTypeInfo, n1, t2:StrTypeInfo, n2):
+ return f'''
+ try {{
+ return {op.seq_op(t1.as_seq(n1), t2.as_seq(n2))};
+ }} catch (NoConversion e) {{
+ return Py.NotImplemented;
+ }}
+ '''
+
+def comparison(op:BinaryOpInfo, t1:StrTypeInfo, n1, t2:StrTypeInfo, n2):
+ "Template generating the body of a comparison operation."
+ iw = max(op.min_working_type.value,
+ t1.min_working_type.value,
+ t2.min_working_type.value)
+ w = WorkingType(iw)
+ if w == WorkingType.STRING:
+ return _comparison_str(op, t1, n1, t2, n2)
+ elif w == WorkingType.SEQ:
+ return _comparison_seq(op, t1, n1, t2, n2)
+ elif w == WorkingType.OBJECT:
+ return _comparison_obj(op, t1, n1, t2, n2)
+ else:
+ raise ValueError(
+ f"Cannot make method body for {op.name} and {w}")
+
+def _comparison_guard(op:BinaryOpInfo, t1:StrTypeInfo, n1, t2:StrTypeInfo, n2):
+ if t2.name == "Object" or t2.name == t1.name:
+ # The objects might be identical, permitting a shortcut
+ name = op.name
+ if name == "__eq__" or name == "__le__" or name == "__ge__":
+ return f'{n1} == {n2} || '
+ elif name == "__ne__" or name == "__lt__" or name == "__gt__":
+ return f'{n1} != {n2} && '
+ return ""
+
+def _comparison_str(op:BinaryOpInfo, t1:StrTypeInfo, n1, t2:StrTypeInfo, n2):
+ guard = _comparison_guard(op, t1, n1, t2, n2)
+ return f'''
+ return {guard}{op.int_op(t1.as_str(n1), t2.as_str(n2))};
+ '''
+
+def _comparison_seq(op:BinaryOpInfo, t1:StrTypeInfo, n1, t2:StrTypeInfo, n2):
+ guard = _comparison_guard(op, t1, n1, t2, n2)
+ return f'''
+ return {guard}{op.seq_op(t1.as_seq(n1), t2.as_seq(n2))};
+ '''
+
+def _comparison_obj(op:BinaryOpInfo, t1:StrTypeInfo, n1, t2:StrTypeInfo, n2):
+ guard = _comparison_guard(op, t1, n1, t2, n2)
+ return f'''
+ try {{
+ return {guard}{op.seq_op(t1.as_seq(n1), t2.as_seq(n2))};
+ }} catch (NoConversion e) {{
+ return Py.NotImplemented;
+ }}
+ '''
+
+class PyUnicodeGenerator(ImplementationGenerator):
+
+ # The canonical and adopted implementations in PyUnicode.java,
+ # as there are no further accepted self-classes.
+ ACCEPTED_CLASSES = [
+ PY_UNICODE_CLASS,
+ STRING_CLASS,
+ ]
+ OPERAND_CLASSES = ACCEPTED_CLASSES + [
+ ]
+
+ # Operations have to provide versions in which long and
+ # BigInteger are the common type to which arguments are converted.
+
+ UNARY_OPS = [
+ # Arguments are: name, min_working_type,
+ # body_method,
+ # str_op
+ ]
+
+ BINARY_OPS = [
+ # Arguments are: name, return_type, working_type,
+ # body_method,
+ # str_op, seq_op,
+ # class_specific
+
+ # BinaryOpInfo('__add__', OBJECT_CLASS, WorkingType.STRING,
+ # binary_method,
+ # lambda x, y: f'{x} + ({y})',
+ # lambda x, y: f'{x}.concat({y})',
+ # True),
+
+ BinaryOpInfo('__lt__', OBJECT_CLASS, WorkingType.STRING,
+ comparison,
+ lambda x, y: f'{x}.compareTo({y}) < 0',
+ lambda x, y: f'{x}.compareTo({y}) < 0'),
+ BinaryOpInfo('__le__', OBJECT_CLASS, WorkingType.STRING,
+ comparison,
+ lambda x, y: f'{x}.compareTo({y}) <= 0',
+ lambda x, y: f'{x}.compareTo({y}) <= 0'),
+ BinaryOpInfo('__eq__', OBJECT_CLASS, WorkingType.STRING,
+ comparison,
+ lambda x, y: f'eq({x}, {y})',
+ lambda x, y: f'eq({x}, {y})'),
+ BinaryOpInfo('__ne__', OBJECT_CLASS, WorkingType.STRING,
+ comparison,
+ lambda x, y: f'!eq({x}, ({y})',
+ lambda x, y: f'!eq({x}, {y})'),
+ BinaryOpInfo('__gt__', OBJECT_CLASS, WorkingType.STRING,
+ comparison,
+ lambda x, y: f'{x}.compareTo({y}) > 0',
+ lambda x, y: f'{x}.compareTo({y}) > 0'),
+ BinaryOpInfo('__ge__', OBJECT_CLASS, WorkingType.STRING,
+ comparison,
+ lambda x, y: f'{x}.compareTo({y}) >= 0',
+ lambda x, y: f'{x}.compareTo({y}) >= 0'),
+ ]
+
+ # Emit methods selectable by a single type
+ def special_methods(self, e):
+
+ # Emit the unary operations
+ for op in self.UNARY_OPS:
+ e.emit_line(f'// {"-"*(60-len(op.name))} {op.name}')
+ e.emit_line()
+ for t in self.ACCEPTED_CLASSES:
+ self.special_unary(e, op, t)
+
+ # Emit the binary operations op(T, Object)
+ for op in self.BINARY_OPS:
+ e.emit_line(f'// {"-"*(60-len(op.name))} {op.name}')
+ e.emit_line()
+ for vt in self.ACCEPTED_CLASSES:
+ self.special_binary(e, op, vt, OBJECT_CLASS)
+
+ # Emit methods selectable by a pair of types (for call sites)
+ def special_binops(self, e):
+
+ # Emit the binary operations and comparisons
+ for op in self.BINARY_OPS:
+ if op.class_specific:
+ e.emit_line(f'// {"-"*(60-len(op.name))} {op.name}')
+ e.emit_line()
+ for vt in self.ACCEPTED_CLASSES:
+ for wt in self.OPERAND_CLASSES:
+ self.special_binary(e, op, vt, wt)
+
+ def left_justify(self, text):
+ lines = list()
+ # Find common leading indent
+ common = 999
+ for line in text.splitlines():
+ # Discard trailing space
+ line = line.rstrip()
+ # Discard empty lines
+ if (n:=len(line)) > 0:
+ space = n - len(line.lstrip())
+ if space < common: common = space
+ lines.append(line)
+ if common == 999: common = 0
+ # Remove this common prefix
+ clean = list()
+ for line in lines:
+ clean.append(line[common:])
+ return clean
+
+ def special_unary(self, e, op:UnaryOpInfo, t):
+ e.emit('static ').emit(op.return_type.name).emit(' ')
+ e.emit(op.name).emit('(').emit(t.name).emit(' self) {')
+ with e.indentation():
+ method = unary_method(op, t)
+ method = self.left_justify(method)
+ e.emit_lines(method)
+ e.emit_line('}').emit_line()
+
+ def special_binary(self, e, op:BinaryOpInfo, t1, t2):
+ reflected = op.name.startswith('__r') and \
+ op.name not in ("__rrshift__", "__round__", "__repr__")
+ n1, n2 = 'vw' if not reflected else 'wv'
+ e.emit('static ').emit(op.return_type.name).emit(' ')
+ e.emit(op.name).emit('(')
+ e.emit(t1.name).emit(' ').emit(n1).emit(', ')
+ e.emit(t2.name).emit(' ').emit(n2).emit(') {')
+ with e.indentation():
+ method = op.body_method(op, t1, n1, t2, n2)
+ method = self.left_justify(method)
+ e.emit_lines(method)
+ e.emit_line('}').emit_line()
+
+
diff --git a/build-tools/python/lib/core/__init__.py b/build-tools/python/lib/core/__init__.py
new file mode 100644
index 000000000..718b4976c
--- /dev/null
+++ b/build-tools/python/lib/core/__init__.py
@@ -0,0 +1,15 @@
+# core package: generators and other tooling
+
+# Copyright (c)2021 Jython Developers.
+# Licensed to PSF under a contributor agreement.
+
+# These classes support the processing of template files into
+# the Java class definitions that realise Python objects
+# and their methods.
+
+from .base import ImplementationGenerator, TypeInfo, WorkingType, OpInfo
+from .PyFloat import PyFloatGenerator
+from .PyLong import PyLongGenerator
+from .PyUnicode import PyUnicodeGenerator
+
+
diff --git a/build-tools/python/lib/core/base.py b/build-tools/python/lib/core/base.py
new file mode 100644
index 000000000..e0e5c7d8e
--- /dev/null
+++ b/build-tools/python/lib/core/base.py
@@ -0,0 +1,119 @@
+# base.py: foundations for source-code generation from templates
+
+# Copyright (c)2021 Jython Developers.
+# Licensed to PSF under a contributor agreement.
+
+# Module: evo1.generate.base
+
+from dataclasses import dataclass
+from enum import Enum
+from datetime import datetime
+
+
+# The method implementations convert operands to a common "working
+# type" in order to perform the central operation. The type varies
+# with the operation and the operand(s). For example, when adding
+# two operands known to be Integer, the common type is LONG (Java
+# long), so that there is no overflow, while bit-wise operations on
+# the same pair may be carried out in an INT.
+#
+# In a unary operation, the wider of the (minimum) operation type
+# and the operand type is used. When mixing types in a binary
+# operation, the widest of the two types and the operation is used.
+class WorkingType(Enum):
+ "Enumerates the types to which operands may be converted."
+ BOOLEAN = 0
+ INT = 1
+ LONG = 2
+ BIG = 3
+ DOUBLE = 4
+ STRING = 5
+ SEQ = 6
+ OBJECT = 7
+
+
+# We use pre-defined data classes to describe (Java) types that may
+# appear as operands or return types. We record the name in Java,
+# information about the minimum "width" at which we ought to
+# compute with them, and how to convert them to int, long and big
+# representations.
+
+@dataclass
+class TypeInfo:
+ "Information about a type an templates for conversion to int types"
+ # Java name of a Java class ("PyLong", "Integer", etc.)
+ name: str
+ # An argument of this type implies the working type is at least:
+ min_working_type: WorkingType
+
+
+# Implementation template scripts extend this dataclass to describe
+# their operations.
+
+@dataclass
+class OpInfo:
+ "Base class for describing operations."
+ # Name of the operation ("__add__", "__neg__", etc.).
+ name: str
+ # An implementation of this op has a return type of:
+ return_type: TypeInfo
+ # Implementation of this op implies the working type is at least:
+ min_working_type: WorkingType
+
+
+# Base class of generators for object implementations
+
+class ImplementationGenerator:
+
+ # Adjust the indent to match that requested
+ def set_indent(self, i):
+ self.emitter.indent = i
+
+ # Create a warning comment
+ def emit_object_template(self, e, src):
+ name = getattr(src, 'name', '?').replace('\\', '/')
+ e.emit_line("/*")
+ e.emit_line(" * Generated by java_object_gen using ")
+ e.emit(f"generator {self.__class__.__name__}.")
+ e.emit_line(f" * Source: {name}")
+ timestamp = datetime.now().strftime("%A %Y-%m-%d %H:%M:%S")
+ e.emit_line(f" * Date: {timestamp}")
+ e.emit_line(" */")
+
+ # Emit a rule (comment) above a block of implementations
+ def emit_heading(self, e, name:str):
+ e.emit_line(f'// {"-"*(60-len(name))} {name}')
+ e.emit_line()
+
+ # Emit methods selectable by a single type
+ def special_methods(self, e):
+ pass
+
+ # Emit methods selectable by a pair of types (for call sites)
+ def special_binops(self, e):
+ pass
+
+ def emit_object_plumbing(self, e):
+ pass
+
+ def left_justify(self, text):
+ "Left-justify a block of text, returning a list of lines."
+ lines = list()
+ # Find common leading indent
+ common = 999
+ for line in text.splitlines():
+ # Discard trailing space
+ line = line.rstrip()
+ # Discard empty lines
+ if (n:=len(line)) > 0:
+ space = n - len(line.lstrip())
+ if space < common: common = space
+ lines.append(line)
+ if common == 999: common = 0
+ # Remove this common prefix
+ clean = list()
+ for line in lines:
+ clean.append(line[common:])
+ return clean
+
+
diff --git a/build-tools/python/lib/srcgen.py b/build-tools/python/lib/srcgen.py
new file mode 100644
index 000000000..863962f0c
--- /dev/null
+++ b/build-tools/python/lib/srcgen.py
@@ -0,0 +1,161 @@
+# Classes that emit indented, wrapped (Java) source code
+
+# Copyright (c)2021 Jython Developers.
+# Licensed to PSF under a contributor agreement.
+
+import io
+import sys
+
+class IndentedEmitter:
+ """Class to write wrapped, indented (program) text onto a stream.
+
+ Text is supplied via the emit() and emit_line() methods, and added to
+ an internal buffer. emit_line() writes the current buffer (if it is not
+ empty), always beginning a new, indented line. emit() first checks for
+ sufficient buffer space, writing existing content to the output stream
+ only as necessary to respect the stated width. The supplied text is
+ treated as atomic, however long: neither method inserts line-breaks.
+ close() must be called to ensure the last buffered text reaches the
+ output stream. (Consider using contextlib.closing.)
+ """
+
+ class IndentationContextManager:
+ """Context in which the indentation is increased by one."""
+
+ def __init__(self, emitter):
+ self.emitter = emitter
+
+ def __enter__(self):
+ self.emitter.indent += 1
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self.emitter.indent -= 1
+
+ def indentation(self):
+ """Return a context manager to increase the indentation by one."""
+ return IndentedEmitter.IndentationContextManager(self)
+
+ def __init__(self, stream=None, width=None, indent=None):
+ self.stream = stream or sys.stdout
+ self.width = width if width is not None else 70
+ self.indent = indent if indent is not None else 1
+ # Output buffer when lines are pieced together
+ self.buf = io.StringIO()
+
+ def flush(self):
+ """Emit residual line (if any) to the output stream."""
+ residue = self.buf.getvalue().rstrip()
+ if residue:
+ print(residue, file=self.stream)
+ self.buf.seek(0)
+ self.buf.truncate()
+
+ close = flush # synonym for the benefit of "with closing(...)"
+
+ def emit(self, text="", suffix=""):
+ """Write the text+suffix to self.buf.
+
+ Start a new line if necessary.
+ """
+ n = len(text)
+ if suffix:
+ n += len(suffix)
+ if self.buf.tell() + n > self.width:
+ # Must start a new line first
+ self.emit_line()
+ self.buf.write(text)
+ if suffix:
+ self.buf.write(suffix)
+ return self
+
+ def emit_line(self, text=""):
+ """Begin a new line with indent and optional text."""
+ if self.buf.tell() > 0:
+ # Flush existing buffer to output
+ print(self.buf.getvalue().rstrip(), file=self.stream)
+ self.buf.seek(0)
+ self.buf.truncate()
+ for _ in range(self.indent):
+ self.buf.write(" ")
+ self.buf.write(text)
+ return self
+
+ def emit_lines(self, lines):
+ """Begin a new line and emit with indented multi-line text."""
+ for line in lines:
+ self.emit_line(line)
+ return self
+
+
+class JavaConstantEmitter(IndentedEmitter):
+ """A class capable of emitting Java constants from Python values.
+
+ This class extends the basic IndentedEmitter for wrapped, indented
+ program text with methods that translate Python values to equivalent
+ Java constants (or constructor expressions).
+ """
+
+ MAX_INT = (1 << 31) - 1
+ MIN_INT = -MAX_INT - 1
+
+ def java_int(self, value, suffix=""):
+ """Emit the value as a Java int constant."""
+ if self.MIN_INT <= value <= self.MAX_INT:
+ return self.emit(repr(value) + suffix)
+ else:
+ raise ValueError("Value out of range for Java int")
+
+ def java_string(self, value, suffix=""):
+ """Emit the value as a Java String constant."""
+ text = repr(str(value))
+ if text.startswith("'"):
+ q = '"'
+ text = q + text[1:-1].replace(q, '\\"') + q
+ return self.emit(text, suffix)
+
+ def java_byte(self, value, suffix=""):
+ """Emit the value as a Java int constant wrapped to signed byte."""
+ bstr = format(value if value < 128 else value - 256, "d")
+ return self.emit(bstr, suffix)
+
+ def java_double(self, value, suffix=""):
+ """Emit the value as a Java double constant."""
+ return self.emit(repr(value), suffix)
+
+ def java_arglist(self, handler, a, suffix=""):
+ """Emit comma-separated Java values using the given handler.
+
+ The handler is a function f(obj, suffix="") that emits the
+ individual argument. It must be capable of converting all types
+ that may be supplied in a.
+ """
+ n = len(a)
+ if n == 0:
+ self.emit(suffix)
+ else:
+ with self.indentation():
+ for i in range(n - 1):
+ handler(a[i], ", ")
+ handler(a[-1], suffix)
+ return self
+
+ def java_array(self, handler, a, suffix=""):
+ """Emit a Java array of elements emitted by the given handler.
+
+ The handler is a function f(obj, suffix="") that emits the
+ individual element. Since Java arrays are homogeneous, it
+ will often be a single bound method emitting a compatible value
+ e.g. self.java_byte.
+ """
+ n = len(a)
+ if n == 0:
+ self.emit("{}", suffix)
+ else:
+ self.emit("{ ")
+ with self.indentation():
+ for i in range(n - 1):
+ handler(a[i], ", ")
+ handler(a[-1], " }" + suffix)
+ return self
+
diff --git a/build-tools/python/tool/java_object_gen.py b/build-tools/python/tool/java_object_gen.py
new file mode 100644
index 000000000..6b8363d01
--- /dev/null
+++ b/build-tools/python/tool/java_object_gen.py
@@ -0,0 +1,166 @@
+# tool/java_object_gen Emit Java
+#
+# Copyright (c)2021 Jython Developers.
+# Licensed to PSF under a contributor agreement.
+
+# This is a tool used from the core.gradle build file to generate object
+# implementation methods, such as __neg__ and __rsub__, in Java.
+# It processes Java files looking for a few simple markers, which it
+# replaces with blocks of method definitions.
+#
+# See the files in core/src/main/javaTemplate for examples.
+
+import sys
+import os
+import re
+import argparse
+import srcgen
+from re import match
+from contextlib import closing
+from dataclasses import dataclass
+
+from core import ImplementationGenerator
+from core import PyFloatGenerator
+from core import PyLongGenerator
+from core import PyUnicodeGenerator
+
+
+class ImplementationTemplateProcessorFactory:
+ "Class creating a processor for object templates"
+
+ def __init__(self, source_dir, dest_dir, error, verbose=False):
+ "Create a factory specifying source and destination roots"
+ self.src_dir = os.path.relpath(source_dir)
+ self.dst_dir = os.path.relpath(dest_dir)
+ self.verbose = verbose
+ # Check source directory
+ if not os.path.isdir(self.src_dir):
+ error(f'no such directory {self.src_dir}')
+ # Ensure destination directory
+ if not os.path.isdir(self.dst_dir):
+ os.makedirs(self.dst_dir, exist_ok=True)
+ # Confirm
+ if self.verbose:
+ # cwd is the project directory e.g. ~/rt3
+ cwd = os.getcwd()
+ print(f' Current dir = {cwd}')
+ print(f' templates from {self.src_dir} to {self.dst_dir}')
+
+ def get_processor(self, package, name):
+ "Create a template processor for one named class"
+ return ImplementationTemplateProcessor(self, package, name)
+
+
+class ImplementationTemplateProcessor:
+ "A template processor for one named class"
+
+ # Patterns marker lines in template files.
+ # Each has a group 1 that captures the indentation.
+ OBJECT_GENERATOR = re.compile(
+ r'([\t ]*)//\s*\$OBJECT_GENERATOR\$\s*(\w+)')
+ SPECIAL_METHODS = re.compile(r'([\t ]*)//\s*\$SPECIAL_METHODS\$')
+ SPECIAL_BINOPS = re.compile(r'([\t ]*)//\s*\$SPECIAL_BINOPS\$')
+ MANGLED = re.compile(r'(([\t ]*)//\s*\($\w+\$)')
+
+ def __init__(self, factory, package, name):
+ self.factory = factory
+ self.package = package
+ self.name = name
+ self.generatorClass = ImplementationGenerator
+ self.generator = None
+ self.emitterClass = srcgen.IndentedEmitter
+
+ def open_src(self):
+ return open(
+ os.path.join(self.factory.src_dir, self.package, self.name),
+ 'r', encoding='utf-8')
+
+ def open_dst(self):
+ location = os.path.join(self.factory.dst_dir, self.package)
+ os.makedirs(location, exist_ok=True)
+ return open(
+ os.path.join(location, self.name),
+ 'w', encoding='utf-8', newline='\n')
+
+ def process(self):
+ if self.factory.verbose:
+ print(f" process {self.name}")
+ with self.open_src() as src:
+ with self.open_dst() as dst:
+ self.process_lines(src, dst)
+
+ def process_lines(self, src, dst):
+
+ def emitter(m):
+ indent = (len(m[1].expandtabs(4)) + 3) // 4
+ return self.emitterClass(dst, 70, indent)
+
+ for line in src:
+
+ if m := self.OBJECT_GENERATOR.match(line):
+ generatorName = m[2]
+ self.generatorClass = globals()[generatorName]
+ self.generator = self.generatorClass()
+ with closing(emitter(m)) as e:
+ self.generator.emit_object_template(e, src)
+
+ elif m := self.SPECIAL_METHODS.match(line):
+ with closing(emitter(m)) as e:
+ self.generator.special_methods(e)
+
+ elif m := self.SPECIAL_BINOPS.match(line):
+ with closing(emitter(m)) as e:
+ self.generator.special_binops(e)
+
+ elif m := self.MANGLED.match(line):
+ print("Mangled template directive?",
+ m[2], file=sys.stderr)
+ dst.write(line)
+
+ else:
+ dst.write(line)
+
+
+def get_parser():
+ parser = argparse.ArgumentParser(
+ prog='java_object_gen',
+ description='Generate Python object implementations.'
+ )
+
+ parser.add_argument('source_dir',
+ help='Template directory (to process)')
+ parser.add_argument('dest_dir',
+ help='Destination directory (in build tree)')
+ parser.add_argument('--verbose', '-v', action='store_true',
+ help='Show more information')
+ return parser
+
+
+def process(src_dir, dest_dir, error, verbose=False):
+ '''Friendly entry point to use this script via API.'''
+ # Embed arguments into factory
+ factory = ImplementationTemplateProcessorFactory(
+ src_dir, dest_dir, error, verbose)
+
+ # Process all Java files in the template tree at src_dir
+ for dirpath, dirnames, filenames in os.walk(src_dir):
+ # Any .java files here?
+ javanames = [n for n in filenames
+ if os.path.splitext(n)[1].lower() == '.java']
+ if javanames:
+ package = os.path.relpath(dirpath, src_dir)
+ for name in javanames:
+ proc = factory.get_processor(package, name)
+ proc.process()
+
+
+def main():
+ # Parse the command line to argparse arguments
+ parser = get_parser()
+ args = parser.parse_args()
+ process(args.source_dir, args.dest_dir, parser.error, args.verbose)
+
+
+if __name__ == '__main__':
+ main()
+
diff --git a/build-tools/python/tool/marshal_test.py b/build-tools/python/tool/marshal_test.py
new file mode 100644
index 000000000..e3721b7ad
--- /dev/null
+++ b/build-tools/python/tool/marshal_test.py
@@ -0,0 +1,117 @@
+# Generate test material for simple marshal tests
+
+import io, sys, os, os.path, math
+import marshal, array, py_compile
+import dis, inspect, types
+
+def make_interned(b):
+ "Change type code to interned equivalent"
+ tc = chr(b[0]&0x7f)
+ a0 = None
+ if tc == 'a': a0 = b'A'
+ elif tc == 'u': a0 = b't'
+ elif tc == 'z': a0 = b'Z'
+
+ if a0:
+ return a0 + b[1:]
+ else:
+ return b
+
+
+def as_byte_array(s):
+ a = array.array('B', s)
+ vals = map(lambda v: format(v, "#04x"), a)
+ return "new byte[] {" + ", ".join(vals) + "}"
+
+
+def as_java(v):
+ "Limited translation to Java"
+ if isinstance(v, bool):
+ return str(v).lower()
+ elif isinstance(v, int):
+ if v < 2**31 and v >= -2**31:
+ return str(v)
+ else:
+ return f"new BigInteger(\"{v:d}\")"
+ elif isinstance(v, float):
+ if math.isinf(v):
+ if v > 0:
+ return "Double.POSITIVE_INFINITY"
+ else:
+ return "Double.NEGATIVE_INFINITY"
+ elif math.isnan(v):
+ return "Double.NaN"
+ else:
+ return v.hex()
+ elif isinstance(v, str):
+ return f"\"{v:s}\""
+ elif isinstance(v, tuple):
+ args = ", ".join(map(as_java, v))
+ return "Py.tuple(" + args + ")"
+ elif isinstance(v, list):
+ args = ", ".join(map(as_java, v))
+ return "new PyList(List.of(" + args + "))"
+ elif isinstance(v, dict):
+ args = ", ".join(map(as_java, v.items()))
+ return "PyDict.fromKeyValuePairs(" + args + ")"
+ elif isinstance(v, bytes):
+ return as_bytes(v)
+ elif isinstance(v, StopIteration):
+ return "Py.StopIteration"
+ else:
+ return "Py.None"
+
+
+def as_bytes(s):
+ a = array.array('B', s)
+ values = map(lambda v: format(v, "#04x"), a)
+ return "bytes(" + ", ".join(values) + ")"
+
+
+def print_load_example(expr, env = locals()):
+ result = eval(expr, None, env)
+ b = marshal.dumps(result)
+ tc = chr(b[0]&0x7f)
+ if tc in 'auz':
+ # Force to intern the string
+ b = make_interned(b)
+ tc = chr(b[0]&0x7f)
+ print(f"loadExample( \"{expr:s}\", // tc='{tc:s}'")
+ javabytes = as_bytes(b)
+ print(f"{javabytes:s},")
+ java = as_java(result)
+ print(f"{java:s} ),")
+
+
+# str
+sa = "hello"
+sb = "sæll"
+su = "\U0001f40d"
+
+
+# tuple
+t = (1,2,3)
+
+# list
+list0 = []
+list1 = [sa]
+list3 = [sa, 2, t]
+listself = [1, 2, 3]
+listself[1] = listself
+
+expressions = [
+ "None",
+ "False", "True",
+ "0", "1", "-42", "2**31-1", "2047**4", "2**45", "-42**15",
+ "0.", "1.", "-42.", "1e42", "1.8e300", "1.12e-308",
+ "float.fromhex('0x1.fffffffffffffp1023')", "float.fromhex('-0x1.p-1022')",
+ "float('inf')", "float('-inf')", "float('nan')",
+ "'hello'", "'sæll'", "'\U0001f40d'",
+ "()", "(sa,sa,sa)", "(sb,sb,t,t)",
+ "[]", "[sa]", "[sa, 2, t]",
+ "{}", "{sa:sb}", "dict(python=su)", "{sa:1, sb:2, su:t}",
+]
+
+for x in expressions:
+ print_load_example(x)
+
diff --git a/build-tools/settings.gradle b/build-tools/settings.gradle
new file mode 100644
index 000000000..5e0426400
--- /dev/null
+++ b/build-tools/settings.gradle
@@ -0,0 +1,9 @@
+/*
+ * build-tools/settings.gradle
+ *
+ * Included project for tools used to create Jython.
+ */
+
+rootProject.name = 'build-tools'
+
+rootProject.buildFileName = rootProject.name + '.gradle'
diff --git a/build.gradle b/build.gradle
index ef0a2144a..c5a69740a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,34 +1,14 @@
/*
- * Gradle build for Jython. See also settings.gradle.
+ * build.gradle
*
- * This is an attempt to build a distributable JAR using Gradle that could be
- * cited as a dependency by other Gradle or Maven projects, when they use the
- * Jython interpreter from Java (under JSR-223 or directly).
- *
- * At present, the build is additional to the Ant build that remains the
- * primary and reliable support for development, for test, and to build the
- * Jython installers.
- *
- * The delivered jar should contain only Jython project material (Java classes
- * and the Python library) while the many JARs Jython depends on will be cited
- * in the accompanying metadata as dependencies.
- *
- * The Jython source structure does not follow the standard convention for
- * Gradle. This script deals with that without changing it, but it uses a build
- * directory (build2) entirely separate from Ant's, in which generated and
- * compiled material is posted conformant with Gradle conventions. This means
- * that the later tasks Gradle provides (test and jar) do not need so much
- * customisation.
+ * Build file for the Jython Project, a multi-project build.
*/
plugins {
id 'java-library'
- id 'antlr'
id 'maven-publish'
}
-import java.text.SimpleDateFormat
-
// ---------------- Determine the version of Jython ----------------------------
/*
@@ -45,7 +25,7 @@ import java.text.SimpleDateFormat
// Versions are specified in this grammar:
// . ( . )? ( )? ( - )?
-version = '2.7.3a1'
+version = '3.11.0a1'
// Valid examples (please preserve in comments):
//version = '2.7.2a2'
@@ -56,861 +36,38 @@ version = '2.7.3a1'
group = 'org.python'
-
// ---------------- Miscellaneous configuration --------------------------------
-/*
- * We support Java 8 onwards officially, up to v2.7.2, but retain the option of
- * compiling for Java 7 (in v2.7.2) by maintaining compatibility in the code
- * base and in the choice of JARs.
- */
-sourceCompatibility = '1.7' // Make both 1.8 after 2.7.2 released
-targetCompatibility = '1.8'
-
-project.compileJava.options.debug = true
-
-
-// Separate the Gradle build from that of Ant
-buildDir = file('build2')
-ext {
- buildDate = new Date()
- /*
- * The directory structure supporting the build has separate locations for
- * several intermediate stages.
- */
- // Java source generated by ANTLR
- antlrGenDir = "$buildDir/gensrc/org/python/antlr"
- // Intermediate locations for compiled classes
- unexposedDir = "$buildDir/unexposed"
- exposedDir = "$buildDir/exposed"
- // The standard library may safely be assembled in-place as a resource
- pythonLibDir = "$buildDir/python/Lib/"
- buildLibDir = "$buildDir/resources/main/Lib/"
- buildTestLibDir = "$buildDir/resources/test/Lib/"
- compiledLibDir = "$buildDir/resources/main/Lib/"
- compiledTestLibDir = "$buildDir/resources/test/Lib/"
-}
-
-
-repositories {
- // Jython is distributed through Maven Central. Get our dependencies there too.
- mavenCentral()
-}
-
-sourceSets {
-
- main { // Non-standard locations must be specified explicitly
+allprojects {
- antlr {
- srcDirs = ['grammar']
- exclude 'Base.g' // Not used (and produces errors)
- }
+ apply plugin: 'java'
+ // We support Java 11 onwards officially.
+ sourceCompatibility = '17'
- java {
- srcDirs = ['src', project.ext.antlrGenDir]
- // Reference to proprietary libraries not supplied
- exclude 'com/ziclix/python/sql/handler/InformixDataHandler.java'
- exclude 'com/ziclix/python/sql/handler/OracleDataHandler.java'
- }
-
- resources {
- // Resources in project root, but this invites an explosion.
- // ... so claim no sources:
- srcDirs = []
- // and fix it in task processResources
- }
- }
+ // Same configuiration all sub-projects
+ tasks.withType(JavaCompile) {
+ options.encoding = 'UTF-8'
+ options.debug = true
- test { // Non-standard locations must be specified explicitly
+ // deprecation is noisy
+ options.deprecation = true
- java {
- srcDirs = ['tests/java']
- // Reference to proprietary libraries not supplied
- exclude 'com/ziclix/python/sql/**'
- }
+ // Use only public API
+ options.compilerArgs.addAll(['--release', '17'])
}
-}
-
-dependencies {
- /*
- * Must these correspond exactly with the external libraries (JARs)
- * mentioned in the Ant build.xml? Or is some form of dynamic version
- * better for downstream?
- *
- * Note that an application may specify a later version. Gradle will
- * choose the latest required.
- */
-
- // Using a version available from repo (not 'extlibs/servlet-api-2.5' as in build.xml)
- implementation 'javax.servlet:javax.servlet-api:3.1.0'
-
- /*
- * These seem to be unnecessary while the proprietary database support is
- * not bundled with Jython. Applications needing them can cite these or a
- * version they prefer.
- */
- //implementation 'mysql:mysql-connector-java:5.1.42'
- //implementation 'org.postgresql:postgresql:42.1.1.jre7'
-
- // pin to Antlr 3 until we upgrade parsing
- antlr 'org.antlr:antlr:3.5.2'
- implementation 'org.antlr:antlr-runtime:3.5.2'
-
- implementation 'org.apache.commons:commons-compress:1.19'
-
- implementation 'org.bouncycastle:bcpkix-jdk15on:1.62'
- implementation 'org.bouncycastle:bcprov-jdk15on:1.62'
-
- implementation 'org.ow2.asm:asm:7.1'
- implementation 'org.ow2.asm:asm-commons:7.1'
- implementation 'org.ow2.asm:asm-util:7.1'
-
- // The Android Guava and "failureaccess" are necessary to support Java 7.
- implementation 'com.google.guava:guava:28.0-android'
- implementation 'com.google.guava:failureaccess:1.0.1'
- // Swap for regular Guava at Java 8.
-
- implementation 'com.ibm.icu:icu4j:59.1'
- implementation 'com.carrotsearch:java-sizeof:0.0.5'
-
- implementation 'com.github.jnr:jffi:1.2.20'
- implementation 'com.github.jnr:jnr-netdb:1.1.6'
- implementation 'com.github.jnr:jnr-ffi:2.1.10'
- implementation 'com.github.jnr:jnr-posix:3.0.50'
- implementation 'com.github.jnr:jnr-constants:0.9.12'
-
- implementation 'jline:jline:2.14.5'
-
- implementation 'io.netty:netty-buffer:4.1.45.Final'
- implementation 'io.netty:netty-codec:4.1.45.Final'
- implementation 'io.netty:netty-common:4.1.45.Final'
- implementation 'io.netty:netty-handler:4.1.45.Final'
- implementation 'io.netty:netty-resolver:4.1.45.Final'
- implementation 'io.netty:netty-transport:4.1.45.Final'
-
- // Used implicitly in the Ant build, must be explicit here
- implementation 'org.apache.ant:ant:1.9.7'
-
- // Pin to 4.10 until dependency on hamcrest classes resolved.
- testImplementation 'junit:junit:4.10'
-}
-
-
-// ---------------- Resource Processing ----------------------------------------
-
-/*
- * Jython brings several files we could treat as resources, but they do not sit
- * in the Gradle-conventional 'main/resources' directory, rather are in the
- * project root or rub shoulders with the java source. Pick them individually.
- *
- * Several tasks defined below declare that processResources depends on them,
- * with the objective that at the end of processResources all generated
- * resources and the stdlib (but not the compiled stdlib) should be in place
- * in $buildDir/resources/main.
- */
-processResources {
- from(file('.')) {
- include 'LICENSE.txt'
- }
- from(file('src')) {
- include 'META-INF/**'
- include 'org/python/modules/ucnhash.dat'
- }
-}
-
-// ---------------- ANTLR Task -------------------------------------------------
-
-generateGrammarSource {
- maxHeapSize = "512m"
- outputDirectory = file(antlrGenDir)
-}
-
-// ---------------- compleJava Task --------------------------------------------
-
-compileJava {
- // Divert compiled classes to intermediate location pre-exposure.
- destinationDir = file(unexposedDir)
-}
-
-// ---------------- Expose Task ------------------------------------------------
-
-/*
- * The exposer operates between the output of compileJava (unexposed directory)
- * and a second intermediate location (exposed directory). These two the
- * mergeExposed task will finally combine in the Gradle-standard classes
- * directory used as input by the jar task.
- */
-configurations {
- expose.extendsFrom(implementation)
-}
-
-dependencies {
- // The expose (Ant) task depends on classes compiled to here:
- expose files(unexposedDir)
-}
-
-// A (Gradle) task to run the Ant task 'expose'.
-task expose (group: 'Custom', dependsOn: compileJava) {
-
- description = 'Expose Java types to Python using their annotations.'
-
- // Allow Gradle to infer the need to regenreate the outputs
- inputs.files(fileTree("${project.ext.unexposedDir}/org/python"))
- outputs.dir(project.ext.exposedDir)
-
- doLast {
- /*
- * Define an Ant task called 'expose' in the project's AntBuilder.
- * We can't define it until ExposeTask has been compiled.
- */
- ant.taskdef(
- name: 'expose',
- classname: 'org.python.expose.generate.ExposeTask',
- classpath: configurations.expose.asPath
- )
-
- // Use the Gradle-conventional directory structure (not the legacy one).
- ant.expose(
- srcdir: file(project.ext.unexposedDir),
- destdir: mkdir(file(project.ext.exposedDir)),
- includesfile: file('CoreExposed.includes')
- )
- }
-}
-
-// Task to merge the exposed and unexposed classes
-task mergeExposed(group: 'Custom', type:Copy, dependsOn: expose) {
- description = 'Copy exposed Java types to classes.'
- // Exposed version will take precedence
- duplicatesStrategy = DuplicatesStrategy.EXCLUDE
- from file(exposedDir)
- from file(unexposedDir)
- into sourceSets.main.output.classesDirs.singleFile
-}
-
-// Attach to the classes task the placing of all compiled and exposed classes.
-classes.dependsOn(mergeExposed)
-
-
-// ---------------- Version-related file generation ----------------------------
-
-/*
- * Write the information that org.python.Version reads from
- * org/python/version.properties in the class file structure. The inputs to
- * this are: information from Git (git command required); project.version;
- * and project.ext.buildDate. The task works quite hard to decode
- * project.version, which must have the correct form, to deduce whether you
- * really intend this to be a release. If anything comes after the release
- * number, typically it's a '+' sign, the version becomes a snapshot.
- */
-task generateVersionInfo(
- type: WriteProperties,
- description: 'Write the version information as properties') {
-
- outputFile = file("${processResources.destinationDir}/org/python/version.properties")
- comment = ' Jython version information (from build.gradle)'
-
- // Create the properties when the task runs. But do it before the write!
- doFirst {
-
- /*
- * Query Git for version and tagging. The git commands are exactly
- * those used by CPython to create constants configure.ac, but do not
- * correspond very well to their names.
- */
-
- // Not much like a branch. Used only if git_tag is blank.
- def branch = 'git name-rev --name-only HEAD'.execute().text.split('\n', 2)[0]
- property('jython.build.git_branch', branch)
- println " jython.build.git_branch = ${branch}"
-
- // When tagged, the result is "tags/vX.Y.Zrc9", or whatever.
- // When not, tagged it is the (short) revision number.
- def tag = 'git describe --all --always --dirty'.execute().text.split('\n', 2)[0]
- property('jython.build.git_tag', tag)
- println " jython.build.git_tag = ${tag}"
-
- // Revision number (short = 7 hex digits)
- def ident = 'git rev-parse --short HEAD'.execute().text.split('\n', 2)[0]
- property('jython.build.git_version', ident)
- println " jython.build.git_version = ${ident}"
-
- /*
- * Decompose the version string into elements for Jython to access as
- * properties. (The Ant build.xml requires them to be set in parts, but
- * we can work it out from project.version.)
- */
- // .(.)()?(+|-)?
- def versionRegex = /(\d+)\.(\d+)(\.(\d+))?((a|b|rc)(\d+))?(\+|-(\w+))?/
- def versionMatcher = project.version =~ versionRegex
- if (versionMatcher.count != 1) {
- throw new IllegalArgumentException(
- "Cannot parse project version string '${project.version}'")
- }
- // In principle it could match more than once: take the leftmost
- def versionResult = versionMatcher[0]
-
- // . means ..0
- String major = versionResult[1]
- String minor = versionResult[2]
- String micro = versionResult[3] ? versionResult[4] : '0'
-
- // Convert the optional to numbers
- int level = 0, serial = 0
- if (versionResult[5]) {
- // This is some kind of pre-final release (unless snapshot)
- serial = versionResult[7] as int
- switch (versionResult[6]) {
- case 'a': level = 0xa; break // ALPHA release
- case 'b': level = 0xb; break // BETA release
- case 'rc': level = 0xc; break // release candidate
- }
- } else {
- // Not marked as a/b/rc so ostensibly a final release.
- level = 0xf
- }
-
- // Convert optional +|- to -DEV or -SNAPSHOT suffix or empty string
- String snapshotSuffix = versionResult[8];
- if (snapshotSuffix == '+') {
- snapshotSuffix = "-SNAPSHOT"
- }
-
- /*
- * Work out if it looks like a release, or adjust project.version. This logic prevents us
- * releasing from a polluted repo (similar to logic in the Ant build.xml).
- */
- def L = [0:'', 10:'a', 11:'b', 12:'rc', 15:'']
- String release = "$major.$minor.$micro${L[level]}${serial?:''}"
-
- if (snapshotSuffix == null) {
- // The version is named correctly for a release. Make safety checks on the repo.
- String expectTag = "tags/v$release"
- String message = null;
- if (tag.endsWith('-dirty')) {
- message = 'Version-controlled files have been edited since the last commit'
- } else if (tag != expectTag) {
- message = "Change set $ident is not tagged $expectTag."
- } else {
- // Query Git for status: non-empty if uncontrolled (unignored) files.
- String gitStatus = 'git status --porcelain'.execute().text
- if (gitStatus.trim().length() > 0) {
- message = 'Workspace contains uncontrolled files'
- }
- }
- // If a message was set for any reason, fall back to a snapshot.
- if (message == null) {
- // Repository state is good for a full build.
- snapshotSuffix = ''
- } else {
- // Some reason found not to build the release.
- println "$message - build is a snapshot."
- snapshotSuffix = '-SNAPSHOT'
- }
- }
-
- // Rebuild the version with the snapshot suffix, even if not given originally.
- project.version = release + snapshotSuffix
- println "This build is for v${project.version}."
-
- property('jython.version', project.version)
- property('jython.major_version', major)
- property('jython.minor_version', minor)
- property('jython.micro_version', micro)
- property('jython.release_level', level)
- property('jython.release_serial', serial)
-
- /*
- * Time-stamp the build. In the time part, the ':' gets escaped to
- * '\:', consistent with Properties.store(), unlike the Ant build.
- */
- property('jython.build.time',
- (new SimpleDateFormat('HH:mm:ss'))
- .format(project.ext.buildDate))
- property('jython.build.date',
- (new SimpleDateFormat('MMM d yyyy'))
- .format(project.ext.buildDate))
- }
-}
-
-// Attach this task to processResources
-processResources.dependsOn(generateVersionInfo)
-
-
-// ---------------- Copy Python Library ----------------------------------------
-
-/*
- * The default behaviour of the Java plug-in is to make a JAR of the classes in
- * the "main" source set. We need a more complex assembly that provides users
- * with exposed classes instead of their plain counterparts, and also various
- * configuration files and the Python library.
- *
- * These copies include the tests, so we can test things :), but a subsequent
- * JarTask of the build should exclude them as necessary. (Not yet implemented.)
- */
-
-ext {
- libPython = 'lib-python/2.7'
- libJython = 'Lib'
- libTestSpecs = [
- 'distutils/tests/',
- 'email/test/',
- 'json/tests/',
- 'lib2to3/tests/',
- 'unittest/test/',
- 'test/'
- ]
-}
-
-/*
- * Copy the Python standard library. We take this from a distribution of
- * CPython, but take only the files specified in CPythonLib.includes.
- * The Jython version of the standard library will be copied to the same place.
- * Files from the Jython library having the same name (relative path) as one
- * in CPythonLib.includes thereby take precedence.
- */
-task mergePythonLib(
- type: Copy,
- description: 'Merge lib-python and Jython Lib') {
-
- // There might be a way using a collection of File rather than actual copy.
- into pythonLibDir
-
- // Copy Jython Lib, with precedence over CPython files of the same name
- duplicatesStrategy = DuplicatesStrategy.INCLUDE
- from libJython
- exclude '**/*.class'
-
- // Allow Gradle to infer the need to regenerate the outputs
- inputs.dir libJython
- inputs.dir libPython
- inputs.file file('CPythonLib.includes')
-
- doFirst {
- // Select the CPython stdlib files by making a list.
- def cPythonLibIncludes = []
- // Read list from CPythonLib.includes, stripping comments and spaces.
- file('CPythonLib.includes').eachLine { line ->
- def trimmed = line.split('#', 2)[0].trim()
- if (trimmed.length() > 0) {
- cPythonLibIncludes << trimmed
- }
- }
- // Copy the subset as specified by the list
- project.copy {
- into pythonLibDir
- duplicatesStrategy = DuplicatesStrategy.EXCLUDE
- from libPython
- include cPythonLibIncludes
- exclude '**/*.pyc', '**/*.pyd'
- }
- }
-}
-
-/*
- * Copy from the merge location into the main resources, excluding material
- * only needed for tests.
- */
-task copyLib(
- type: Copy,
- dependsOn: mergePythonLib,
- description: 'Copy merged Python library (main excluding tests)') {
- into buildLibDir
- from pythonLibDir
- exclude '**/*.pyc', '**/*.pyd', '**/*.class'
- // Exclude tests and test material
- exclude libTestSpecs
-}
-
-// Attach this task to processResources
-processResources.dependsOn(copyLib)
-
-/*
- * Copy from the merge location into the test resopurces, including only
- * that extra material needed for tests.
- */
-task copyTestLib(
- type: Copy,
- dependsOn: mergePythonLib,
- description: 'Copy merged Python library (tests only)') {
- into buildTestLibDir
- from pythonLibDir
- exclude '**/*.pyd', '**/*.class' // test material includes .pyc files
- // Include only tests and test material
- include libTestSpecs
-}
-
-// Attach this task to processResources
-processTestResources.dependsOn(copyTestLib)
-
-
-// ---------------- Jython-Compile Python --------------------------------------
-
-/*
- * Compile the Python modules to .class files for the JAR. Whereas Jython runs
- * happily with a concrete Lib folder, creating and caching the .class files,
- * when Jython is supplied as a JAR, we prefer to compile the class files once
- * in advance.
- */
-
-configurations {
- pycompile.extendsFrom(implementation)
-}
-
-dependencies {
- // Jython as built so far should be on the path of the jycompile (Ant) task
- pycompile files("$buildDir/classes/java/main")
- pycompile files("$buildDir/resources/main")
-}
-
-// A (Gradle) task to run the Ant task 'jycompile' (not pycompile).
-task pycompile(
- group: 'Custom',
- description: 'Compile the Python modules to .class files for the JAR') {
-
- // Compiler depends on rest of Jython being fully assembled in 'classes'
- dependsOn classes
- // Note that classes depends on processResources (Java plug-in).
-
- // Allow Gradle to infer the need to regenerate the outputs
- inputs.dir project.ext.buildLibDir
- outputs.dir project.ext.compiledLibDir
-
- doFirst {
- /*
- * Define an Ant task called 'jycompile' in the project's AntBuilder.
- * We can't define it until JythoncAntTask has been compiled, so this
- * must happen during the execution of the task (early).
- */
- ant.taskdef(
- name: 'jycompile',
- classname: 'org.python.util.JycompileAntTask',
- classpath: configurations.pycompile.asPath
- )
- }
-
- doLast {
- /*
- * Now use the 'jycompile' Ant task to compile the Python source we
- * supply to users. The exclusions have been copied from build.xml,
- * and also this comment:
-
- */
- def exclusions = ['test/**', 'lib2to3/tests/**',
- 'lib2to3/tests/data/myfixes/**']
- ant.jycompile(
- srcdir: project.ext.buildLibDir,
- destdir: project.ext.compiledLibDir,
- excludes: exclusions.join(',') // Yes, it's that way round :o
- )
- }
-}
-
-
-// ---------------- Building the JARs ------------------------------------------
-
-/*
- * The default behaviour of the Java plug-in is to make a JAR of the classes in
- * the "main" source set and its resources. Having carefully substituted/added
- * exposed classes in the assembled classes directory, and having prepared the
- * (compiled) stdlib as a resource, this is close to what we need, with a few
- * adjustments as noted.
- */
-jar {
-
- // Ensure that compiled stdlib is part of the resources to JAR.
- dependsOn pycompile
-
- // It is important for import that X$py.class be newer than X.py
- preserveFileTimestamps = true
-
- // We don't JAR the expose tool itself
- exclude 'org/python/expose/generate/**'
-
- // Build a custom manifest
- manifest {
- // These attribute values are based on inspecting the ant build
- attributes ([
- 'Main-Class': 'org.python.util.jython',
- 'Built-By': 'build.gradle',
- ])
-
- attributes( [ // Build-Info section
- 'version': project.version,
- 'build-compiler': 'modern',
- 'jdk-target-version': project.targetCompatibility,
- 'debug': compileJava.options.debug,
- 'informix': false,
- 'oracle': false
- ], 'Build-Info' )
- }
-}
-
-/*
- * This is a task complementary to the jar task, taking just the test material.
- * This is not published via the main repositories because it counts as a
- * distinct artefact with its own POM.
- */
-// XXX Consider instead a multi-project build with one artefact per sub-project.
-task testJar(type: Jar) {
- classifier = 'tests'
-
- from sourceSets.test.output
- // We don't JAR the expose tool, so we don't JAR the tests
- exclude 'org/python/expose/generate/**'
-
- // Build a custom manifest
- manifest {
- // These attribute values are based on inspecting the ant build
- attributes ([
- //'Main-Class': 'org.python.util.jython',
- 'Built-By': 'build.gradle',
- ])
-
- attributes( [ // Build-Info section
- 'version': project.version,
- 'build-compiler': 'modern',
- 'jdk-target-version': project.targetCompatibility,
- 'debug': compileTestJava.options.debug
- ], 'Build-Info' )
- }
-}
-
-
-// ---------------- Documentation ----------------------------------------------
-
-/*
- * The JavaDoc, anyway.
- */
-javadoc {
- options.encoding = 'UTF-8'
- source = fileTree(dir: 'src', include: '**/*.java')
-}
-
-// ---------------- Publication ------------------------------------------------
-
-/*
- * Post the JAR we built to a public repository. We provide secondary -source
- * and -javadoc JARs too (supporting 'main').
- *
- * How do we test the artifact actually published is correct? The 'test' task
- * tests Jython laid out in the build directory, not the JAR we propose to
- * distribute.
- *
- * Maybe have a second JAR that contains the additional material necessary to
- * run integration tests (regression tests and others).
- */
-
-task sourcesJar(type: Jar, dependsOn: classes) {
- classifier = 'sources'
- from sourceSets.main.allJava
-}
-
-task javadocJar(type: Jar, dependsOn: javadoc) {
- classifier = 'javadoc'
- from javadoc.destinationDir
-}
-
-publishing {
-
- publications {
- // The production JAR we expect to be cited as a dependency by users
- main(MavenPublication) {
-
- from components.java
-
- // Also provide the source.
- artifact sourcesJar
- // Also provide the docs. (Some javadoc errors currently.)
- artifact javadocJar
-
- pom {
- // Same description as in ~/maven/pom-template
- name = 'Jython'
- description =
- 'Jython is an implementation of the high-level, dynamic, object-oriented\n' +
- 'language Python written in 100% Pure Java, and seamlessly integrated with\n' +
- 'the Java platform. It thus allows you to run Python on any Java platform.'
- url = 'https://www.jython.org/'
-
- // We use the PSF 2.0, but only most recently, and actually a bundle.
- licenses {
- license {
- name = 'Jython Software License'
- // Not actually the license URL, but linked from here.
- url = 'https://www.jython.org/'
- distribution = 'repo'
- }
- }
-
- // Point to Git repositories hosted on GitHub.
- scm {
- connection = 'scm:git:https://github.com/jython/jython.git'
- developerConnection = 'scm:git:ssh://git@github.com:jython/jython.git'
- url = 'https://github.com/jython/jython'
- }
-
- // Could list us all, but why not just the list for now?
- developers {
- developer {
- id = 'jython'
- name = 'Jython Developers'
- email = 'jython-dev@lists.sourceforge.net'
- }
- }
- }
- }
+ tasks.withType(Javadoc) {
+ options.encoding = 'UTF-8'
+ options.showFromPackage()
}
repositories {
- // Staging area where ant -f maven/build.xml will look.
- maven {
- name = 'stagingRepo'
- url = "file://${buildDir}/stagingRepo"
- }
+ mavenLocal()
+ // Jython is distributed through Maven Central. Get our dependencies there too.
+ mavenCentral()
}
}
-// Ensure version computation/branding precedes any publication we use.
-publish.dependsOn(generateVersionInfo)
-
-/* FIXME: Depending on publishMainPublicationToMavenLocal does not work,
- because it does not exist when evaluating this line. Is this the deferred
- configuration removed in Gradle 5.0? Failsd on POM version mismatch if main
- publish task not run before publishMainPublicationToMavenLocal.
-*/
-//publishMainPublicationToMavenLocal.dependsOn(generateVersionInfo)
-
-
-// ---------------- Java unit tests --------------------------------------------
-
-ext {
- //distDir = relativePath("$buildDir/assembly")
- testSourceDir = relativePath('tests/java')
-}
-
-
-dependencies {
- // Put the exposed classes on the path of the test tasks
- testImplementation files(expose)
-}
-
-// Ensure exposed classes are ahead of standard path
-sourceSets.test {
- compileClasspath = files(expose.outputs) + compileClasspath
- runtimeClasspath = files(expose.outputs) + runtimeClasspath
- // println "runtimeClasspath = ${runtimeClasspath.asPath}"
-}
-
-compileTestJava {
- dependsOn expose
- options.debug = project.compileJava.options.debug
-}
-
-test {
-
- dependsOn copyLib
-
- // Stop on first test failure
- failFast = true
-
- // Properties as defined in Ant target javatest-basepath
- // XXX Not sure of all that python.home is used for in tests.
- systemProperty 'python.home', file(copyLib.destinationDir).parent
- systemProperty 'python.test.source.dir', project.ext.testSourceDir
- // Place cache outside the targets for jar task
- systemProperty 'python.cachedir', "${project.buildDir}/cachedir"
- // Logging level: default is message=INFO
- //systemProperty 'python.verbose', 'CONFIG'
-
- include '**/*Test*'
-
- // Exclude based on Ant target javatest-basepath
- exclude '**/InterpTestCase'
- exclude '**/jythonTest*' // Must run interactively
- exclude 'org/python/antlr/**'
- exclude 'org/python/tests/imp/**' // See build.xml:importest
-
- // Some additional exclusions or else the task fails
-
- // FIXME: leaves stdin/out/err as PyFileWriter that has no fileno()
- // causing _ioTest to fail.
- exclude '**/jsr223/*'
-
- // FIXME: Tests that hard-code directory paths (use a symbol):
- exclude 'org/python/compiler/custom_proxymaker/**'
- exclude 'org/python/compiler/JavaMakerSmokeTest.class'
-
- // FIXME: Failing test finds project root from test class location
- exclude 'org/python/core/PySystemState_registry_Test.class'
-
- // FIXME: Fails as sys._jy_console not set when run under Gradle
- exclude 'org/python/util/InterpreterTest.class'
-
- doFirst {
- println "systemProperties = $systemProperties"
- }
-
-}
-
-
-// ---------------- Miscellaneous fettling of the prepare phase ----------------
-
-// Source is globally UTF-8 (well, nearly).
-tasks.withType(JavaCompile) {
- options.encoding = "UTF-8"
-}
-
-
-
-// ---------------- Support for debugging --------------------------------------
-
-
-afterEvaluate { project ->
- //dumpCP()
- //dumpSS()
-}
-
-void dumpCP() {
- println('\nconfigurations.testCompile:')
- configurations.testCompile.each { println it }
- println('\nconfigurations.testRuntime:')
- configurations.testRuntime.each { println it }
- println('\nconfigurations.expose:')
- configurations.expose.each { println it }
- println('\nconfigurations.pycompile:')
- configurations.pycompile.each { println it }
-}
-
-void dumpSS() {
- println '*** source sets ***'
- for (ss in sourceSets) {
- String name = ss.name
- println ss
- println " ${name}.compileConfigurationName = ${ss.compileConfigurationName}"
- println " ${name}.implementationConfigurationName = ${ss.implementationConfigurationName}"
- println " ${name}.runtimeConfigurationName = ${ss.runtimeConfigurationName}"
- println " ${name}.java.srcDirs = ${ss.java.srcDirs}"
- println " ${name}.antlr.srcDirs = ${ss.antlr.srcDirs}"
- println " ${name}.resources.srcDirs = ${ss.resources.srcDirs}"
- println " ${name}.output.dirs = ${ss.output.dirs.files}"
- println " ${name}.output.classesDirs = ${ss.output.classesDirs.files}"
- println " ${name}.output.resourcesDir = ${ss.output.resourcesDir}"
- println " ${name}.classesTaskName = ${ss.classesTaskName}"
- println " ${name}.compileJavaTaskName = ${ss.compileJavaTaskName}"
- println " ${name}.jarTaskName = ${ss.jarTaskName}"
- }
-}
+// ---------------- Version-related file generation ----------------------------
+//
+// Use the same mechanism as Jython 2 when we are ready.
diff --git a/build.xml b/build.xml
deleted file mode 100644
index 7a9b4ec10..000000000
--- a/build.xml
+++ /dev/null
@@ -1,1540 +0,0 @@
-
-
-
-
-
-Case 1: developer build
------------------------
-Use the command:
- ant developer-build
-or just:
- ant
-as it is the default target. This build will create directories
-/build and /dist below ${basedir}.
-Jython will identify its version with a trailing "-DEV".
-
-Case 2: build an installer for the development version
-------------------------------------------------------
-Use the command:
- ant installer
-An installer built this way does not include javadoc or source
-JARs unless you build them first. It will be versioned as a
-snapshot, e.g. ${jython.release}-SNAPSHOT. You can choose another name
-for the snapshot, via a property:
- ant -Dsnapshot.name=MYTEST installer
-
-Case 3: full build for a release (use clean, tagged checkout)
--------------------------------------------------------------
-You do not have to have access to the Jython Git
-repository, but you do need to be at the root of a checked-out
-(i.e. newly cloned) source tree. The release aretefacts will be
-marked as a snapshot (not an official release) if any of the
-following apply:
-- there is no .git directory (this is not a repo),
-- the source tree contains any extraneous files,
-- files have been edited and not committed,
-- the current state is not tagged with correct release,
-- you supply the snapshot.name property.
-
-This will create a big jython-installer-${jython.release}.jar,
-in the artefacts directory.
-
-See also https://jython-devguide.rtfd.io/en/latest/release_jy.html
-
-Note on targets
----------------
-A subset of the available targets are designed for direct invocation.
-Following an ant convention, the callable targets have a description
-attribute. Use ant -p to display these targets. All other targets
-may behave unpredictably if called directly.
-
-Where ant looks for ant.properties
-----------------------------------
- 1. in user.home
- 2. in the same directory as this build.xml file
-The first setting of a property wins. Further settings are ignored.
-
-An example ant.properties file:
--------------------------------
-
-# - zxJDBC
-oracle.jar = ../support/ojdbc6.jar
-informix.jar = ../support/jdbc-4.10.12.jar
-
-# - option for javac (build.compiler=modern is a global option to use standard jdk 1.7/1.8)
-#build.compiler=modern
-#jdk.target.version=1.7
-#debug=false
-#deprecation=off
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- .
- Build environment for ${ant.project.name}
- (Note: if ${propertyname} is displayed, then the property is not set)
- --- build Jython version ---
- jython.version.short = '${jython.version.short}'
- jython.release = '${jython.release}'
- jython.version = '${jython.version}'
- snapshot.name = '${snapshot.name}'
- snapshot.suffix = '${snapshot.suffix}'
- jython.dev.jar = '${jython.dev.jar}'
- jython.deploy.jar = '${jython.deploy.jar}'
- jython.standalone.jar = '${jython.standalone.jar}'
- jython.javadoc.jar = '${jython.javadoc.jar}'
- jython.sources.jar = '${jython.sources.jar}'
- jar.update = '${jar.update}'
- --- optional libraries ---
- informix = '${informix.jar}'
- informix.present = '${informix.present}'
- oracle = '${oracle.jar}'
- oracle.present = '${oracle.present}'
- --- properties ---
- basedir = '${basedir}'
- source.dir = '${source.dir}'
- build.dir = '${build.dir}'
- compile.dir = '${compile.dir}'
- exposed.dir = '${exposed.dir}'
- gensrc.dir = '${gensrc.dir}'
- dist.dir = '${dist.dir}'
- apidoc.dir = '${apidoc.dir}'
- templates.dir = '${templates.dir}'
- templates.lazy = '${templates.lazy}'
- python.lib = '${python.lib}'
- --- compiler options ---
- build.compiler = '${build.compiler}'
- jdk.target.version = '${jdk.target.version}'
- jdk.source.version = '${jdk.source.version}'
- deprecation = '${deprecation}'
- debug = '${debug}'
- nowarn = '${nowarn}'
- main.classpath = '${ant.refid:main.classpath}'
- --- test config ---
- test = '${test}'
- test.source.dir = '${test.source.dir}'
- reports.dir = '${reports.dir}'
-
-
-
-
- --- properties only used for a full-build ---
- git.present = '${git.present}'
- build.git.is_unmodified = '${build.git.is_unmodified}'
- build.git.is_clean = '${build.git.is_clean}'
- build.git.is_tagged = '${build.git.is_tagged}'
- build.git.branch = '${build.git.branch}'
- build.git.tag = '${build.git.tag}'
- build.git.version = '${build.git.version}'
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-${message}
-
-${build.git.status}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Writing git and build metadata to version.properties.
- jython.version = ${jython.version}
- build.git.branch = ${build.git.branch}
- build.git.version = ${build.git.version}
- build.git.tag = ${build.git.tag}
-
-
-
-
-
-
- # Jython version information
-jython.version=${jython.version}
-jython.major_version=${jython.major_version}
-jython.minor_version=${jython.minor_version}
-jython.micro_version=${jython.micro_version}
-jython.release_level=${jython.release_level}
-jython.release_serial=${jython.release_serial}
-jython.build.date=${build.date}
-jython.build.time=${build.time}
-jython.build.git_branch=${build.git.branch}
-jython.build.git_tag=${build.git.tag}
-jython.build.git_version=${build.git.version}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
--------------------------------------------------------------------------
-This is a snapshot build. It reflects the current development status.
-The text for an official release would continue like ...
--------------------------------------------------------------------------
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- copy misc files from ${basedir}
-
-
-
-
-
-
-
-
-
-
-
-
-
- copy sources from ${basedir}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- copy the demo files from ${basedir}/Demo
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- compiling installer from ${installer.src.dir}
-
-
- copy installer classes to ${dist.dir}
-
-
-
-
-
-
-
-
- copy installer icon to ${dist.dir}
-
-
-
-
-
-
-
-
-
- building installer .jar file
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- creating ${bugtests.dir}/support_config.py
-
-
-# this configuration was auto-generated by ant build script,
-# safe to edit by hand (won't be overwritten)
-java_home="${jdk.home}"
-jython_home="${dist.dir}"
-classpath="${ant.refid:bugtest.classpath}"
-
-
-
-
-
-
-
-
-
diff --git a/core/core.gradle b/core/core.gradle
new file mode 100644
index 000000000..fd2120a75
--- /dev/null
+++ b/core/core.gradle
@@ -0,0 +1,152 @@
+/*
+ * core.gradle
+ *
+ * Sub-project of Jython: interpreter core. We to generate and compile the
+ * implementation of the core Python objects, exporting a minimal API.
+ */
+
+plugins {
+ id 'java-library'
+}
+
+dependencies {
+ //implementation 'org.ow2.asm:asm:9.+'
+
+ // JUnit 5 dependencies
+ testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.+'
+ testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.+'
+ testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.+'
+}
+
+
+test {
+ useJUnitPlatform { // JUnit5 as standard
+ includeEngines 'junit-jupiter'
+ }
+}
+
+
+// Sources and destinations when generating Java object implementations
+
+def genSourcesDir = "$buildDir/generated/sources"
+def genOutputDir = "$genSourcesDir/javaTemplate"
+def genJavaDir = "$genOutputDir/java/main"
+def genTestJavaDir = "$genOutputDir/java/test"
+
+def genInputDir = getProject().file('src/main/javaTemplate')
+def genTestInputDir = getProject().file('src/test/javaTemplate')
+
+
+// Source and destinations of Python source test material
+
+// We compile these to code objects to test the CPython interpreter.
+def testPythonExampleInputDir = getProject().file('src/test/pythonExample')
+
+def pythonExampleOutput = "$buildDir/generated/sources/pythonExample"
+def testPythonExampleOutputDir = "$pythonExampleOutput/test"
+
+
+sourceSets {
+
+ main {
+ java {
+ srcDir genJavaDir
+ }
+ }
+
+ test {
+ java {
+ srcDir genTestJavaDir
+ }
+ resources {
+ srcDir testPythonExampleOutputDir
+ }
+ }
+}
+
+
+
+// A task that runs Python with the tools library on the path
+
+class PythonExec extends Exec {
+
+ PythonExec() {
+ executable 'python'
+ environment('PYTHONPATH', getProject().getRootProject().file(
+ 'build-tools/python/lib'))
+ }
+}
+
+
+// Generate Python object definitions
+
+task generateObjectDefinitions(type: PythonExec,
+ description: 'Generate object definitions from templates' ) {
+ args getProject().getRootProject().file(
+ 'build-tools/python/tool/java_object_gen.py')
+ args '--verbose'
+ args genInputDir
+ args genJavaDir
+}
+compileJava.dependsOn(generateObjectDefinitions)
+
+
+// Report the version of Python available
+
+task pythonVersion(type: PythonExec) {
+ args '-V'
+}
+generateObjectDefinitions.dependsOn(pythonVersion)
+
+
+// Copy and compile Python source examples to the build tree
+
+task compileTestPythonExamples(type: PythonExec,
+ description: 'Copy and compile Python test examples') {
+ // Clues for the incremental build
+ inputs.files(
+ fileTree(testPythonExampleInputDir) {
+ include '**/*.py'
+ })
+ outputs.dir(testPythonExampleOutputDir)
+ // Exec task is python -m compile_examples
+ args '-m'
+ args 'compile_examples'
+ args testPythonExampleInputDir
+ args testPythonExampleOutputDir
+}
+processTestResources.dependsOn(compileTestPythonExamples)
+
+
+// Some adjustments to Javadoc to ensure complex tables readable
+
+tasks.withType(Javadoc) {
+
+ options.showFromPackage()
+
+ // addStringOption inserts one "-" for us :/
+ // CSS adjustments (initially only for table style)
+ options.addStringOption("-add-stylesheet",
+ "src/main/javadoc/project-styles.css")
+
+ // Enable "custom" tags used in JDK Javadoc since JSR-335.
+ // https://nipafx.dev/javadoc-tags-apiNote-implSpec-implNote
+ options.tags(
+ "apiNote:a:API Note:",
+ "implSpec:a:Implementation Requirements:",
+ "implNote:a:Implementation Note:",
+ // Unfortunately we must add these standard tags too,
+ // so they come after the ones we want to enable.
+ "param", "return", "throws",
+ "since", "version", "serialData", "see")
+}
+
+
+// Particular things we ask of the compiler in this sub-project.
+// See also ~/build.gradle for tweaks applying to all projects.
+
+tasks.withType(JavaCompile) {
+ // retain parameter names (for use by exposer)
+ options.compilerArgs.add('-parameters')
+}
+
diff --git a/core/src/main/java/org/python/base/InterpreterError.java b/core/src/main/java/org/python/base/InterpreterError.java
new file mode 100644
index 000000000..ca9810b5a
--- /dev/null
+++ b/core/src/main/java/org/python/base/InterpreterError.java
@@ -0,0 +1,51 @@
+// Copyright (c)2021 Jython Developers.
+// Licensed to PSF under a contributor agreement.
+package org.python.base;
+
+/**
+ * Internal error thrown when the Python implementation cannot be
+ * relied on to work. A Python exception (a {@code PyObject} that
+ * might be caught in Python code) is not then appropriate.
+ * Typically thrown during initialisation or for irrecoverable
+ * internal errors.
+ */
+public class InterpreterError extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor specifying a message.
+ *
+ * @param msg a Java format string for the message
+ * @param args to insert in the format string
+ */
+ public InterpreterError(String msg, Object... args) { super(String.format(msg, args)); }
+
+ /**
+ * Constructor specifying a cause and a message.
+ *
+ * @param cause a Java exception behind the interpreter error
+ * @param msg a Java format string for the message
+ * @param args to insert in the format string
+ */
+ public InterpreterError(Throwable cause, String msg, Object... args) {
+ super(String.format(msg, args), cause);
+ }
+
+ /**
+ * Constructor specifying a cause.
+ *
+ * @param cause a Java exception behind the interpreter error
+ */
+ public InterpreterError(Throwable cause) {
+ this(cause, notNull(cause.getMessage(), "(no message)"));
+ }
+
+ /**
+ * @param msg a string or {@code null}
+ * @param defaultMsg a string or {@code null}
+ * @return non-{@code null} {@code msg} or ""
+ */
+ private static String notNull(String msg, String defaultMsg) {
+ return msg != null ? msg : defaultMsg;
+ }
+}
diff --git a/core/src/main/java/org/python/base/MethodKind.java b/core/src/main/java/org/python/base/MethodKind.java
new file mode 100644
index 000000000..a1149d0d1
--- /dev/null
+++ b/core/src/main/java/org/python/base/MethodKind.java
@@ -0,0 +1,42 @@
+package org.python.base;
+
+/**
+ * Enum describing whether a method is an instance, static or class
+ * method (in Python).
+ */
+public enum MethodKind {
+ /**
+ * The method must be defined by a Java static method. An initial
+ * self or module argument is not expected. (If the writer attempts
+ * to declare one it will simply be the first parameter.) In a call
+ * from Python that uses dot notation, which is attribute lookup,
+ * the target object (or its type) is used to find the method, but
+ * is not bound to the first parameter.
+ */
+ /*
+ * In CPython STATIC cannot be used for functions in modules, but we
+ * find it useful to expose Java static methods that way.
+ */
+ STATIC,
+
+ /**
+ * The first argument is self or a module. The method must be
+ * defined either by a Java instance method or by a static method in
+ * which an initial self or module argument is declared. In a call
+ * from Python that uses dot notation, which is attribute lookup,
+ * the target object (or module) is used to find the method, and is
+ * bound to the first parameter.
+ */
+ INSTANCE,
+
+ /**
+ * The first argument is the Python type of the target. The method
+ * must be defined either by a Java static method in which an
+ * initial type argument is declared. In a call from Python that
+ * uses dot notation, which is attribute lookup, the target object's
+ * type is used to find the method, and is bound to the first
+ * parameter.
+ */
+ // CLASS cannot be used for functions in modules.
+ CLASS
+}
diff --git a/core/src/main/java/org/python/base/MissingFeature.java b/core/src/main/java/org/python/base/MissingFeature.java
new file mode 100644
index 000000000..70c88e9e3
--- /dev/null
+++ b/core/src/main/java/org/python/base/MissingFeature.java
@@ -0,0 +1,22 @@
+package org.python.base;
+
+/**
+ * Thrown when we reach a combination of circumstances in the
+ * interpreter that may arise from legitimate use, but we aren't ready
+ * to implement it.
+ *
+ * What does the reference implementation do at this point?
+ */
+public class MissingFeature extends InterpreterError {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor specifying a message.
+ *
+ * @param msg a Java format string for the message
+ * @param args to insert in the format string
+ */
+ public MissingFeature(String msg, Object... args) {
+ super(String.format(msg, args));
+ }
+}
diff --git a/core/src/main/java/org/python/base/package-info.java b/core/src/main/java/org/python/base/package-info.java
new file mode 100644
index 000000000..c6c39ea55
--- /dev/null
+++ b/core/src/main/java/org/python/base/package-info.java
@@ -0,0 +1,11 @@
+/**
+ * The {@code base} package contains classes that support the interpreter
+ * without requiring it to be initialised. (Specifically, they may be used
+ * before the Python type system is in working order, and without causing it to
+ * initialise.)
+ *
+ * Classes {@code public} in this package are intended to be accessible to users
+ * (meaning extension writers and those embedding the interpreter in an
+ * application).
+ */
+package org.python.base;
diff --git a/core/src/main/java/org/python/core/Abstract.java b/core/src/main/java/org/python/core/Abstract.java
new file mode 100644
index 000000000..71685c05d
--- /dev/null
+++ b/core/src/main/java/org/python/core/Abstract.java
@@ -0,0 +1,961 @@
+// Copyright (c)2021 Jython Developers.
+// Licensed to PSF under a contributor agreement.
+package org.python.core;
+
+import java.lang.invoke.MethodHandle;
+import java.util.function.Supplier;
+
+import org.python.base.InterpreterError;
+import org.python.base.MissingFeature;
+import org.python.core.Slot.EmptyException;
+
+/**
+ * The "abstract interface" to operations on Python objects. Methods
+ * here execute the slot functions of the type definition of the
+ * objects passed in. A primary application is to the CPython byte
+ * code interpreter. (Methods here often correspond closely to a
+ * CPython opcode.)
+ *
+ * In CPython, the methods are found in {@code Objects/abstract.c}
+ */
+public class Abstract {
+
+ /**
+ * There are only static methods here, so no instances should be
+ * created. Formally make the constructor {@code protected} so we
+ * can sub-class. (Otherwise {@code private} would be the right
+ * choice.)
+ */
+ protected Abstract() {}
+
+ /**
+ * The equivalent of the Python expression repr(o), and is called by
+ * the repr() built-in function.
+ *
+ * @param o object
+ * @return the string representation o
+ * @throws TypeError if {@code __repr__} returns a non-string
+ * @throws Throwable from invoked implementation of {@code __repr__}
+ */
+ // Compare CPython PyObject_Repr in object.c
+ static Object repr(Object o) throws TypeError, Throwable {
+ if (o == null) {
+ return "";
+ } else {
+ Operations ops = Operations.of(o);
+ try {
+ Object res = ops.op_repr.invoke(o);
+ if (PyUnicode.TYPE.check(res)) {
+ return res;
+ } else {
+ throw returnTypeError("__repr__", "string", res);
+ }
+ } catch (Slot.EmptyException e) {
+ return String.format("<%s object>", PyType.of(o).getName());
+ }
+ }
+ }
+
+ /**
+ * The equivalent of the Python expression str(o).
+ *
+ * @param o object
+ * @return the string representation o
+ * @throws TypeError if {@code __str__} or {@code __repr__} returns
+ * a non-string
+ * @throws Throwable from invoked implementations of {@code __str__}
+ * or {@code __repr__}
+ */
+ // Compare CPython PyObject_Str in object.c
+ static Object str(Object o) throws Throwable {
+ if (o == null) {
+ return "";
+ } else {
+ Operations ops = Operations.of(o);
+ if (PyUnicode.TYPE.checkExact(o)) {
+ return o;
+ } else if (Slot.op_str.isDefinedFor(ops)) {
+ Object res = ops.op_str.invoke(o);
+ if (PyUnicode.TYPE.check(res)) {
+ return res;
+ } else {
+ throw returnTypeError("__str__", "string", res);
+ }
+ } else {
+ return repr(o);
+ }
+ }
+ }
+
+ /**
+ * Convert a given {@code Object} to an instance of a Java class.
+ * Raise a {@code TypeError} if the conversion fails.
+ *
+ * @param target type defined by {@code c}
+ * @param o the {@code Object} to convert.
+ * @param c the class to convert it to.
+ * @return converted value
+ */
+ @SuppressWarnings("unchecked")
+ public static T tojava(Object o, Class c) {
+ try {
+ // XXX Stop-gap implementation (just cast it)
+ if (c.isAssignableFrom(o.getClass())) {
+ return (T)o;
+ } else {
+ throw new Slot.EmptyException();
+ }
+ // XXX Replace when this slot is defined:
+ // return (T)Operations.of(o).op_tojava.invokeExact(o, c);
+ } catch (NullPointerException npe) {
+ // Probably an error, but easily converted.
+ return null;
+ } catch (Slot.EmptyException e) {
+ throw typeError("cannot convert %s to %s", o, c.getName());
+ }
+ }
+
+ /**
+ * Compute and return the hash value of an object. This is the
+ * equivalent of the Python expression {@code hash(v)}.
+ *
+ * @param v to hash
+ * @return the hash
+ * @throws TypeError if {@code v} is an unhashable type
+ * @throws Throwable on errors within {@code __hash__}
+ */
+ static int hash(Object v) throws TypeError, Throwable {
+ try {
+ return (int)Operations.of(v).op_hash.invokeExact(v);
+ } catch (Slot.EmptyException e) {
+ throw typeError("unhashable type: %s", v);
+ }
+ }
+
+ /**
+ * Test a value used as condition in a {@code for} or {@code if}
+ * statement.
+ *
+ * @param v to test
+ * @return if Python-truthy
+ * @throws Throwable from invoked implementations of
+ * {@code __bool__} or {@code __len__}
+ */
+ // Compare CPython PyObject_IsTrue in object.c
+ static boolean isTrue(Object v) throws Throwable {
+ // Begin with common special cases
+ if (v == Py.True)
+ return true;
+ else if (v == Py.False || v == Py.None)
+ return false;
+ else {
+ // Ask the object type through the op_bool or op_len slots
+ Operations ops = Operations.of(v);
+ if (Slot.op_bool.isDefinedFor(ops))
+ return (boolean)ops.op_bool.invokeExact(v);
+ else if (Slot.op_len.isDefinedFor(ops))
+ return 0 != (int)ops.op_len.invokeExact(v);
+ else
+ // No op_bool and no length: claim everything is True.
+ return true;
+ }
+ }
+
+ /**
+ * Perform a rich comparison, raising {@code TypeError} when the
+ * requested comparison operator is not supported.
+ *
+ * @param v left operand
+ * @param w right operand
+ * @param op comparison type
+ * @return comparison result
+ * @throws Throwable from invoked implementations
+ */
+ // Compare CPython PyObject_RichCompare, do_richcompare in object.c
+ static Object richCompare(Object v, Object w, Comparison op) throws Throwable {
+ return op.apply(v, w);
+ }
+
+ /**
+ * Perform a rich comparison with boolean result. This wraps
+ * {@link #richCompare(Object, Object, Comparison)}, converting the
+ * result to Java {@code false} or {@code true}, or throwing
+ * (probably {@link TypeError}), when the objects cannot be
+ * compared.
+ *
+ * @param v left operand
+ * @param w right operand
+ * @param op comparison type
+ * @return comparison result
+ * @throws Throwable from invoked method implementations
+ */
+ // Compare CPython PyObject_RichCompareBool in object.c
+ static boolean richCompareBool(Object v, Object w, Comparison op) throws Throwable {
+ /*
+ * Quick result when objects are the same. Guarantees that identity
+ * implies equality.
+ */
+ if (v == w) {
+ if (op == Comparison.EQ)
+ return true;
+ else if (op == Comparison.NE)
+ return false;
+ }
+ return isTrue(op.apply(v, w));
+ }
+
+ /**
+ * Perform a rich comparison with boolean result. This wraps
+ * {@link #richCompare(Object, Object, Comparison)}, converting the
+ * result to Java {@code false} or {@code true}.
+ *
+ * When the when the objects cannot be compared, the client gets to
+ * choose the exception through the provider {@code exc}. When this
+ * is {@code null}, the return will simply be {@code false} for
+ * incomparable objects.
+ *
+ * @param type of exception
+ * @param v left operand
+ * @param w right operand
+ * @param op comparison type
+ * @param exc supplies an exception of the desired type
+ * @return comparison result
+ * @throws T on any kind of error
+ */
+ static boolean richCompareBool(Object v, Object w, Comparison op,
+ Supplier exc) throws T {
+ try {
+ return richCompareBool(v, w, op);
+ } catch (Throwable e) {
+ if (exc == null)
+ return false;
+ else
+ throw exc.get();
+ }
+ }
+
+ /**
+ * {@code o.name} with Python semantics.
+ *
+ * @param o object to operate on
+ * @param name of attribute
+ * @return {@code o.name}
+ * @throws AttributeError if non-existent etc.
+ * @throws Throwable on other errors
+ */
+ // Compare CPython _PyObject_GetAttr in object.c
+ // Also PyObject_GetAttrString in object.c
+ static Object getAttr(Object o, String name) throws AttributeError, Throwable {
+ // Decisions are based on type of o (that of name is known)
+ Operations ops = Operations.of(o);
+ try {
+ // Invoke __getattribute__.
+ return ops.op_getattribute.invokeExact(o, name);
+ } catch (EmptyException | AttributeError e) {
+ try {
+ // Not found or not defined: fall back on __getattr__.
+ return ops.op_getattr.invokeExact(o, name);
+ } catch (EmptyException ignored) {
+ // __getattr__ not defined, original exception stands.
+ if (e instanceof AttributeError) { throw e; }
+ throw noAttributeError(o, name);
+ }
+ }
+ }
+
+ /**
+ * {@code o.name} with Python semantics.
+ *
+ * @param o object to operate on
+ * @param name of attribute
+ * @return {@code o.name}
+ * @throws AttributeError if non-existent etc.
+ * @throws TypeError if the name is not a {@code str}
+ * @throws Throwable on other errors
+ */
+ // Compare CPython PyObject_GetAttr in object.c
+ static Object getAttr(Object o, Object name) throws AttributeError, TypeError, Throwable {
+ // Decisions are based on types of o and name
+ if (name instanceof String) {
+ return getAttr(o, name);
+ } else if (name instanceof PyUnicode) {
+ return getAttr(o, name.toString());
+ } else {
+ throw attributeNameTypeError(name);
+ }
+ }
+
+ /**
+ * Python {@code o.name}: returning {@code null} when not found (in
+ * place of {@code AttributeError} as would
+ * {@link #getAttr(Object, Object)}). Other exceptions that may be
+ * raised in the process, propagate.
+ *
+ * @param o the object in which to look for the attribute
+ * @param name of the attribute sought
+ * @return the attribute or {@code null}
+ * @throws TypeError if {@code name} is not a Python {@code str}
+ * @throws Throwable on other errors
+ */
+ // Compare CPython _PyObject_LookupAttr in object.c
+ static Object lookupAttr(Object o, Object name) throws TypeError, Throwable {
+ // Corresponds to object.c : PyObject_GetAttr
+ // Decisions are based on types of o and name
+ if (name instanceof String) {
+ return lookupAttr(o, name);
+ } else if (name instanceof PyUnicode) {
+ return lookupAttr(o, name.toString());
+ } else {
+ throw attributeNameTypeError(name);
+ }
+ }
+
+ /**
+ * Python {@code o.name} returning {@code null} when not found (in
+ * place of {@code AttributeError} as would
+ * {@link #getAttr(Object, String)}). Other exceptions that may be
+ * raised in the process, propagate.
+ *
+ * @param o the object in which to look for the attribute
+ * @param name of the attribute sought
+ * @return the attribute or {@code null}
+ * @throws Throwable on other errors than {@code AttributeError}
+ */
+ // Compare CPython _PyObject_LookupAttr in object.c
+ static Object lookupAttr(Object o, String name) throws TypeError, Throwable {
+ // Decisions are based on type of o (that of name is known)
+ try {
+ // Invoke __getattribute__
+ MethodHandle getattro = Operations.of(o).op_getattribute;
+ return getattro.invokeExact(o, name);
+ } catch (EmptyException | AttributeError e) {
+ return null;
+ }
+ }
+
+ /**
+ * {@code o.name = value} with Python semantics.
+ *
+ * @param o object to operate on
+ * @param name of attribute
+ * @param value to set
+ * @throws AttributeError if non-existent etc.
+ * @throws Throwable on other errors
+ */
+ // Compare CPython PyObject_SetAttr in object.c
+ public static void setAttr(Object o, String name, Object value)
+ throws AttributeError, Throwable {
+ // Decisions are based on type of o (that of name is known)
+ try {
+ Operations.of(o).op_setattr.invokeExact(o, name, value);
+ } catch (EmptyException e) {
+ throw attributeAccessError(o, name, Slot.op_setattr);
+ }
+ }
+
+ /**
+ * {@code o.name = value} with Python semantics.
+ *
+ * @param o object to operate on
+ * @param name of attribute
+ * @param value to set
+ * @throws AttributeError if non-existent etc.
+ * @throws TypeError if the name is not a {@code str}
+ * @throws Throwable on other errors
+ */
+ // Compare CPython PyObject_SetAttr in object.c
+ public static void setAttr(Object o, Object name, Object value)
+ throws AttributeError, TypeError, Throwable {
+ if (name instanceof String) {
+ setAttr(o, name, value);
+ } else if (name instanceof PyUnicode) {
+ setAttr(o, name.toString(), value);
+ } else {
+ throw attributeNameTypeError(name);
+ }
+ }
+
+ /**
+ * {@code del o.name} with Python semantics.
+ *
+ * @param o object to operate on
+ * @param name of attribute
+ * @throws AttributeError if non-existent etc.
+ * @throws Throwable on other errors
+ *
+ */
+ // Compare CPython PyObject_DelAttr in abstract.h
+ // which is a macro for PyObject_SetAttr in object.c
+ public static void delAttr(Object o, String name) throws AttributeError, Throwable {
+ // Decisions are based on type of o (that of name is known)
+ try {
+ Operations.of(o).op_delattr.invokeExact(o, name);
+ } catch (EmptyException e) {
+ throw attributeAccessError(o, name, Slot.op_delattr);
+ }
+ }
+
+ /**
+ * {@code del o.name} with Python semantics.
+ *
+ * @param o object to operate on
+ * @param name of attribute
+ * @throws AttributeError if non-existent etc.
+ * @throws TypeError if the name is not a {@code str}
+ * @throws Throwable on other errors
+ */
+ // Compare CPython PyObject_SetAttr in object.c
+ public static void delAttr(Object o, Object name) throws AttributeError, TypeError, Throwable {
+ if (name instanceof String) {
+ delAttr(o, name);
+ } else if (name instanceof PyUnicode) {
+ delAttr(o, name.toString());
+ } else {
+ throw attributeNameTypeError(name);
+ }
+ }
+
+ /**
+ * Get {@code cls.__bases__}, a Python {@code tuple}, by name from
+ * the object invoking {@code __getattribute__}. If {@code cls} does
+ * not define {@code __bases__}, or it is not a {@code tuple},
+ * return {@code null}. In that case, it is customary for the caller
+ * to throw a {@link TypeError}.
+ *
+ * @param cls normally a type object
+ * @return {@code cls.__bases__} or {@code null}
+ * @throws Throwable propagated from {@code __getattribute__}
+ */
+ // Compare CPython abstract_get_bases in abstract.c
+ private static PyTuple getBasesOf(Object cls) throws Throwable {
+ // Should return a tuple: convert anything else to null.
+ Object bases = lookupAttr(cls, "__bases__");
+ // Treat non-tuple as not having the attribute.
+ return bases instanceof PyTuple ? (PyTuple)bases : null;
+ }
+
+ /**
+ * Return {@code true} iff the class {@code derived} is identical to
+ * or derived from the class {@code cls}. The answer is sought along
+ * the MRO of {@code derived} if {@code derived} and {@code cls} are
+ * both Python {@code type} objects, or sub-classes of {@code type},
+ * or by traversal of {@code cls.__bases__} otherwise.
+ *
+ * @param derived candidate derived type.
+ * @param cls type that may be an ancestor of {@code derived}, (but
+ * not a tuple of such).
+ * @return ẁhether {@code derived} is a sub-class of {@code cls} by
+ * these criteria.
+ * @throws TypeError if either input has no {@code __bases__} tuple.
+ * @throws Throwable propagated from {@code __subclasscheck__} or
+ * other causes
+ */
+ // Compare CPython recursive_issubclass in abstract.c
+ // and _PyObject_RealIsSubclass in abstract.c
+ static boolean recursiveIsSubclass(Object derived, Object cls) throws TypeError, Throwable {
+ if (cls instanceof PyType && derived instanceof PyType)
+ // Both are PyType so this is relatively easy.
+ return ((PyType)derived).isSubTypeOf((PyType)cls);
+ else if (getBasesOf(derived) == null)
+ // derived is neither PyType nor has __bases__
+ throw new TypeError("issubclass", 1, "a class", derived);
+ else if (getBasesOf(cls) == null)
+ // cls is neither PyType nor has __bases__
+ throw argumentTypeError("issubclass", 2, "a class or tuple of classes", cls);
+ else
+ // Answer by traversing cls.__bases__ for derived
+ return isSubclassHelper(derived, cls);
+ }
+
+ /**
+ * This is equivalent to the Python expression {@code iter(o)}. It
+ * returns a new Python iterator for the object argument, or the
+ * object itself if it is already an iterator.
+ *
+ * {@code o} must either define {@code __iter__}, which will be
+ * called to obtain an iterator, or define {@code __getitem__}, on
+ * which an iterator will be created. It is guaranteed that the
+ * object returned defines {@code __next__}.
+ *
+ * @param o the claimed iterable object
+ * @return an iterator on {@code o}
+ * @throws TypeError if the object cannot be iterated
+ * @throws Throwable from errors in {@code o.__iter__}
+ */
+ // Compare CPython PyObject_GetIter in abstract.c
+ static Object getIterator(Object o) throws TypeError, Throwable { return getIterator(o, null); }
+
+ /**
+ * Equivalent to {@link #getIterator(Object)}, with the opportunity
+ * to specify the kind of Python exception to raise.
+ *
+ * @param the type of exception to throw
+ * @param o the claimed iterable object
+ * @param exc a supplier (e.g. lambda expression) for the exception
+ * @return an iterator on {@code o}
+ * @throws E to throw if an iterator cannot be formed
+ * @throws Throwable from errors in {@code o.__iter__}
+ */
+ // Compare CPython PyObject_GetIter in abstract.c
+ static Object getIterator(Object o, Supplier exc)
+ throws TypeError, Throwable {
+ Operations ops = Operations.of(o);
+ if (Slot.op_iter.isDefinedFor(ops)) {
+ // o defines __iter__, call it.
+ Object r = ops.op_iter.invokeExact(o);
+ // Did that return an iterator? Check r defines __next__.
+ if (Slot.op_next.isDefinedFor(Operations.of(r))) {
+ return r;
+ } else if (exc == null) { throw returnTypeError("iter", "iterator", r); }
+ } else if (Slot.op_getitem.isDefinedFor(ops)) {
+ // o defines __getitem__: make a (Python) iterator.
+ throw new MissingFeature("PyIterator");
+ }
+
+ // Out of possibilities: throw caller-defined exception
+ if (exc != null) {
+ throw exc.get();
+ } else {
+ throw typeError(NOT_ITERABLE, o);
+ }
+ }
+
+ /**
+ * Return {@code true} if the object {@code o} supports the iterator
+ * protocol (has {@code __iter__}).
+ *
+ * @param o to test
+ * @return true if {@code o} supports the iterator protocol
+ */
+ static boolean iterableCheck(Object o) {
+ return Slot.op_iter.isDefinedFor(Operations.of(o));
+ }
+
+ /**
+ * Return true if the object {@code o} is an iterator (has
+ * {@code __next__}).
+ *
+ * @param o to test
+ * @return true if {@code o} is an iterator
+ */
+ // Compare CPython PyIter_Check in abstract.c
+ static boolean iteratorCheck(Object o) { return Slot.op_next.isDefinedFor(Operations.of(o)); }
+
+ /**
+ * Return the next value from the Python iterator {@code iter}. If
+ * there are no remaining values, returns {@code null}. If an error
+ * occurs while retrieving the item, the exception propagates.
+ *
+ * @param iter the iterator
+ * @return the next item
+ * @throws Throwable from {@code iter.__next__}
+ */
+ // Compare CPython PyIter_Next in abstract.c
+ static Object next(Object iter) throws Throwable {
+ Operations o = Operations.of(iter);
+ try {
+ return o.op_next.invokeExact(iter);
+ } catch (StopIteration e) {
+ return null;
+ } catch (EmptyException e) {
+ throw typeError(NOT_ITERABLE, iter);
+ }
+ }
+
+ // Plumbing -------------------------------------------------------
+
+ /**
+ * Crafted error supporting {@link #getAttr(Object, PyUnicode)},
+ * {@link #setAttr(Object, PyUnicode, Object)}, and
+ * {@link #delAttr(Object, PyUnicode)}.
+ *
+ * @param o object accessed
+ * @param name of attribute
+ * @param slot operation
+ * @return an error to throw
+ */
+ private static TypeError attributeAccessError(Object o, String name, Slot slot) {
+ String mode, kind, fmt = "'%.100s' object has %s attributes (%s.%.50s)";
+ // What were we trying to do?
+ switch (slot) {
+ case op_delattr:
+ mode = "delete ";
+ break;
+ case op_setattr:
+ mode = "assign to ";
+ break;
+ default:
+ mode = "";
+ break;
+ }
+ // Can we even read this object's attributes?
+ Operations ops = Operations.of(o);
+ kind = Slot.op_getattribute.isDefinedFor(ops) ? "only read-only" : "no";
+ // Now we know what to say
+ return new TypeError(fmt, ops, kind, mode, name);
+ }
+
+ // Convenience functions constructing errors --------------------
+
+ private static final String IS_REQUIRED_NOT = "%.200s is required, not '%.100s'";
+ private static final String RETURNED_NON_TYPE = "%.200s returned non-%.200s (type %.200s)";
+ private static final String ARGUMENT_MUST_BE = "%s()%s%s argument must be %s, not '%.200s'";
+ protected static final String NOT_MAPPING = "%.200s is not a mapping";
+ protected static final String NOT_ITERABLE = "%.200s object is not iterable";
+
+ /**
+ * Return {@code true} iff {@code derived} is a Python sub-class of
+ * {@code cls} (including where it is the same class). The answer is
+ * found by traversing the {@code __bases__} tuples recursively,
+ * therefore does not depend on the MRO or respect
+ * {@code __subclasscheck__}.
+ *
+ * @param derived candidate derived type
+ * @param cls type that may be an ancestor of {@code derived}
+ * @return whether {@code derived} is a sub-class of {@code cls}
+ * @throws Throwable from looking up {@code __bases__}
+ */
+ // Compare CPython abstract_issubclass in abstract.c
+ private static boolean isSubclassHelper(Object derived, Object cls) throws Throwable {
+ while (derived != cls) {
+ // Consider the bases of derived
+ PyTuple bases = getBasesOf(derived);
+ int n;
+ // derived is a subclass of cls if any of its bases is
+ if (bases == null || (n = bases.size()) == 0) {
+ // The __bases__ tuple is missing or empty ...
+ return false;
+ } else if (n == 1) {
+ // The answer is the answer for that single base.
+ derived = bases.get(0);
+ } else {
+ // several bases so work through them in sequence
+ for (int i = 0; i < n; i++) {
+ if (isSubclassHelper(bases.get(i), cls))
+ return true;
+ }
+ // And not otherwise
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Create a {@link TypeError} with a message involving the type of
+ * {@code args[0]} and optionally other arguments.
+ *
+ * @param fmt format for message with a {@code %s} first
+ * @param args arguments to the formatted message, where Python type
+ * name of {@code args[0]} will replace it
+ * @return exception to throw
+ */
+ public static TypeError typeError(String fmt, Object... args) {
+ args[0] = PyType.of(args[0]).getName();
+ return new TypeError(fmt, args);
+ }
+
+ /**
+ * Create a {@link TypeError} with a message along the lines "T
+ * indices must be integers or slices, not X" involving the a target
+ * type T and a purported index type X presented, e.g. "list indices
+ * must be integers or slices, not str".
+ *
+ * @param t type of target of function or operation
+ * @param x type of object presented as an index
+ * @return exception to throw
+ */
+ static TypeError indexTypeError(PyType t, PyType x) {
+ String fmt = "%.200s indices must be integers or slices, not %.200s";
+ return new TypeError(fmt, t.getName(), x.getName());
+ }
+
+ /**
+ * Create a {@link TypeError} with a message along the lines "T
+ * indices must be integers or slices, not X" involving the type
+ * name T of a target {@code o} and the type name X of {@code i}
+ * presented as an index, e.g. "list indices must be integers or
+ * slices, not str".
+ *
+ * @param o target of function or operation
+ * @param i actual object presented as an index
+ * @return exception to throw
+ */
+ static TypeError indexTypeError(Object o, Object i) {
+ return indexTypeError(PyType.of(o), PyType.of(i));
+ }
+
+ /**
+ * Create a {@link TypeError} with a message along the lines "T is
+ * required, not X" involving any descriptive phrase T and the type
+ * X of {@code o}, e.g. "a bytes-like object is required, not
+ * 'str'".
+ *
+ * @param t expected kind of thing
+ * @param o actual object involved
+ * @return exception to throw
+ */
+ static TypeError requiredTypeError(String t, Object o) {
+ return new TypeError(IS_REQUIRED_NOT, t, PyType.of(o).getName());
+ }
+
+ /**
+ * Create a {@link TypeError} with a message along the lines
+ * "attribute name must be string, not 'X'" giving the type X of
+ * {@code name}.
+ *
+ * @param name actual object offered as a name
+ * @return exception to throw
+ */
+ static TypeError attributeNameTypeError(Object name) {
+ String fmt = "attribute name must be string, not '%.200s'";
+ return new TypeError(fmt, PyType.of(name).getName());
+ }
+
+ /**
+ * Create a {@link TypeError} with a message along the lines "can't
+ * set attributes of X" giving str of {@code name}.
+ *
+ * @param obj actual object on which setting failed
+ * @return exception to throw
+ */
+ static TypeError cantSetAttributeError(Object obj) {
+ return new TypeError("can't set attributes of %.200s", obj);
+ }
+
+ /**
+ * Create a {@link TypeError} with a message along the lines "F()
+ * [name] argument must be T, not X", involving a function name, an
+ * argument name, an expected type description T and the type X of
+ * {@code o}, e.g. "split() separator argument must be str or None,
+ * 'tuple'".
+ *
+ * @param f name of function or operation
+ * @param name of argument
+ * @param t describing the expected kind of argument
+ * @param o actual argument (not its type)
+ * @return exception to throw
+ */
+ public static TypeError argumentTypeError(String f, String name, String t, Object o) {
+ String space = name.length() == 0 ? "" : " ";
+ return new TypeError(ARGUMENT_MUST_BE, f, space, name, t, PyType.of(o).getName());
+ }
+
+ /**
+ * Create a {@link TypeError} with a message along the lines "F()
+ * [nth] argument must be T, not X", involving a function name,
+ * optionally an ordinal n, an expected type description T and the
+ * type X of {@code o}, e.g. "int() argument must be a string, a
+ * bytes-like object or a number, not 'list'" or "complex() second
+ * argument must be a number, not 'type'".
+ *
+ * @param f name of function or operation
+ * @param n ordinal of argument: 1 for "first", etc., 0 for ""
+ * @param t describing the expected kind of argument
+ * @param o actual argument (not its type)
+ * @return exception to throw
+ */
+ public static TypeError argumentTypeError(String f, int n, String t, Object o) {
+ return argumentTypeError(f, ordinal(n), t, o);
+ }
+
+ // Helper for argumentTypeError
+ private static String ordinal(int n) {
+ switch (n) {
+ case 0:
+ return "";
+ case 1:
+ return " first";
+ case 2:
+ return " second";
+ case 3:
+ return " third";
+ default:
+ return String.format(" %dth", n);
+ }
+ }
+
+ /**
+ * Create a {@link TypeError} with a message along the lines "F
+ * returned non-T (type X)" involving a function name, an expected
+ * type T and the type X of {@code o}, e.g. "__int__ returned
+ * non-int (type str)".
+ *
+ * @param f name of function or operation
+ * @param t expected type of return
+ * @param o actual object returned
+ * @return exception to throw
+ */
+ static TypeError returnTypeError(String f, String t, Object o) {
+ return new TypeError(RETURNED_NON_TYPE, f, t, PyType.of(o).getName());
+ }
+
+ /**
+ * Create a {@link AttributeError} with a message along the lines
+ * "'T' object has no attribute N", where T is the type of the
+ * object accessed.
+ *
+ * @param v object accessed
+ * @param name of attribute
+ * @return exception to throw
+ */
+ static AttributeError noAttributeError(Object v, Object name) {
+ return noAttributeOnType(PyType.of(v), name);
+ }
+
+ /**
+ * Create a {@link AttributeError} with a message along the lines
+ * "'T' object has no attribute N", where T is the type given.
+ *
+ * @param type of object accessed
+ * @param name of attribute
+ * @return exception to throw
+ */
+ static AttributeError noAttributeOnType(PyType type, Object name) {
+ String fmt = "'%.50s' object has no attribute '%.50s'";
+ return new AttributeError(fmt, type.getName(), name);
+ }
+
+ /**
+ * Create a {@link TypeError} with a message along the lines "N must
+ * be set to T, not a X object" involving the name N of the
+ * attribute, any descriptive phrase T and the type X of
+ * {@code value}, e.g. "__dict__ must be set to a
+ * dictionary, not a 'list' object".
+ *
+ * @param name of the attribute
+ * @param kind expected kind of thing
+ * @param value provided to set this attribute in some object
+ * @return exception to throw
+ */
+ static TypeError attrMustBe(String name, String kind, Object value) {
+ String msg = "%.50s must be set to %.50s, not a '%.50s' object";
+ return new TypeError(msg, name, kind, PyType.of(value).getName());
+ }
+
+ /**
+ * Create a {@link TypeError} with a message along the lines "N must
+ * be set to a string, not a X object".
+ *
+ * @param name of the attribute
+ * @param value provided to set this attribute in some object
+ * @return exception to throw
+ */
+ static TypeError attrMustBeString(String name, Object value) {
+ return attrMustBe(name, "a string", value);
+ }
+
+ /**
+ * Create a {@link AttributeError} with a message along the lines
+ * "'T' object attribute N is read-only", where T is the type of the
+ * object accessed.
+ *
+ * @param v object accessed
+ * @param name of attribute
+ * @return exception to throw
+ */
+ static AttributeError readonlyAttributeError(Object v, Object name) {
+ return readonlyAttributeOnType(PyType.of(v), name);
+ }
+
+ /**
+ * Create a {@link AttributeError} with a message along the lines
+ * "'T' object attribute N is read-only", where T is the type given.
+ *
+ * @param type of object accessed
+ * @param name of attribute
+ * @return exception to throw
+ */
+ static AttributeError readonlyAttributeOnType(PyType type, Object name) {
+ String fmt = "'%.50s' object attribute '%s' is read-only";
+ return new AttributeError(fmt, type.getName(), name);
+ }
+
+ /**
+ * Create a {@link AttributeError} with a message along the lines
+ * "'T' object attribute N cannot be deleted", where T is the type
+ * of the object accessed.
+ *
+ * @param v object accessed
+ * @param name of attribute
+ * @return exception to throw
+ */
+ static AttributeError mandatoryAttributeError(Object v, Object name) {
+ return mandatoryAttributeOnType(PyType.of(v), name);
+ }
+
+ /**
+ * Create a {@link AttributeError} with a message along the lines
+ * "'T' object attribute N cannot be deleted", where T is the type
+ * given.
+ *
+ * @param type of object accessed
+ * @param name of attribute
+ * @return exception to throw
+ */
+ static AttributeError mandatoryAttributeOnType(PyType type, Object name) {
+ String fmt = "'%.50s' object attribute '%s' cannot be deleted";
+ return new AttributeError(fmt, type.getName(), name);
+ }
+
+ /**
+ * Create an {@link IndexError} with a message along the lines "N
+ * index out of range", where N is usually a function or type name.
+ *
+ * @param name object accessed
+ * @return exception to throw
+ */
+ static IndexError indexOutOfRange(String name) {
+ String fmt = "%.50s index out of range";
+ return new IndexError(fmt, name);
+ }
+
+ /**
+ * Submit a {@code DeprecationWarning} call (which may result in an
+ * exception) with the same message as
+ * {@link #returnTypeError(String, String, Object)}, the whole
+ * followed by one about deprecation of the facility.
+ *
+ * @param f name of function or operation
+ * @param t expected type of return
+ * @param o actual object returned
+ * @return {@code o}
+ */
+ static Object returnDeprecation(String f, String t, Object o) {
+ // Warnings.format(DeprecationWarning.TYPE, 1,
+ // RETURNED_NON_TYPE_DEPRECATION, f, t,
+ // PyType.of(o).getName(), t);
+ return o;
+ }
+
+ private static final String RETURNED_NON_TYPE_DEPRECATION =
+ RETURNED_NON_TYPE + ". " + "The ability to return an instance of a strict "
+ + "subclass of %s is deprecated, and may be "
+ + "removed in a future version of Python.";
+
+ /** Throw generic something went wrong internally (last resort). */
+ static void badInternalCall() {
+ throw new InterpreterError("bad internal call");
+ }
+
+ /**
+ * Create an {@link InterpreterError} for use where a Python method
+ * (or special method) implementation receives an argument that
+ * should be impossible in a correct interpreter. This is a sort of
+ * {@link TypeError} against the {@code self} argument, but
+ * occurring where no programming error should be able to induce it
+ * (e.g. coercion fails after we have passed the check that
+ * descriptors make on their {@code obj}, or when invoking a special
+ * method found via an {@link Operations} object.
+ *
+ * @param d expected kind of argument
+ * @param o actual argument (not its type)
+ * @return exception to throw
+ */
+ static InterpreterError impossibleArgumentError(String d, Object o) {
+ return new InterpreterError(IMPOSSIBLE_CLASS, d, o.getClass().getName());
+ }
+
+ private static final String IMPOSSIBLE_CLASS =
+ "expected %.50s argument but found impossible Java class %s";
+}
diff --git a/core/src/main/java/org/python/core/AbstractPyObject.java b/core/src/main/java/org/python/core/AbstractPyObject.java
new file mode 100644
index 000000000..82930e046
--- /dev/null
+++ b/core/src/main/java/org/python/core/AbstractPyObject.java
@@ -0,0 +1,37 @@
+// Copyright (c)2021 Jython Developers.
+// Licensed to PSF under a contributor agreement.
+package org.python.core;
+
+/**
+ * Class that may be used as a base for Python objects (but doesn't have to be)
+ * to supply some universally needed methods and the type.
+ */
+abstract class AbstractPyObject implements CraftedPyObject {
+
+ private PyType type;
+
+ /**
+ * Constructor specifying the Python type, as returned by {@link #getType()}. As
+ * this is a base for the implementation of all sorts of Python types, it needs
+ * to be told which one it is.
+ *
+ * @param type actual Python type being created
+ */
+ protected AbstractPyObject(PyType type) { this.type = type; }
+
+ @Override
+ public PyType getType() { return type; }
+
+ @Override
+ public String toString() { return Py.defaultToString(this); }
+
+ // slot functions -------------------------------------------------
+ /*
+ * It should be possible to declare special (instance) methods in this class to
+ * save work in implementation classes of Python types. The processing of
+ * special methods would treat them as defined afresh by each exposed
+ * implementation (each class that calls PyType.fromSpec()). This may be
+ * undesirable where sub-classes that are object implementations should instead
+ * Python-inherit their definition.
+ */
+}
diff --git a/core/src/main/java/org/python/core/ArgParser.java b/core/src/main/java/org/python/core/ArgParser.java
new file mode 100644
index 000000000..563be1680
--- /dev/null
+++ b/core/src/main/java/org/python/core/ArgParser.java
@@ -0,0 +1,1388 @@
+// Copyright (c)2022 Jython Developers.
+// Licensed to PSF under a contributor agreement.
+package org.python.core;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.StringJoiner;
+
+import org.python.base.InterpreterError;
+import org.python.base.MethodKind;
+
+/**
+ * This class provides a parser for the positional and keyword
+ * arguments supplied during a call to a built-in function or
+ * method. The purpose of an argument parser is to provide the body
+ * of a function, or perhaps a {@code MethodHandle} with an array of
+ * values, corresponding in order and number to its parameters
+ * (formal arguments).
+ *
+ * This parser transforms several argument presentations that occur
+ * in a Python implementation, and arranges them into an array. This
+ * array is either created by the parser, or designated by the
+ * caller. The parser may therefore be used to prepare arguments for
+ * a pure a Java method (or {@code MethodHandle}) that accepts an
+ * array, or to insert arguments as initial values of local
+ * variables in an an optimised interpreter frame ({@link PyFrame}).
+ *
+ * The fields of the parser that determine the acceptable numbers of
+ * positional arguments and their names are essentially those of a
+ * {@code code} object ({@link PyCode}). Defaults are provided
+ * values that mirror the defaults built into a {@code function}
+ * object ({@link PyFunction}).
+ *
+ * Consider for example a function that in Python would have the
+ * function definition:
Note that "aa" and "kk" are at the end of the parameter
+ * names. (This is how a CPython frame is laid out.)
+ *
+ * Defaults are provided, after the parser has been constructed, as
+ * values corresponding to parameter names, when right-justified in
+ * the space to which they apply. (See diagram below.) Both the
+ * positional and keyword defaults are given by position in this
+ * formulation. The {@link #kwdefaults(Object...)} call is allowed
+ * to supply {@code null} values at positions it does not define.
+ *
+ * When parsed to an array, the layout of the argument values, in
+ * relation to fields of the parser will be as follows.
+ *
+ *
A Python {@code frame}
+ *
+ *
names
+ *
a
+ *
b
+ *
c
+ *
d
+ *
e
+ *
f
+ *
g
+ *
h
+ *
i
+ *
aa
+ *
kk
+ *
+ *
+ *
layout
+ *
posOnly
+ *
+ *
kwOnly
+ *
+ *
+ *
+ *
defaults
+ *
kwdefaults
+ *
+ *
+ *
+ * The most readable way of specifying a parser (although one that
+ * is a little costly to construct) is to list the parameters as
+ * they would be declared in Python, including the furniture that
+ * marks up the positional-only, keyword-only, positional varargs,
+ * and keyword varargs. This is the API offered by
+ * {@link #fromSignature(String, String...)}. In practice we only
+ * use this in unit tests. For serious applications we construct the
+ * {@code ArgParser} with a complex of arguments derived by
+ * inspection of the Java or Python signature.
+ */
+class ArgParser {
+
+ // Compare code object (and CPython _Arg_Parser in modsupport.h)
+
+ /** Empty names array. */
+ private static final String[] NO_STRINGS = new String[0];
+
+ /** Empty object array. */
+ private static final Object[] NO_OBJECTS = new Object[0];
+
+ /** The name of the function, mainly for error messages. */
+ final String name;
+
+ /**
+ * The kind of object (type or module) in which the method is found.
+ * This makes a difference to the signature reported for an instance
+ * method.
+ */
+ final ScopeKind scopeKind;
+
+ /**
+ * The kind of method (instance, static or class) that this parser
+ * works for.
+ */
+ final MethodKind methodKind;
+
+ /**
+ * Names of parameters that could be satisfied by position or
+ * keyword, including the collector parameters. Elements are
+ * guaranteed to be interned, and not {@code null} or empty. The
+ * array must name all the parameters, of which there are:
+ * {@code argcount + kwonlyargcount
+ + (hasVarArgs() ? 1 : 0) + (hasVarKeywords() ? 1 : 0)}
+ *
+ * It is often is longer since it suits us to re-use an array that
+ * names all the local variables of a frame.
+ */
+ /*
+ * Here and elsewhere we use the same field names as the CPython
+ * code, even though it tends to say "argument" when it could mean
+ * that or "parameter". In comments and documentation
+ * "positional parameter" means a parameter eligible to be satisfied
+ * by an argument given by position.
+ */
+ final String[] argnames;
+
+ /**
+ * The number of positional or keyword parameters, excluding the
+ * "collector" ({@code *args} and {@code **kwargs}) parameters, and
+ * any data that may follow the legitimate parameter names. Equal to
+ * {@code argcount + kwonlyargcount}.
+ */
+ final int regargcount;
+
+ /** The number of positional parameters. */
+ final int argcount;
+
+ /**
+ * The number of parameters that can only be satisfied by arguments
+ * given by position. This differs from {@link #argcount} by
+ * excluding parameters that may be given by keyword or position.
+ */
+ final int posonlyargcount;
+
+ /** The number of keyword-only parameters. */
+ final int kwonlyargcount;
+
+ /**
+ * The documentation string of the method.
+ */
+ String doc;
+
+ /**
+ * The (positional) default parameters or {@code null} if there are
+ * none.
+ */
+ // Compare CPython PyFunctionObject::func_defaults
+ private Object[] defaults;
+
+ /**
+ * The keyword defaults, may be a {@code dict} or {@code null} if
+ * there are none.
+ */
+ // Compare CPython PyFunctionObject::func_kwdefaults
+ private Map