Skip to content

Commit

Permalink
Updates to scripts; fix for matplotlib macosx backend
Browse files Browse the repository at this point in the history
  • Loading branch information
culler committed May 26, 2023
1 parent aa4a80a commit 9ddde07
Show file tree
Hide file tree
Showing 10 changed files with 738 additions and 15 deletions.
10 changes: 8 additions & 2 deletions Sage_framework/build_sage_framework.sh
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,22 @@ ln -s ../../../../../../../share/threejs-sage ${THREEJS_SAGE}
mkdir -p ${NBEXTENSIONS}/widgets/notebook
ln -s ../../jupyter-js-widgets ${NBEXTENSIONS}/widgets/notebook/js

# Remove useless stuff
rm -rf ${VERSION_DIR}/local/lib/saclib
rm -rf ${VERSION_DIR}/local/share/man

# Fix up rpaths and shebangs
echo "Patching files ..."
source ../IDs.sh
mv files_to_sign files_to_sign.bak
python3 fix_paths.py repo ${VERSION_DIR}/local/bin > files_to_sign
##mv ${VERSION_DIR}/${VENV_DIR}/lib/python${PYTHON_VERSION} ${RESOURCE_DIR}
##python3 fix_paths.py repo ${RESOURCE_DIR}/python${PYTHON_VERSION} > files_to_sign
python3 fix_paths.py repo ${VERSION_DIR}/local/bin >> files_to_sign
python3 fix_paths.py repo ${VERSION_DIR}/local/lib >> files_to_sign
python3 fix_paths.py repo ${VERSION_DIR}/local/libexec >> files_to_sign
python3 fix_paths.py repo ${VERSION_DIR}/${VENV_DIR}/bin >> files_to_sign
python3 fix_paths.py repo ${VERSION_DIR}/${VENV_DIR}/lib >> files_to_sign

##ln -s ../../../../../../Resources/python${PYTHON_VERSION} ${VERSION_DIR}/${VENV_DIR}/lib
# Fix the absolute symlinks for the GAP packages
pushd ${VERSION_DIR}/local/share/gap/pkg > /dev/null
for pkg in `ls` ; do
Expand Down
1 change: 1 addition & 0 deletions bin/create_dmg
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ create-dmg \
--app-drop-link 380 220 \
--icon "Recommended_10_0.pkg" 160 380 \
--format ULFO \
--no-internet-enable \
$DMG_NAME $SOURCE
11 changes: 0 additions & 11 deletions bin/makedmg

This file was deleted.

2 changes: 1 addition & 1 deletion bin/notarize_app
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set -e
source IDs.sh
SAGE_VERSION=`bin/get_sage_version`
SAGE_DASH_VERSION=$(echo $SAGE_VERSION | sed s/\\\./\\\-/g)
DIST=SageMath-$SAGE_VERSION.dmg
DIST=SageMath-$SAGE_VERSION
APP=$DIST/SageMath-$SAGE_DASH_VERSION.app
DMG=SageMath-$SAGE_VERSION.dmg
OPTIONS="--wait --no-progress --apple-id $APPLE_ID \
Expand Down
14 changes: 14 additions & 0 deletions bin/quick_dmg
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
SAGE_VERSION=`bin/get_sage_version`
SOURCE=SageMath-$SAGE_VERSION
DMG_NAME=SageMath-$SAGE_VERSION.dmg
VOLUME_NAME=$SOURCE
echo Creating ...
hdiutil create -volname $VOLUME_NAME -srcfolder $SOURCE temp_$DMG_NAME
if [ -e $DMG_NAME ]; then
echo Removing old $DMG_NAME
rm -f $DMG_NAME
fi
echo Compressing ...
hdiutil convert temp_$DMG_NAME -format ULFO -o $DMG_NAME
rm temp_$DMG_NAME
4 changes: 3 additions & 1 deletion build_dist.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ APP=$DIST/SageMath-$SAGE_DASH_VERSION.app
PKG=Recommended_$SAGE_SCORE_VERSION.pkg
PYTHON3=../Frameworks/Sage.framework/Versions/Current/venv/bin/python3
mkdir $DIST
# Render templates and nstall the package
# Render templates and install the package
cd package
. build_package.sh
cd ..
Expand All @@ -30,10 +30,12 @@ cp jinja/output/Info.plist $APP/Contents
cp icon/{Sage.icns,sage_icon_1024.png} $APP/Contents/Resources
cp logos/{sage_logo_512.png,sage_logo_256.png} $APP/Contents/Resources
cp main.py $APP/Contents/Resources
# Build Tcl and Tk frameworks
cd TclTk_frameworks
make
cd ..
mv TclTk_frameworks/Frameworks/{Tcl,Tk}.framework $APP/Contents/Frameworks
# Build Sage framework
cd Sage_framework
bash build_sage_framework.sh
cd ..
Expand Down
194 changes: 194 additions & 0 deletions matplotlib_fix/inputhook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
"""
Similar to `PyOS_InputHook` of the Python API, we can plug in an input hook in
the asyncio event loop.
The way this works is by using a custom 'selector' that runs the other event
loop until the real selector is ready.
It's the responsibility of this event hook to return when there is input ready.
There are two ways to detect when input is ready:
The inputhook itself is a callable that receives an `InputHookContext`. This
callable should run the other event loop, and return when the main loop has
stuff to do. There are two ways to detect when to return:
- Call the `input_is_ready` method periodically. Quit when this returns `True`.
- Add the `fileno` as a watch to the external eventloop. Quit when file descriptor
becomes readable. (But don't read from it.)
Note that this is not the same as checking for `sys.stdin.fileno()`. The
eventloop of prompt-toolkit allows thread-based executors, for example for
asynchronous autocompletion. When the completion for instance is ready, we
also want prompt-toolkit to gain control again in order to display that.
"""
import asyncio
import os
import select
import selectors
import threading
from asyncio import AbstractEventLoop
from selectors import BaseSelector, SelectorKey
from typing import (
TYPE_CHECKING,
Any,
Callable,
List,
Mapping,
NamedTuple,
Optional,
Tuple,
)

