Skip to content
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

Add isTTY function #1622

Merged
merged 29 commits into from Feb 3, 2019
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4d12933
Add isTTY function
dsseng Jan 29, 2019
b5a3e54
Fix lint errors
dsseng Jan 29, 2019
45f83ae
Add rid argument to IsTTY
dsseng Jan 30, 2019
75db01f
Remove unused code
dsseng Jan 30, 2019
b3a1bb0
Add an example for IsTTY
dsseng Jan 30, 2019
c15c4b6
Add a test for IsTTY
dsseng Jan 30, 2019
b81ffb3
Remove unnecessary if..else
dsseng Jan 30, 2019
d7c4b70
remove unused var from test
dsseng Jan 31, 2019
3085dc1
Format code
dsseng Jan 31, 2019
f26ca3a
Change IsTTY output format
dsseng Feb 1, 2019
533907d
Get TTY info in 1 op
dsseng Feb 1, 2019
2c56aba
Format code
dsseng Feb 1, 2019
16497d1
Add IsTTY result caching
dsseng Feb 1, 2019
9c7e56f
Remove IsTTY caching
dsseng Feb 1, 2019
9b1d935
Update IsTTY example
dsseng Feb 1, 2019
e32d2db
Use inline interface for IsTTY
dsseng Feb 1, 2019
fb9ab75
Add a simple test to os_test
dsseng Feb 1, 2019
6f20977
Fix inline interface & move unit test link
dsseng Feb 1, 2019
f959403
Fix IsTTY example
dsseng Feb 2, 2019
91250f9
Use inline interface instead of classic one
dsseng Feb 2, 2019
d93d1e7
Merge branch 'is-tty' of https://github.com/sh7dm/deno into is-tty
dsseng Feb 2, 2019
9c8af0c
Fix some issues in IsTTY test
dsseng Feb 2, 2019
2ce79c1
Move IsTTY test to another file
dsseng Feb 2, 2019
273628e
Remove tty_capture, just import it
dsseng Feb 2, 2019
67543de
Clean up IsTTY test
dsseng Feb 2, 2019
1ef9968
Clean up IsTTY test
dsseng Feb 2, 2019
7ebe4bc
Skip IsTTY test on Windows because it was failing
dsseng Feb 2, 2019
4dc9e25
Merge branch 'master' into is-tty
Feb 2, 2019
536b5ac
Clean IsTTY test
dsseng Feb 2, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion js/deno.ts
@@ -1,7 +1,7 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.

