Skip to content

Commit 800820a

Browse files
committed
release 0.4a2
1 parent b86c2e5 commit 800820a

24 files changed

+1109
-436
lines changed

README.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ py5 is a new version of Processing_ for Python 3.8+. It makes the Processing_ Ja
66
This entire repository is created by the meta-programming project py5generator_. Therefore, this code should not be edited manually. Any issues, etc, should be directed to the py5generator_ repository.
77

88
The `py5 documentation website
9-
<http://py5.ixora.io/>`_ provides basic tutorials and reference documentation.
9+
<http://py5.ixora.io/>`_ provides basic tutorials and reference documentation. See py5examples_ for example code.
1010

1111
.. _Processing: https://github.com/processing/processing4
1212
.. _JPype: https://github.com/jpype-project/jpype
1313
.. _py5generator: https://github.com/hx2A/py5generator
14+
.. _py5examples: https://github.com/hx2A/py5examples

py5/__init__.py

Lines changed: 95 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,11 @@
2323
"""
2424
import sys
2525
from pathlib import Path
26-
import logging
2726
import inspect
2827
from typing import overload, Any, Callable, Union, Dict, List, Tuple # noqa
2928
from nptyping import NDArray, Float, Int # noqa
3029

31-
import json # noqa
30+
# import json # noqa
3231
import numpy as np # noqa
3332
from PIL import Image # noqa
3433
from jpype import JClass # noqa
@@ -62,12 +61,10 @@
6261
pass
6362

6463

65-
__version__ = '0.4a1'
64+
__version__ = '0.4a2'
6665

6766
_PY5_USE_IMPORTED_MODE = py5_tools.imported.get_imported_mode()
6867

69-
logger = logging.getLogger(__name__)
70-
7168
java_conversion.init_jpype_converters()
7269

7370

@@ -18944,6 +18941,80 @@ def list_threads() -> None:
1894418941
"""
1894518942
return _py5sketch.list_threads()
1894618943

