Skip to content

Commit

Permalink
[git-webkit] Ring terminal bell if input hangs
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=244625
<rdar://problem/99397362>

Reviewed by Darin Adler.

* Tools/Scripts/libraries/webkitcorepy/setup.py: Bump version.
* Tools/Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py: Add Timer object, bump version.
* Tools/Scripts/libraries/webkitcorepy/webkitcorepy/terminal.py:
(Terminal):
(Terminal.input): Support ring timer through alert_after.
(Terminal.ring): Add.
(Terminal.choose): Add alert_after.
(Terminal.open_url): Ditto.
* Tools/Scripts/libraries/webkitcorepy/webkitcorepy/timer.py:
(Timer): Added.
* Tools/Scripts/libraries/webkitscmpy/setup.py: Bump version.
* Tools/Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py: Ditto.
* Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/branch.py:
(Branch.main): Add alert_after.

Canonical link: https://commits.webkit.org/255634@main
  • Loading branch information
JonWBedard committed Oct 17, 2022
1 parent 32eb842 commit 5df181e
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 12 deletions.
2 changes: 1 addition & 1 deletion Tools/Scripts/libraries/webkitcorepy/setup.py
Expand Up @@ -30,7 +30,7 @@ def readme():

setup(
name='webkitcorepy',
version='0.13.18',
version='0.13.19',
description='Library containing various Python support classes and functions.',
long_description=readme(),
classifiers=[
Expand Down
Expand Up @@ -35,6 +35,7 @@
from webkitcorepy.subprocess_utils import TimeoutExpired, CompletedProcess, run, Thread
from webkitcorepy.output_capture import LoggerCapture, OutputCapture, OutputDuplicate
from webkitcorepy.task_pool import TaskPool
from webkitcorepy.timer import Timer
from webkitcorepy.terminal import Terminal
from webkitcorepy.environment import Environment
from webkitcorepy.credentials import credentials, delete_credentials
Expand All @@ -45,7 +46,7 @@
from webkitcorepy.file_lock import FileLock
from webkitcorepy.null_context import NullContext

version = Version(0, 13, 18)
version = Version(0, 13, 19)

from webkitcorepy.autoinstall import Package, AutoInstall
if sys.version_info > (3, 0):
Expand Down
27 changes: 20 additions & 7 deletions Tools/Scripts/libraries/webkitcorepy/webkitcorepy/terminal.py
Expand Up @@ -27,7 +27,7 @@
if not sys.platform.startswith('win'):
import readline

from webkitcorepy import StringIO, run
from webkitcorepy import StringIO, run, Timer

if sys.version_info > (3, 0):
file = io.IOBase
Expand All @@ -37,17 +37,30 @@ class Terminal(object):
_atty_overrides = {}
colors = True
URL_PREFIXES = ('file://', 'http://', 'https://', 'radar://', 'rdar://')
RING_INTERVAL = 30

@classmethod
def input(cls, *args, **kwargs):
alert_after = kwargs.pop('alert_after', None)

try:
return (input if sys.version_info > (3, 0) else raw_input)(*args, **kwargs)
if alert_after and cls.isatty(sys.stdout):
with Timer(alert_after, lambda: cls.ring(sys.stdout)):
return (input if sys.version_info > (3, 0) else raw_input)(*args, **kwargs)
else:
return (input if sys.version_info > (3, 0) else raw_input)(*args, **kwargs)
except KeyboardInterrupt:
sys.stderr.write('\nUser interrupted program\n')
sys.exit(1)

@classmethod
def choose(cls, prompt, options=None, default=None, strict=False, numbered=False):
def ring(cls, file=sys.stdout):
if file:
file.write('\a')
file.flush()

@classmethod
def choose(cls, prompt, options=None, default=None, strict=False, numbered=False, alert_after=RING_INTERVAL):
options = options or ('Yes', 'No')

response = None
Expand All @@ -63,7 +76,7 @@ def choose(cls, prompt, options=None, default=None, strict=False, numbered=False
'[{}]'.format(option) if option == default else option for option in options
])))
sys.stdout.flush()
response = cls.input(': ')
response = cls.input(': ', alert_after=alert_after)

if numbered and response.isdigit():
index = int(response) - 1
Expand All @@ -88,7 +101,7 @@ def assert_writeable_stream(cls, target):
if not isinstance(target, (io.IOBase, file, StringIO)):
raise ValueError('{} is not an IO object'.format(target))
if not isinstance(target, StringIO) and not (getattr(target, 'writable', None) and target.writable()) and 'w' not in getattr(target, 'mode', 'r'):
raise ValueError('{} is an IO object, but is not writable {}'.format(target, ))
raise ValueError('{} is an IO object, but is not writable'.format(target))

@classmethod
def supports_color(cls, file):
Expand Down Expand Up @@ -126,7 +139,7 @@ def override_atty(cls, target, isatty=True):
cls._atty_overrides[key] = previous

@classmethod
def open_url(cls, url, prompt=None):
def open_url(cls, url, prompt=None, alert_after=RING_INTERVAL):
if all(not url.startswith(prefix) for prefix in cls.URL_PREFIXES):
sys.stderr.write("'{}' is not a valid URL\n")
return False
Expand All @@ -135,7 +148,7 @@ def open_url(cls, url, prompt=None):

if prompt:
try:
cls.input(prompt)
cls.input(prompt, alert_after=alert_after)
except SystemExit:
sys.stderr.write('User aborted URL open\n')
return False
Expand Down
36 changes: 36 additions & 0 deletions Tools/Scripts/libraries/webkitcorepy/webkitcorepy/timer.py
@@ -0,0 +1,36 @@
# Copyright (C) 2022 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from threading import Timer as _Timer


class Timer(_Timer):
def __init__(self, interval, callback):
super(Timer, self).__init__(interval, callback)
self.daemon = True

def __enter__(self):
self.start()
return self

def __exit__(self, *args, **kwargs):
self.cancel()
2 changes: 1 addition & 1 deletion Tools/Scripts/libraries/webkitscmpy/setup.py
Expand Up @@ -29,7 +29,7 @@ def readme():

setup(
name='webkitscmpy',
version='5.6.21',
version='5.6.22',
description='Library designed to interact with git and svn repositories.',
long_description=readme(),
classifiers=[
Expand Down
Expand Up @@ -46,7 +46,7 @@ def _maybe_add_webkitcorepy_path():
"Please install webkitcorepy with `pip install webkitcorepy --extra-index-url <package index URL>`"
)

version = Version(5, 6, 21)
version = Version(5, 6, 22)

AutoInstall.register(Package('fasteners', Version(0, 15, 0)))
AutoInstall.register(Package('jinja2', Version(2, 11, 3)))
Expand Down
Expand Up @@ -114,7 +114,7 @@ def main(cls, args, repository, why=None, redact=False, target_remote='fork', **
prompt = '{}nter issue URL or title of new issue: '.format('{}, e'.format(why) if why else 'E')
else:
prompt = '{}nter name of new branch (or issue URL): '.format('{}, e'.format(why) if why else 'E')
args.issue = Terminal.input(prompt)
args.issue = Terminal.input(prompt, alert_after=2 * Terminal.RING_INTERVAL)

if string_utils.decode(args.issue).isnumeric() and Tracker.instance() and not redact:
issue = Tracker.instance().issue(int(args.issue))
Expand Down

0 comments on commit 5df181e

Please sign in to comment.