// Public deno module.
export { pid, env, exit } from "./os";
export { pid, env, exit, isTTY } from "./os";
export { chdir, cwd } from "./dir";
export {
File,
Expand Down
23 changes: 23 additions & 0 deletions js/os.ts
Expand Up @@ -23,6 +23,29 @@ interface CodeInfo {
sourceMap: string | undefined;
}

interface IsTTY { stdin: boolean, stdout: boolean, stderr: boolean }

/** Check if running in terminal.
*
* import { isTTY } from "deno";
* console.log(isTTY.stdout);
dsseng marked this conversation as resolved.
Show resolved Hide resolved
*/
export function isTTY(): IsTTY {
dsseng marked this conversation as resolved.
Show resolved Hide resolved
const builder = flatbuffers.createBuilder();
msg.IsTTY.startIsTTY(builder);
const inner = msg.IsTTY.endIsTTY(builder);
const baseRes = sendSync(builder, msg.Any.IsTTY, inner)!;
assert(msg.Any.IsTTYRes === baseRes.innerType());
const res = new msg.IsTTYRes();
assert(baseRes.inner(res) != null);

return {
stdin: res.stdin(),
stdout: res.stdout(),
stderr: res.stderr()
};
}

dsseng marked this conversation as resolved.
Show resolved Hide resolved
/** Exit the Deno process with optional exit code. */
export function exit(exitCode = 0): never {
const builder = flatbuffers.createBuilder();
Expand Down
5 changes: 5 additions & 0 deletions js/os_test.ts
Expand Up @@ -27,3 +27,8 @@ test(function osPid() {
console.log("pid", deno.pid);
assert(deno.pid > 0);
});

// See complete tests in tools/tty_test.py
dsseng marked this conversation as resolved.
Show resolved Hide resolved
test(function osIsTTYSmoke() {
console.log(deno.isTTY());
});
12 changes: 11 additions & 1 deletion src/msg.fbs
Expand Up @@ -59,7 +59,9 @@ union Any {
Run,
RunRes,
RunStatus,
RunStatusRes
RunStatusRes,
IsTTY,
IsTTYRes
}

enum ErrorKind: byte {
Expand Down Expand Up @@ -475,4 +477,12 @@ table RunStatusRes {
exit_signal: int;
}

table IsTTY {}

table IsTTYRes {
stdin: bool;
stdout: bool;
stderr: bool;
}

root_type Base;
28 changes: 28 additions & 0 deletions src/ops.rs
@@ -1,4 +1,5 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.

use crate::errors;
use crate::errors::{DenoError, DenoResult, ErrorKind};
use crate::fs as deno_fs;
Expand All @@ -18,6 +19,7 @@ use crate::resources::Resource;
use crate::tokio_util;
use crate::version;

use atty;
use flatbuffers::FlatBufferBuilder;
use futures;
use futures::Async;
Expand Down Expand Up @@ -121,6 +123,7 @@ pub fn dispatch(
msg::Any::WorkerPostMessage => op_worker_post_message,
msg::Any::Write => op_write,
msg::Any::WriteFile => op_write_file,
msg::Any::IsTTY => op_is_tty,
_ => panic!(format!(
"Unhandled message {}",
msg::enum_name_any(inner_type)
Expand Down Expand Up @@ -173,6 +176,31 @@ pub fn dispatch(
(base.sync(), boxed_op)
}

fn op_is_tty(
_state: &Arc<IsolateState>,
base: &msg::Base<'_>,
_data: libdeno::deno_buf,
) -> Box<Op> {
let builder = &mut FlatBufferBuilder::new();
let inner = msg::IsTTYRes::create(
builder,
&msg::IsTTYResArgs {
stdin: atty::is(atty::Stream::Stdin),
stdout: atty::is(atty::Stream::Stdout),
stderr: atty::is(atty::Stream::Stderr),
},
);
ok_future(serialize_response(
base.cmd_id(),
builder,
msg::BaseArgs {
inner: Some(inner.as_union_value()),
inner_type: msg::Any::IsTTYRes,
..Default::default()
},
))
}
ry marked this conversation as resolved.
Show resolved Hide resolved

fn op_exit(
_config: &Arc<IsolateState>,
base: &msg::Base<'_>,
Expand Down
86 changes: 86 additions & 0 deletions tools/is_tty_test.py
@@ -0,0 +1,86 @@
#!/usr/bin/env python
# Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import os
import pty
import select
import subprocess

from util import build_path, executable_suffix

from sys import stdin

IS_TTY_TEST_TS = "tools/is_tty_test.ts"


# This function is copied from:
# https://gist.github.com/hayd/4f46a68fc697ba8888a7b517a414583e
# https://stackoverflow.com/q/52954248/1240268
dsseng marked this conversation as resolved.
Show resolved Hide resolved
def tty_capture(cmd, bytes_input):
dsseng marked this conversation as resolved.
Show resolved Hide resolved
"""Capture the output of cmd with bytes_input to stdin,
with stdin, stdout and stderr as TTYs."""
mo, so = pty.openpty() # provide tty to enable line-buffering
me, se = pty.openpty()
mi, si = pty.openpty()
fdmap = {mo: 'stdout', me: 'stderr', mi: 'stdin'}

p = subprocess.Popen(
cmd, bufsize=1, stdin=si, stdout=so, stderr=se, close_fds=True)
os.write(mi, bytes_input)

timeout = .04 # seconds
res = {'stdout': b'', 'stderr': b''}
while True:
ready, _, _ = select.select([mo, me], [], [], timeout)
if ready:
for fd in ready:
data = os.read(fd, 512)
if not data:
break
res[fdmap[fd]] += data
elif p.poll() is not None: # select timed-out
break # p exited
for fd in [si, so, se, mi, mo, me]:
os.close(fd) # can't do it sooner: it leads to errno.EIO error
p.wait()
return p.returncode, res['stdout'], res['stderr']


class IsTTY(object):
def __init__(self, deno_exe):
self.deno_exe = deno_exe

def run(self,
arg):
"Returns (return_code, stdout, stderr)."
dsseng marked this conversation as resolved.
Show resolved Hide resolved
cmd = [self.deno_exe, IS_TTY_TEST_TS, arg]
return tty_capture(cmd, b'')

def test_stdin(self):
code, stdout, _ = self.run('stdin')
assert code == 0
assert str(stdin.isatty()).lower() in stdout

def test_stdout(self):
code, stdout, _ = self.run('stdout')
assert code == 0
assert str(stdin.isatty()).lower() in stdout

def test_stderr(self):
code, stdout, _ = self.run('stderr')
assert code == 0
assert str(stdin.isatty()).lower() in stdout

def is_tty_test(deno_exe):
p = IsTTY(deno_exe)
p.test_stdin()
p.test_stdout()
p.test_stderr()
dsseng marked this conversation as resolved.
Show resolved Hide resolved


def main():
deno_exe = os.path.join(build_path(), "deno" + executable_suffix)
is_tty_test(deno_exe)


if __name__ == "__main__":
main()
22 changes: 22 additions & 0 deletions tools/is_tty_test.ts
@@ -0,0 +1,22 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { args, exit, isTTY } from "deno";

const name = args[1];
const test = {
stdin: () => {
console.log(isTTY().stdin);
},
stdout: () => {
console.log(isTTY().stdout);
},
stderr: () => {
console.log(isTTY().stderr);
}
}[name];

if (!test) {
console.log("Unknown test:", name);
exit(1);
}

test();
dsseng marked this conversation as resolved.
Show resolved Hide resolved
3 changes: 3 additions & 0 deletions tools/test.py
Expand Up @@ -13,6 +13,7 @@
from benchmark_test import benchmark_test
from repl_test import repl_tests
from prefetch_test import prefetch_test
from is_tty_test import is_tty_test
import subprocess
import http_server

Expand Down Expand Up @@ -71,6 +72,8 @@ def main(argv):
from permission_prompt_test import permission_prompt_test
permission_prompt_test(deno_exe)

is_tty_test(deno_exe)

repl_tests(deno_exe)

rmtree(deno_dir)
Expand Down