18944+
##############################################################################
18945+
# module functions from print_tools.py
18946+
##############################################################################
18947+
18948+
18949+
def set_println_stream(println_stream: Any) -> None:
18950+
"""Customize where the output of ``println()`` goes.
18951+
18952+
Parameters
18953+
----------
18954+
18955+
println_stream: Any
18956+
println stream object to be used by println method
18957+
18958+
Notes
18959+
-----
18960+
18961+
Customize where the output of ``println()`` goes.
18962+
18963+
When running a Sketch asynchronously through Jupyter Notebook, any ``print``
18964+
statements using Python's builtin function will always appear in the output of
18965+
the currently active cell. This will rarely be desirable, as the active cell
18966+
will keep changing as the user executes code elsewhere in the notebook. The
18967+
``println()`` method was created to provide users with print functionality in a
18968+
Sketch without having to cope with output moving from one cell to the next. Use
18969+
``set_println_stream`` to change how the output is handled. The
18970+
``println_stream`` object must provide ``init()`` and ``print()`` methods, as
18971+
shown in the example. The example demonstrates how to configure py5 to output
18972+
text to an IPython Widget.
18973+
"""
18974+
return _py5sketch.set_println_stream(println_stream)
18975+
18976+
18977+
def println(
18978+
*args,
18979+
sep: str = ' ',
18980+
end: str = '\n',
18981+
stderr: bool = False) -> None:
18982+
"""Print text or other values to the screen.
18983+
18984+
Parameters
18985+
----------
18986+
18987+
args
18988+
values to be printed
18989+
18990+
end: str = '\\n'
18991+
string appended after the last value, defaults to newline character
18992+
18993+
sep: str = ' '
18994+
string inserted between values, defaults to a space
18995+
18996+
stderr: bool = False
18997+
use stderr instead of stdout
18998+
18999+
Notes
19000+
-----
19001+
19002+
Print text or other values to the screen. For a Sketch running outside of a
19003+
Jupyter Notebook, this method will behave the same as the Python's builtin
19004+
``print`` method. For Sketches running in a Jupyter Notebook, this will place
19005+
text in the output of the cell that made the ``run_sketch()`` call.
19006+
19007+
When running a Sketch asynchronously through Jupyter Notebook, any ``print``
19008+
statements using Python's builtin function will always appear in the output of
19009+
the currently active cell. This will rarely be desirable, as the active cell
19010+
will keep changing as the user executes code elsewhere in the notebook. This
19011+
method was created to provide users with print functionality in a Sketch without
19012+
having to cope with output moving from one cell to the next.
19013+
19014+
Use ``set_println_stream()`` to customize the behavior of ``println()``.
19015+
"""
19016+
return _py5sketch.println(*args, sep=sep, end=end, stderr=stderr)
19017+
1894719018
##############################################################################
1894819019
# module functions from sketch.py
1894919020
##############################################################################
@@ -19376,10 +19447,10 @@ def run_sketch(block: bool = None, *,
1937619447
functions will be used to actualize your Sketch.
1937719448

1937819449
Use the ``block`` parameter to specify if the call to ``run_sketch()`` should
19379-
return immediately or block until the Sketch exits. If the ``block`` parameter
19380-
is not specified, py5 will first attempt to determine if the Sketch is running
19381-
in a Jupyter Notebook or an IPython shell. If it is, ``block`` will default to
19382-
``False``, and ``True`` otherwise.
19450+
return immediately (asynchronous Sketch execution) or block until the Sketch
19451+
exits. If the ``block`` parameter is not specified, py5 will first attempt to
19452+
determine if the Sketch is running in a Jupyter Notebook or an IPython shell. If
19453+
it is, ``block`` will default to ``False``, and ``True`` otherwise.
1938319454

1938419455
A list of strings passed to ``py5_options`` will be passed to the Processing
1938519456
PApplet class as arguments to specify characteristics such as the window's
@@ -19394,13 +19465,26 @@ def run_sketch(block: bool = None, *,
1939419465
``sketch_functions`` parameter to pass a dictionary of the desired callable
1939519466
functions. The ``sketch_functions`` parameter is not available when coding py5
1939619467
in class mode. Don't forget you can always replace the ``draw()`` function in a
19397-
running Sketch using ``hot_reload_draw()``."""
19468+
running Sketch using ``hot_reload_draw()``.
19469+
19470+
When running a Sketch asynchronously through Jupyter Notebook, any ``print``
19471+
statements using Python's builtin function will always appear in the output of
19472+
the currently active cell. This will rarely be desirable, as the active cell
19473+
will keep changing as the user executes code elsewhere in the notebook. As an
19474+
alternative, use py5's ``println()`` method, which will place all text in the
19475+
output of the cell that made the ``run_sketch()`` call. This will continue to be
19476+
true if the user moves on to execute code in other Notebook cells. Use
19477+
``set_println_stream()`` to customize this behavior. All py5 error messages and
19478+
stack traces are routed through the ``println()`` method. Be aware that some
19479+
error messages and warnings generated inside the Processing Jars cannot be
19480+
controlled in the same way, and may appear in the output of the active cell or
19481+
mixed in with the Jupyter Kernel logs."""
1939819482
if block is None:
1939919483
block = not _in_ipython_session
1940019484

1940119485
sketch_functions = sketch_functions or inspect.stack()[1].frame.f_locals
1940219486
functions = dict([(e, sketch_functions[e])
19403-
for e in reference.METHODS if e in sketch_functions and callable(sketch_functions[e])])
19487+
for e in reference.METHODS if e in sketch_functions and callable(sketch_functions[e])])
1940419488

1940519489
if not set(functions.keys()) & set(['settings', 'setup', 'draw']):
1940619490
print(("Unable to find settings, setup, or draw functions. "

py5/custom_exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def handle_typeerror(exc_type_name, exc_msg, py5info):
4141
else:
4242
exc_msg += 'Your parameters must match one of the following signatures:\n'
4343
exc_msg += '\n'.join([' * ' + fname +
44-
sig for sig in signatures])
44+
sig for sig in signatures])
4545

4646
return exc_msg
4747

py5/jars/core.jar

-20 Bytes
Binary file not shown.

py5/jars/py5.jar

1.85 KB
Binary file not shown.

py5/methods.py

Lines changed: 22 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,8 @@
1818
#
1919
# *****************************************************************************
2020
import sys
21-
import logging
2221
from pathlib import Path
2322
from collections import defaultdict
24-
from io import StringIO
25-
from contextlib import redirect_stderr, redirect_stdout
2623
from typing import Union
2724
import line_profiler
2825

@@ -35,9 +32,6 @@
3532

3633
_JavaNullPointerException = JClass('java.lang.NullPointerException')
3734

38-
logger = logging.getLogger(__name__)
39-
40-
4135
# *** stacktrace configuration ***
4236
# set stackprinter color style. Default is plaintext. Other choices are darkbg,
4337
# darkbg2, darkbg3, lightbg, lightbg2, lightbg3.
@@ -52,28 +46,30 @@
5246
}
5347

5448