from prompt_toolkit.utils import is_windows

from .utils import get_event_loop

__all__ = [
"new_eventloop_with_inputhook",
"set_eventloop_with_inputhook",
"InputHookSelector",
"InputHookContext",
]

if TYPE_CHECKING:
from _typeshed import FileDescriptor, FileDescriptorLike

_EventMask = int


def new_eventloop_with_inputhook(
inputhook: Callable[["InputHookContext"], None]
) -> AbstractEventLoop:
"""
Create a new event loop with the given inputhook.
"""
selector = InputHookSelector(selectors.DefaultSelector(), inputhook)
loop = asyncio.SelectorEventLoop(selector)
return loop

def set_eventloop_with_inputhook(
inputhook: Callable[["InputHookContext"], None]
) -> AbstractEventLoop:
"""
Create a new event loop with the given inputhook, and activate it.
"""
loop = new_eventloop_with_inputhook(inputhook)
asyncio.set_event_loop(loop)
return loop


class InputHookSelector(BaseSelector):
"""
Usage:
selector = selectors.SelectSelector()
loop = asyncio.SelectorEventLoop(InputHookSelector(selector, inputhook))
asyncio.set_event_loop(loop)
"""

def __init__(
self, selector: BaseSelector, inputhook: Callable[["InputHookContext"], None]
) -> None:
self.selector = selector
self.inputhook = inputhook
self._r, self._w = os.pipe()

def register(
self, fileobj: "FileDescriptorLike", events: "_EventMask", data: Any = None
) -> "SelectorKey":
return self.selector.register(fileobj, events, data=data)

def unregister(self, fileobj: "FileDescriptorLike") -> "SelectorKey":
return self.selector.unregister(fileobj)

def modify(
self, fileobj: "FileDescriptorLike", events: "_EventMask", data: Any = None
) -> "SelectorKey":
return self.selector.modify(fileobj, events, data=None)

def select(
self, timeout: Optional[float] = None
) -> List[Tuple["SelectorKey", "_EventMask"]]:
# If there are tasks in the current event loop,
# don't run the input hook.
if len(getattr(get_event_loop(), "_ready", [])) > 0:
return self.selector.select(timeout=timeout)

ready = False
result = None

# Run selector in other thread.
def run_selector() -> None:
nonlocal ready, result
result = self.selector.select(timeout=timeout)
os.write(self._w, b"x")
ready = True

th = threading.Thread(target=run_selector)
th.start()

def input_is_ready() -> bool:
return ready

# Call inputhook.
# The inputhook function is supposed to return when our selector
# becomes ready. The inputhook can do that by registering the fd in its
# own loop, or by checking the `input_is_ready` function regularly.

# selector = InputHookSelector(selectors.DefaultSelector(), self.inputhook)
self.inputhook(InputHookContext(self._r, input_is_ready))

# Flush the read end of the pipe.
try:
# Before calling 'os.read', call select.select. This is required
# when the gevent monkey patch has been applied. 'os.read' is never
# monkey patched and won't be cooperative, so that would block all
# other select() calls otherwise.
# See: http://www.gevent.org/gevent.os.html

# Note: On Windows, this is apparently not an issue.
# However, if we would ever want to add a select call, it
# should use `windll.kernel32.WaitForMultipleObjects`,
# because `select.select` can't wait for a pipe on Windows.
if not is_windows():
select.select([self._r], [], [], None)

os.read(self._r, 1024)
except OSError:
# This happens when the window resizes and a SIGWINCH was received.
# We get 'Error: [Errno 4] Interrupted system call'
# Just ignore.
pass

# Wait for the real selector to be done.
th.join()
assert result is not None
return result

def close(self) -> None:
"""
Clean up resources.
"""
if self._r:
os.close(self._r)
os.close(self._w)

self._r = self._w = -1
self.selector.close()

def get_map(self) -> Mapping["FileDescriptorLike", "SelectorKey"]:
return self.selector.get_map()


class InputHookContext:
"""
Given as a parameter to the inputhook.
"""

def __init__(self, fileno: int, input_is_ready: Callable[[], bool]) -> None:
self._fileno = fileno
self.input_is_ready = input_is_ready

def fileno(self) -> int:
return self._fileno
Loading

0 comments on commit 9ddde07

Please sign in to comment.