Skip to content

Commit ece762e

Browse files
committed
Merge pull request #245 in LCL/wolframclientforpython from bugfix/406949-better-traceback to master
* commit '3fad63e629e9e39a27df7adf7a8dfba65e649e10': PacletInfo.m edited online with Bitbucket improving error message code lint better low level handling of missing python installation using 12.2 for now bumping version removing stdout proxy implementation
2 parents 5ca29a8 + 3fad63e commit ece762e

11 files changed

Lines changed: 27 additions & 65 deletions

File tree

PacletInfo.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Paclet[
22
Name -> "WolframClientForPython",
33
Version -> "1.1.5",
4-
MathematicaVersion -> "11.3+",
4+
MathematicaVersion -> "12.3+",
55
Loading -> Automatic,
66
Extensions -> {}
77
]

wolframclient/cli/commands/benchmark.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def report(self):
8282
self.table_line()
8383

8484
# running export to do all lazy loadings
85-
binary_deserialize(export(1, target_format = 'wxf'))
85+
binary_deserialize(export(1, target_format="wxf"))
8686

8787
self.table_line("* Binary deserialize")
8888
self.table_line()

wolframclient/cli/commands/start_externalevaluate.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from __future__ import absolute_import, print_function, unicode_literals
22

33
import os
4+
import sys
45

56
from wolframclient.cli.utils import SimpleCommand
67
from wolframclient.utils.api import externalevaluate as ev
8+
from wolframclient.utils.api import zmq
79

810

911
class Command(SimpleCommand):
@@ -19,4 +21,11 @@ def handle(self, port=None, installpath=None, **opts):
1921
if installpath:
2022
os.environ["WOLFRAM_INSTALLATION_DIRECTORY"] = installpath
2123

24+
try:
25+
zmq.Context
26+
except ImportError as e:
27+
print("Error importing zmq: %s. Please install zmq. https://zeromq.org/languages/python/" % e, file=sys.stderr)
28+
sys.stderr.flush()
29+
sys.exit(1)
30+
2231
ev.start_zmq_loop(port=port)

wolframclient/evaluation/kernel/kernelcontroller.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from wolframclient.utils.api import json, os, time, zmq
2020

2121
if six.WINDOWS:
22-
from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW
22+
from subprocess import STARTF_USESHOWWINDOW, STARTUPINFO
2323

2424
__all__ = ["WolframKernelController"]
2525

wolframclient/tests/deserializers/wxf_deserialize.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from wolframclient.utils.datastructures import immutabledict
2222
from wolframclient.utils.tests import TestCase as BaseTestCase
2323

24+
2425
class TestCase(BaseTestCase):
2526
def test_token_dimensions(self):
2627
token = WXFToken(None)
@@ -200,7 +201,7 @@ def test_compressed_input(self):
200201
self.assertEqual(expr, res)
201202

202203
def test_nested_associations(self):
203-
expr = immutabledict((('a', 2), ('a', 3)))
204+
expr = immutabledict((("a", 2), ("a", 3)))
204205
expr = immutabledict(((expr, expr), (2, 3)))
205206
wxf = export(expr, target_format="wxf", compress=True)
206207
res = binary_deserialize(wxf, consumer=WXFConsumer())

wolframclient/tests/evaluation/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
# The evaluation modules is only supported on python 3.5+, because of asyncio
1010
# We need to prevent the test suite from loading this module containing coroutines.
1111
if six.PY_35:
12+
from wolframclient.tests.evaluation import test_async_cloud as m4
1213
from wolframclient.tests.evaluation import test_cloud as m1
13-
from wolframclient.tests.evaluation import test_kernel as m2
1414
from wolframclient.tests.evaluation import test_coroutine as m3
15-
from wolframclient.tests.evaluation import test_async_cloud as m4
15+
from wolframclient.tests.evaluation import test_kernel as m2
1616

1717
test_modules = (m1, m2, m3, m4)
1818
else:

wolframclient/tests/externalevaluate/ev_loop.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from threading import Thread
44

55
import zmq
6-
76
from wolframclient.language import wl
87
from wolframclient.serializers import export
98
from wolframclient.utils.externalevaluate import EXPORT_KWARGS, StdoutProxy, start_zmq_loop

wolframclient/tests/serializers/pandas.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ def sparse_series_dict(self):
3737
d["b"] = 1
3838
d["a"] = 0
3939
d["c"] = 2
40-
return pandas.Series(d, dtype='Sparse[int]')
40+
return pandas.Series(d, dtype="Sparse[int]")
4141