55-
def _exception_msg(exc_type_name, exc_msg, py5info):
49+
def _exception_msg(println, exc_type_name, exc_msg, py5info):
5650
try:
5751
msg = _EXCEPTION_MSGS.get(exc_type_name, exc_msg)
5852
if isinstance(msg, str):
5953
return msg
6054
elif callable(msg):
6155
return msg(exc_type_name, exc_msg, py5info)
6256
else:
63-
logger.error(
64-
f'unknown exception msg type for {exc_type_name}: {type(msg).__name__}')
57+
println(
58+
f'unknown exception msg type for {exc_type_name}: {type(msg).__name__}',
59+
stderr=True)
6560
return exc_msg
6661
except Exception as e:
67-
logger.error(
68-
f'error generating exception msg for {exc_type_name}: {e}')
62+
println(
63+
f'error generating exception msg for {exc_type_name}: {e}',
64+
stderr=True)
6965
return exc_msg
7066

7167

7268
def register_exception_msg(exc_type_name: str, msg: Union[str, callable]):
7369
_EXCEPTION_MSGS[exc_type_name] = msg
7470

7571

76-
def handle_exception(exc_type, exc_value, exc_tb):
72+
def handle_exception(println, exc_type, exc_value, exc_tb):
7773
py5info = []
7874
try:
7975
if _prune_tracebacks and hasattr(exc_tb, 'tb_next'):
@@ -92,8 +88,9 @@ def handle_exception(exc_type, exc_value, exc_tb):
9288
if trim_tb:
9389
trim_tb.tb_next = None
9490
except Exception as e:
95-
logger.critical(
96-
f'Exception thrown while examining error traceback: {str(e)}')
91+
println(
92+
f'Exception thrown while examining error traceback: {str(e)}',
93+
stderr=True)
9794

9895
errmsg = stackprinter.format(
9996
thing=(exc_type, exc_value, exc_tb.tb_next),
@@ -106,22 +103,21 @@ def handle_exception(exc_type, exc_value, exc_tb):
106103
errmsg = errmsg.replace(
107104
str(exc_value),
108105
_exception_msg(
106+
println,
109107
exc_type.__name__,
110108
str(exc_value),
111109
py5info))
112110

113-
logger.critical(errmsg)
111+
println(errmsg, stderr=True)
114112

115113
sys.last_type, sys.last_value, sys.last_traceback = exc_type, exc_value, exc_tb
116114

117115

118116
@JImplements('py5.core.Py5Methods')
119117
class Py5Methods:
120118

121-
def __init__(self, sketch, _stream_redirect=None):
119+
def __init__(self, sketch):
122120
self._sketch = sketch
123-
self._stream_redirect = _stream_redirect
124-
125121
self._functions = dict()
126122
self._pre_hooks = defaultdict(dict)
127123
self._post_hooks = defaultdict(dict)
@@ -186,28 +182,6 @@ def get_function_list(self):
186182

187183
@JOverride
188184
def run_method(self, method_name, params):
189-
if self._stream_redirect:
190-
return self._stream_redirect_run_method(method_name, params)
191-
else:
192-
return self._run_method(method_name, params)
193-
194-
def _stream_redirect_run_method(self, method_name, params):
195-
stdout, stderr = StringIO(), StringIO()
196-
197-
with redirect_stdout(stdout), redirect_stderr(stderr):
198-
retval = self._run_method(method_name, params)
199-
200-
stdout_val = stdout.getvalue()
201-
if stdout_val:
202-
self._stream_redirect('stdout', stdout_val)
203-
204-
stderr_val = stderr.getvalue()
205-
if stderr_val:
206-
self._stream_redirect('stderr', stderr_val)
207-
208-
return retval
209-
210-
def _run_method(self, method_name, params):
211185
try:
212186
if method_name in self._functions:
213187
# first run the pre-hooks, if any
@@ -224,17 +198,21 @@ def _run_method(self, method_name, params):
224198
hook(self._sketch)
225199
return True
226200
except Exception:
227-
handle_exception(*sys.exc_info())
201+
handle_exception(self._sketch.println, *sys.exc_info())
228202
self._sketch._terminate_sketch()
229203
return False
230204

205+
@JOverride
206+
def py5_println(self, text, stderr):
207+
self._sketch.println(text, stderr=stderr)
208+
231209
@JOverride
232210
def shutdown(self):
233-
logger.info('shutdown initiated')
234211
try:
235212
self._sketch._shutdown()
236213
self._is_terminated = True
237214
self.terminate_hooks()
238-
logger.info('shutdown complete')
239215
except Exception:
240-
logger.exception('exception in sketch shutdown sequence')
216+
self._sketch.println(
217+
'exception in sketch shutdown sequence',
218+
stderr=True)

py5/mixins/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@
2121
from .data import DataMixin # noqa
2222
from .threads import ThreadsMixin # noqa
2323
from .pixels import PixelMixin # noqa
24+
from .print_tools import PrintlnStream, _WidgetPrintlnStream, _DefaultPrintlnStream, _DisplayPubPrintlnStream # noqa

0 commit comments

Comments
 (0)