Skip to content

Issue-374 Add unit tests and improve solution #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: issue-374-integer-type-hint
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 19 additions & 19 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,27 @@ on:
jobs:
test:
runs-on: '${{ matrix.os }}'
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
os: [ ubuntu-18.04 ]
os: [ ubuntu-22.04 ]
java-version: [ 8 ]
python-version: [ '2.7', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10' ]
python-version: [ '3.8' ]
include:
- os: windows-2019
java-version: 17
python-version: '3.10'
- os: ubuntu-18.04
java-version: 11
python-version: '2.7'
- os: ubuntu-18.04
java-version: 17
python-version: '3.8'
name: Py ${{ matrix.python-version }}, Java ${{ matrix.java-version }}, ${{ matrix.os }}
- os: macos-14
java-version: 8
python-version: '3.12'
# - os: windows-2019
# java-version: 11
# python-version: '3.10'
# - os: ubuntu-22.04
# java-version: 17
# python-version: '3.11'
# - os: ubuntu-22.04
# java-version: 21
# python-version: '3.9'
name: Python ${{ matrix.python-version }}, Java ${{ matrix.java-version }}, ${{ matrix.os }}
steps:
- uses: actions/checkout@1e204e9a9253d643386038d443f96446fa156a97 # [email protected]

Expand Down Expand Up @@ -57,12 +62,7 @@ jobs:
./gradlew clean
shell: bash

- name: Enable IPV6
if: ${{ runner.os != 'Windows' }}
run: |
echo 0 | sudo tee /proc/sys/net/ipv6/conf/all/disable_ipv6

- name: Run gradle tests
- name: Run Gradle tests
run: |
cd py4j-java
./gradlew check
Expand All @@ -82,7 +82,7 @@ jobs:
echo `java -version`
echo $JAVA_HOME
# Java TLS tests are disabled until they can be fixed (refs #441)
pytest -k "not java_tls_test."
pytest -k "not java_tls_test." -vvv -s

test-doc:
name: Documentation build
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ We follow pep8 rather stricly:
3. Line length is 80
4. Code must pass the default flake8 (version 2.5) tests (pep8 + pyflakes)

Code must be compatible with Python 2.7 and from 3.4 to the newest released
Code must be compatible with Python 3.8 to the newest released
version of Python.

If external libraries must be used, they should be wrapped in a mechanism that
Expand Down
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ documentation fixes. Please visit the `contributing guide
<https://www.py4j.org/contributing.html>`_ for more
information.

.. image:: https://img.shields.io/github/workflow/status/bartdag/py4j/test.svg
:target: https://github.com/bartdag/py4j/actions/workflows/test.yml
.. image:: https://img.shields.io/github/actions/workflow/status/py4j/py4j/test.yml.svg
:target: https://github.com/py4j/py4j/actions/workflows/test.yml

.. image:: https://img.shields.io/pypi/l/py4j.svg

Expand Down
4 changes: 2 additions & 2 deletions py4j-java/src/test/java/py4j/commands/DirCommandTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ public class DirCommandTest {
{
// Defined in ExampleClass
ExampleClassMethods.addAll(Arrays.asList(new String[] { "method1", "method2", "method3", "method4", "method5",
"method6", "method7", "method8", "method9", "method10", "method11", "getList", "getField1", "setField1",
"getStringArray", "getIntArray", "callHello", "callHello2", "static_method", "getInteger",
"method6", "method7", "method8", "method9", "method10", "method11", "method12", "getList", "getField1",
"setField1", "getStringArray", "getIntArray", "callHello", "callHello2", "static_method", "getInteger",
"getBrokenStream", "getStream", "sleepFirstTimeOnly" }));
// Defined in Object
ExampleClassMethods.addAll(Arrays
Expand Down
15 changes: 15 additions & 0 deletions py4j-java/src/test/java/py4j/examples/ExampleClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public class ExampleClass {
Expand Down Expand Up @@ -174,6 +175,20 @@ public BigInteger method11(BigInteger bi) {
return bi.add(new BigInteger("1"));
}

public int method12(HashSet<Object> set) {
Object element = set.stream().findAny().get();
if (element instanceof Long) {
return 4;
}
if (element instanceof Integer) {
return 1;
}
if (element instanceof String) {
return 2;
}
return 3;
}

@SuppressWarnings("unused")
private int private_method() {
return 0;
Expand Down
8 changes: 2 additions & 6 deletions py4j-python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,11 @@
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Java",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Object Brokering",
Expand Down
9 changes: 9 additions & 0 deletions py4j-python/src/py4j/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@
ITERATOR_TYPE = "g"
PYTHON_PROXY_TYPE = "f"

class TypeHint:
"""Enables users to provide a hint to the Python to Java converter specifying the accurate data type for a given value.
Essential to enforce i.e. correct number type, like Long."""
def __init__(self, value, java_type):
self.value = value
self.java_type = java_type

# Protocol
END = "e"
ERROR = "x"
Expand Down Expand Up @@ -273,6 +280,8 @@ def get_command_part(parameter, python_proxy_pool=None):

if parameter is None:
command_part = NULL_TYPE
elif isinstance(parameter, TypeHint):
command_part = parameter.java_type + smart_decode(parameter.value)
elif isinstance(parameter, bool):
command_part = BOOLEAN_TYPE + smart_decode(parameter)
elif isinstance(parameter, Decimal):
Expand Down
10 changes: 5 additions & 5 deletions py4j-python/src/py4j/tests/client_server_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def testSendObjects(self):
start_gc_test=True, join=False) as p:
p.join()
client_server.shutdown()
self.assertEquals(1000, hello.calls)
self.assertEqual(1000, hello.calls)

def testCleanConnections(self):
"""This test intentionally create multiple connections in multiple
Expand Down Expand Up @@ -605,9 +605,9 @@ def testMultiClientServerWithSharedJavaThread(self):
self.assertNotEqual(sharedPythonThreadId0, sharedPythonThreadId1)
# Check that the Python thread id does not change between
# invocations
self.assertEquals(sharedPythonThreadId0,
self.assertEqual(sharedPythonThreadId0,
entry0.getSharedPythonThreadId())
self.assertEquals(sharedPythonThreadId1,
self.assertEqual(sharedPythonThreadId1,
entry1.getSharedPythonThreadId())

# ## 3 Hops to Shared Java Thread
Expand Down Expand Up @@ -648,9 +648,9 @@ def testMultiClientServer(self):
# ## 0 Hops to Thread ID

# Check that the two thread getters get the same thread
self.assertEquals(thisThreadId,
self.assertEqual(thisThreadId,
int(threadIdGetter0.getThreadId()))
self.assertEquals(thisThreadId,
self.assertEqual(thisThreadId,
int(threadIdGetter1.getThreadId()))

# ## 1 Hop to Thread ID
Expand Down
1 change: 1 addition & 0 deletions py4j-python/src/py4j/tests/java_dir_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
# overloaded
"method10",
"method11",
"method12",
"getList",
"getField1",
"setField1",
Expand Down
18 changes: 11 additions & 7 deletions py4j-python/src/py4j/tests/java_gateway_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
set_default_callback_accept_timeout, GatewayConnectionGuard,
get_java_class)
from py4j.protocol import (
Py4JError, Py4JJavaError, Py4JNetworkError, decode_bytearray,
encode_bytearray, escape_new_line, unescape_new_line, smart_decode)
Py4JError, Py4JJavaError, Py4JNetworkError, TypeHint, LONG_TYPE,
decode_bytearray, encode_bytearray, escape_new_line, unescape_new_line, smart_decode)


SERVER_PORT = 25333
Expand Down Expand Up @@ -364,8 +364,8 @@ def testNoneArg(self):
try:
ex.method2(None)
ex2 = ex.method4(None)
self.assertEquals(ex2.getField1(), 3)
self.assertEquals(2, ex.method7(None))
self.assertEqual(ex2.getField1(), 3)
self.assertEqual(2, ex.method7(None))
except Exception:
print_exc()
self.fail()
Expand Down Expand Up @@ -439,11 +439,11 @@ def testSetField(self):
ex = self.gateway.getNewExample()

set_field(ex, "field10", 2334)
self.assertEquals(get_field(ex, "field10"), 2334)
self.assertEqual(get_field(ex, "field10"), 2334)

sb = self.gateway.jvm.java.lang.StringBuffer("Hello World!")
set_field(ex, "field21", sb)
self.assertEquals(get_field(ex, "field21").toString(), "Hello World!")
self.assertEqual(get_field(ex, "field21").toString(), "Hello World!")

self.assertRaises(Exception, set_field, ex, "field1", 123)

Expand Down Expand Up @@ -582,6 +582,7 @@ def testGCCollectNoMemoryManagement(self):
enable_memory_management=False))
gc.collect()
# Should have nothing in the finalizers
print(ThreadSafeFinalizer.finalizers)
self.assertEqual(len(ThreadSafeFinalizer.finalizers), 0)

def internal():
Expand All @@ -607,7 +608,7 @@ def internal():
class TypeConversionTest(unittest.TestCase):
def setUp(self):
self.p = start_example_app_process()
self.gateway = JavaGateway()
self.gateway = JavaGateway(auto_convert=True)

def tearDown(self):
safe_shutdown(self)
Expand All @@ -619,6 +620,8 @@ def testLongInt(self):
self.assertEqual(4, ex.method7(2147483648))
self.assertEqual(4, ex.method7(-2147483649))
self.assertEqual(4, ex.method7(long(2147483648)))
self.assertEqual(4, ex.method7(TypeHint(1, LONG_TYPE)))
self.assertEqual(4, ex.method12({TypeHint(1, LONG_TYPE)}))
self.assertEqual(long(4), ex.method8(3))
self.assertEqual(4, ex.method8(3))
self.assertEqual(long(4), ex.method8(long(3)))
Expand Down Expand Up @@ -1032,6 +1035,7 @@ def testAccessSubprocess(self):
self.gateway = JavaGateway.launch_gateway()
self.assertTrue(self.gateway.java_process)

@unittest.skipIf(sys.platform.startswith("win"), "Flaky on Windows")
def testShutdownSubprocess(self):
self.gateway = JavaGateway.launch_gateway()
self.assertTrue(self.gateway.java_process)
Expand Down
21 changes: 21 additions & 0 deletions py4j-web/advanced_topics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,27 @@ Java methods slightly less efficient because in the worst case, Py4J needs to
go through all registered converters for all parameters. This is why automatic
conversion is disabled by default.

.. _explicit_conversion:

Explicit converting Python objects to Java primitives
-----------------------------------------------------

Sometimes, especially when ``auto_convert=True`` it is difficult to enforce correct type
passed from Python to Java. Then, ``TypeHint`` from ``py4j.protocol`` may be used.
``java_type`` argument of constructor should be one of Java types defined in ``py4j.protocol``.

So if you have method in Java like:

.. code-block:: java

void method(HashSet<Long> longs) {}

Then you can pass arguments with correct type to this method with ``TypeHint``

::

>>> set_with_longs = { TypeHint(1, LONG_TYPE), TypeHint(2, LONG_TYPE) }
>>> gateway.jvm.my.Class().method(set_with_longs)

.. _py4j_exceptions:

Expand Down
2 changes: 1 addition & 1 deletion py4j-web/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ We follow pep8 rather strictly:
3. Line length is 80.
4. Code must pass the default flake8 tests (pep8 + pyflakes).

Code must be compatible with Python 2.7 and from 3.4 to the newest released
Code must be compatible with Python 3.8 to the newest released
version of Python.

If external libraries must be used, they should be wrapped in a mechanism that
Expand Down
3 changes: 1 addition & 2 deletions py4j-web/download.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ Requirements

Py4J requires:

* A Python interpreter. Py4J has been tested with CPython 2.7,
3.4, 3.5, 3.6, 3.7, 3.8, 3.9 and 3.10.
* A Python interpreter. Py4J has been tested with 3.8+.
* Java 7.0+.

Py4J for Eclipse requires:
Expand Down
4 changes: 2 additions & 2 deletions py4j-web/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
Installing Py4J
===============

Installing Python 2.7 or 3.4-3.10
Installing Python 3.8-3.12
---------------------------------

Py4J is a library written in Python and Java. Currently, Py4J has been tested
with Python 2.7, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9 and 3.10. You can install Python by going to the
with Python 3.8, 3.9, 3.10, 3.11 and 3.12. You can install Python by going to the
`official Python download page <http://www.python.org/download/>`_.


Expand Down
2 changes: 1 addition & 1 deletion py4j-web/requirements-doc.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Sphinx >=4.4,<5.0
Sphinx >5.0,<6.0
12 changes: 5 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,11 @@
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Java",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Object Brokering",
Expand Down