Skip to content

Commit

Permalink
[refactor] Cosmetic changes to core/process.py
Browse files Browse the repository at this point in the history
Also add demo/jobs-builtin.sh, which reminds me of what we have to do
for 'jobs' and 'jobs -l'.

This is related to issue #360.
  • Loading branch information
Andy C committed Nov 16, 2021
1 parent d2796b9 commit cd9786e
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 14 deletions.
45 changes: 31 additions & 14 deletions core/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from _devbuild.gen.id_kind_asdl import Id
from _devbuild.gen.runtime_asdl import (
job_state_e, job_state_t,
job_state_e, job_state_t, job_state_str,
wait_status, wait_status_t,
redirect, redirect_arg_e, redirect_arg__Path, redirect_arg__CopyFd,
redirect_arg__MoveFd, redirect_arg__HereDoc,
Expand Down Expand Up @@ -690,8 +690,10 @@ def DisplayLine(self):
# type: () -> str

# NOTE: These can be pieces of a pipeline, so they're arbitrary nodes.
# TODO: We should extract the SPIDS from each node!
return '[subprog] %s' % ui.CommandType(self.node)
# TODO: Extract SPIDS from node to display source?

thunk_str = ui.CommandType(self.node)
return '[subprog] %s' % thunk_str

def Run(self):
# type: () -> None
Expand Down Expand Up @@ -831,7 +833,8 @@ def Init_ParentPipeline(self, pi):

def __repr__(self):
# type: () -> str
return '<Process %s>' % self.thunk
s = ' %s' % self.parent_pipeline if self.parent_pipeline else ''
return '<Process %s%s>' % (self.thunk, s)

def AddStateChange(self, s):
# type: (ChildStateChange) -> None
Expand Down Expand Up @@ -966,7 +969,15 @@ def __init__(self, sigpipe_status_ok):

def __repr__(self):
# type: () -> str
return '<Pipeline %s>' % ' '.join(repr(p) for p in self.procs)
parts = ['<Pipeline\n']
parts.append(' procs=%s\n' % ' '.join(repr(p) for p in self.procs))
parts.append(' last_thunk=%s\n' % (self.last_thunk,))
parts.append(' pipe_status=%s\n' % self.pipe_status)
parts.append('>\n')
return ''.join(parts)

def DisplayLine(self):
return 'Pipeline %s\n' % self.procs

def Add(self, p):
# type: (Process) -> None
Expand Down Expand Up @@ -999,8 +1010,6 @@ def AddLast(self, thunk):
return

r, w = posix.pipe()
#log('last pipe %d %d', r, w)

prev = self.procs[-1]
prev.AddStateChange(StdoutToPipe(r, w))

Expand Down Expand Up @@ -1084,7 +1093,7 @@ def Run(self, waiter, fd_state):
# ls | wc -l
# echo foo | read line # no need to fork

cmd_ev, node = self.last_thunk
cmd_ev, last_node = self.last_thunk

#log('thunk %s', self.last_thunk)
if self.last_pipe is not None:
Expand All @@ -1097,7 +1106,7 @@ def Run(self, waiter, fd_state):
# exec() rather than fork/exec().

try:
cmd_ev.ExecuteAndCatch(node)
cmd_ev.ExecuteAndCatch(last_node)
finally:
fd_state.Pop()
# We won't read anymore. If we don't do this, then 'cat' in 'cat
Expand All @@ -1106,9 +1115,9 @@ def Run(self, waiter, fd_state):

else:
if len(self.procs):
cmd_ev.ExecuteAndCatch(node) # Background pipeline without last_pipe
cmd_ev.ExecuteAndCatch(last_node) # Background pipeline without last_pipe
else:
cmd_ev._Execute(node) # singleton foreground pipeline, e.g. '! func'
cmd_ev._Execute(last_node) # singleton foreground pipeline, e.g. '! func'

self.pipe_status[-1] = cmd_ev.LastStatus()
#log('pipestatus before all have finished = %s', self.pipe_status)
Expand Down Expand Up @@ -1142,6 +1151,11 @@ def WhenDone(self, pid, status):
self.state = job_state_e.Done


def _JobStateStr(i):
# type: (job_state_t) -> str
return job_state_str(i)[10:] # remove 'job_state.'


class JobState(object):
"""Global list of jobs, used by a few builtins."""

Expand Down Expand Up @@ -1261,14 +1275,15 @@ def List(self):
# TODO: don't use __repr__ and so forth

print('Jobs:')
for pid, job in iteritems(self.jobs):
for job_id, job in iteritems(self.jobs):
# Use the %1 syntax
print('%%%d %s %s' % (pid, job.State(), job))
print('%%%d %7s %s' % (job_id, _JobStateStr(job.State()), job))

print('')
print('Processes:')
for pid, proc in iteritems(self.child_procs):
print('%d %s %s' % (pid, proc.state, proc.thunk.DisplayLine()))
p = ' |' if proc.parent_pipeline else ''
print('%d %7s %s%s' % (pid, _JobStateStr(proc.state), proc.thunk.DisplayLine(), p))

def ListRecent(self):
# type: () -> None
Expand Down Expand Up @@ -1408,6 +1423,8 @@ def WaitForOne(self, eintr_retry):
return True # caller should keep waiting

proc = self.job_state.child_procs[pid]
if 0:
self.job_state.List()

if WIFSIGNALED(status):
term_sig = WTERMSIG(status)
Expand Down
68 changes: 68 additions & 0 deletions demo/jobs-builtin.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
#
# Compare the output of "jobs" in different shells
#
# Usage:
# $SH demo/jobs-builtin.sh <function name>

set -o nounset
#set -o pipefail
set -o errexit

# Notes:
# - the formats are consistent, because it's specified by POSIX
# - they use [1] [2] [3]
# - + is the default job for 'fg' and 'bg', which is also %%
# - - is the job that would become the default, which is also %-
#
# "[%d] %c %s %s\n", <job-number>, <current>, <state>, <command>
#
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/jobs.html
#
# TODO: Oil should print this as QSN! So you can parse it.
#
# - dash is the only shell that doesn't show source code; it just shows | for pipe
# - mksh has some weird quoting, like \sleep 1
# - mksh and zsh doesn't show trailing &, but bash does
#

show_jobs() {
echo ___

# all shells support this long format which shows components of a pipeline
jobs -l
#jobs
}

myfunc() {
sleep 0.3
show_jobs

sleep 0.4
show_jobs
}


demo() {
sleep 1 & sleep 2 &
show_jobs

{ echo pipe1; sleep 0.5; } | cat &
show_jobs

myfunc &

# only bash supports wait -n
if test -n "${BASH_VERSION:-}"; then
wait -n
show_jobs
fi

wait
show_jobs

ls | wc -l
show_jobs
}

"$@"
3 changes: 3 additions & 0 deletions test/py3_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ def main(argv):

print('Parsed %s: %s' % (filename, module))

# TODO:
# - Use Python 3.10 and match statements here!
# - Parse type comments out of __init__() like self.field = field
if type_comments:
for stmt in module.body:
if isinstance(stmt, ast.FunctionDef):
Expand Down

0 comments on commit cd9786e

Please sign in to comment.