Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sys.process to wrap a process as a function

Tags:

scala

I have an external process that I would like to treat as a function from String=>String. Given a line of input, it will respond with a single line of output. It seems that I should use scala.sys.process, which is clearly an elegant library that makes many shell operations easily accessible from within scala. However, I can't figure out how to perform this simple use case.

If I write a single line to the process' stdin, it prints the result in a single line. How can I use sys.process to create a wrapper so I can use the process interactively? For example, if I had an implementation for ProcessWrapper, here is a program and it's output:

// abstract definition
class ProcessWrapper(executable: String) {
  def apply(line: String): String
}

// program using an implementation
val process = new ProcessWrapper("cat -b")
println(process("foo"))
println(process("bar"))
println(process("baz"))

Output:

 1  foo
 2  bar
 3  baz

It is important that the process is not reloaded for each call to process because there is a significant initialization step.

like image 808
schmmd Avatar asked Jan 30 '13 01:01

schmmd


People also ask

What is function wrapping in Python?

Wrappers around the functions are also knows as decorators which are a very powerful and useful tool in Python since it allows programmers to modify the behavior of function or class. Decorators allow us to wrap another function in order to extend the behavior of the wrapped function, without permanently modifying it.

What is wrapping in PL/SQL?

Wrapping is the process of hiding PL/SQL source code. Wrapping helps to protect your source code by making it more difficult for others to view it. You can wrap PL/SQL source code with either the wrap utility or DBMS_DDL subprograms. The wrap utility wraps a single source file, such as a SQL*Plus script.

How do I use the wrap utility in Oracle?

The wrap utility need not connect to Oracle Database (in fact, it cannot connect to Oracle Database). To run the wrap utility, enter the wrap command at your operating system prompt using this syntax (with no spaces around the equal signs): input_file is the name of a file containing SQL statements, which you typically run using SQL*Plus.

How to invoke a function from the main process in workerpool?

To invoke our function from the main process, we need to tell workerpool about the worker.ts file and then tell it to invoke the function by name:


2 Answers

So - after my comment - this would be my solution

import java.io.BufferedReader
import java.io.File
import java.io.InputStream
import java.io.InputStreamReader

import scala.annotation.tailrec

class ProcessWrapper(cmdLine: String, lineListenerOut: String => Unit, lineListenerErr: String => Unit,
                     finishHandler: => Unit,
                     lineMode: Boolean = true, envp: Array[String] = null, dir: File = null) {

  class StreamRunnable(val stream: InputStream, listener: String => Unit) extends Runnable {
    def run() {
      try {
        val in = new BufferedReader(new InputStreamReader(this.stream));
        @tailrec
        def readLines {
          val line = in.readLine
          if (line != null) {
            listener(line)
            readLines
          }
        }
        readLines
      }
      finally {
        this.stream.close
        finishHandler
      }
    }
  }
  val process = Runtime.getRuntime().exec(cmdLine, envp, dir);
  val outThread = new Thread(new StreamRunnable(process.getInputStream, lineListenerOut), "StreamHandlerOut")
  val errThread = new Thread(new StreamRunnable(process.getErrorStream, lineListenerErr), "StreamHandlerErr")
  val sendToProcess = process.getOutputStream
  outThread.start
  errThread.start

  def apply(txt: String) {
    sendToProcess.write(txt.getBytes)
    if (lineMode)
      sendToProcess.write('\n')
    sendToProcess.flush
  }

}

object ProcessWrapper {
  def main(args: Array[String]) {
    val process = new ProcessWrapper("python -i", txt => println("py> " + txt),
      err => System.err.println("py err> " + err), System.exit(0))
    while (true) {
      process(readLine)
    }
  }
}

The main part is the StreamRunnable, where the process is read in a thread and the received line is passed on to a "LineListener" (a simple String => Unit - function). The main is just a sample implementation - calling python ;)

like image 67
michael_s Avatar answered Sep 21 '22 10:09

michael_s


I'm not sure, but you want somethings like that ?

  case class ProcessWrapper(executable: String) {
    import java.io.ByteArrayOutputStream
    import scala.concurrent.duration.Duration
    import java.util.concurrent.TimeUnit

    lazy val process = sys.runtime.exec(executable)

    def apply(line: String, blockedRead: Boolean = true): String = {
      process.getOutputStream().write(line.getBytes())
      process.getOutputStream().flush()
      val r = new ByteArrayOutputStream
      if (blockedRead) {
        r.write(process.getInputStream().read())
      }
      while (process.getInputStream().available() > 0) {
        r.write(process.getInputStream().read())
      }
      r.toString()
    }

    def close() = process.destroy()
  }

  val process = ProcessWrapper("cat -b")

  println(process("foo\n"))
  println(process("bar\n"))
  println(process("baz\n"))
  println(process("buz\n"))
  println(process("puz\n"))

  process.close

Result :

 1    foo

 2    bar

 3    baz

 4    buz

 5    puz

I think that PlayCLI is a better way.

like image 38
twillouer Avatar answered Sep 23 '22 10:09

twillouer