4242
def sparse_series_dict_indexed(self):
4343
constructor_dict = {1: 1.0}
4444
index = [0, 1, 2]
4545
# Series with index passed in
46-
series = pandas.Series(constructor_dict, dtype='Sparse[float]')
46+
series = pandas.Series(constructor_dict, dtype="Sparse[float]")
4747
return pandas.Series(series, index=index)
4848

4949
def multiindex_series(self):

wolframclient/tests/serializers/wxf_pythonarray.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def test_python_array(self):
3333

3434
def test_generators(self):
3535

36-
array = NumericArray((i for i in range(10)), "Integer8", shape=(10, ))
36+
array = NumericArray((i for i in range(10)), "Integer8", shape=(10,))
3737

3838
self.assertEqual(
3939
export(numpy.arange(10).astype(numpy.int8), target_format="wxf"),
@@ -49,4 +49,4 @@ def test_generators(self):
4949
export(array, target_format="wxf"),
5050
)
5151

52-
self.assertEqual(len(array), 10)
52+
self.assertEqual(len(array), 10)

wolframclient/utils/externalevaluate.py

Lines changed: 6 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -94,59 +94,18 @@ def execute_from_string(code, globals={}, **opts):
9494
return env[last_expr.name]
9595

9696

97-
class SideEffectSender(logging.Handler):
98-
def emit(self, record):
99-
if isinstance(sys.stdout, StdoutProxy):
100-
sys.stdout.send_side_effect(record.msg)
97+
class SocketWriter:
10198

99+
keep_listening = wl.ExternalEvaluate.Private.ExternalEvaluateKeepListening
102100

103-
class SocketWriter:
104101
def __init__(self, socket):
105102
self.socket = socket
106103

107104
def write(self, bytes):
108105
self.socket.send(zmq.Frame(bytes))
109106

110-
111-
class StdoutProxy:
112-
113-
keep_listening = wl.ExternalEvaluate.Private.ExternalEvaluateKeepListening
114-
115-
def __init__(self, stream):
116-
self.stream = stream
117-
self.clear()
118-
119-
def clear(self):
120-
self.current_line = []
121-
self.lines = []
122-
123-
def write(self, message):
124-
messages = force_text(message).split("\n")
125-
126-
if len(messages) == 1:
127-
self.current_line.extend(messages)
128-
else:
129-
self.current_line.append(messages.pop(0))
130-
rest = messages.pop(-1)
131-
132-
self.lines.extend(messages)
133-
self.flush()
134-
if rest:
135-
self.current_line.append(rest)
136-
137-
def flush(self):
138-
if self.current_line or self.lines:
139-
self.send_lines("".join(self.current_line), *self.lines)
140-
self.clear()
141-
142-
def send_lines(self, *lines):
143-
if len(lines) == 1:
144-
return self.send_side_effect(wl.Print(*lines))
145-
elif lines:
146-
return self.send_side_effect(wl.CompoundExpression(*map(wl.Print, lines)))
147-
148107
def send_side_effect(self, expr):
149-
self.stream.write(export(self.keep_listening(expr), **EXPORT_KWARGS))
108+
self.write(export(self.keep_listening(expr), **EXPORT_KWARGS))
150109

151110

152111
def evaluate_message(input=None, return_type=None, args=None, **opts):
@@ -201,7 +160,6 @@ def start_zmq_instance(port=None, write_to_stdout=True, **opts):
201160

202161
def start_zmq_loop(
203162
message_limit=float("inf"),
204-
redirect_stdout=True,
205163
export_kwargs=EXPORT_KWARGS,
206164
evaluate_message=evaluate_message,
207165
exception_class=None,
@@ -210,22 +168,17 @@ def start_zmq_loop(
210168
):
211169

212170
handler = to_wl(exception_class=exception_class, **export_kwargs)(handle_message)
213-
214171
socket = start_zmq_instance(**opts)
215-
216172
stream = SocketWriter(socket)
217-
218173
messages = 0
219174

220-
if redirect_stdout:
221-
sys.stdout = StdoutProxy(stream)
175+
class SideEffectSender(logging.Handler):
176+
def emit(self, record):
177+
stream.send_side_effect(record.msg)
222178

223179
side_effect_logger.addHandler(SideEffectSender())
224180

225181
# now sit in a while loop, evaluating input
226182
while messages < message_limit:
227183
stream.write(handler(socket, evaluate_message=evaluate_message, consumer=consumer))
228184
messages += 1
229-
230-
if redirect_stdout:
231-
sys.stdout = sys.__stdout__

0 commit comments

Comments
 (0)