Executables for PBT
Executables are how software programs run and interact with other software. Programming their interaction is a useful skill to have–-as is being able to pick up the specific syntax for process interactions within a new programming language by using the language's documentation.
Here, we'll go over some of the basics, but I encourage you to reference online documentation and tutorials liberally for this part!
You should follow along for either Python or Rust here, not both.
Python
Python comes pre-installed on CS Linux and on Macs. If you would like to install Python in your own computer and have not yet, grab version 3.X from the Python website.
Python's official online documentation also provides an introduction.
Rust
Install Rust (any version, default to current) from the Rust website.
Making Executables
For Property-Based Testing (PBT), you need to create two executable programs: topsort
and oracle
.
Files need executable permissions in order to be executed (figures). To set executable permissions, run
chmod +x topsort
and
chmod +x oracle
To learn the details of Unix permissions, we recommend reviewing the relevant parts of this Wikipedia article.
Python
To make a file executable, we must provide an interpreter directive (colloquially known as the "hashbang" on Unix machines). You'll want to create the files topsort
and oracle
(note: no .py
in the name) and temporarily add the following lines to the top of each:
print("Hello World!")
To test your programs, run ./topsort
and ./oracle
.
The #!
line serves as the interpreter directive, and the Python interpreter will run the file's code when executed.
Rust
For Rust, the Rust Book has a great walkthrough of creating a project with an executable.
You can choose whether or not to create two separate Rust projects, one per executable, or to put both executables in the same project.
To make two separate project, run:
cargo new topsort
cargo new oracle
To create one project (where you'll need to later rename the executable), run:
cargo new pbt
cd
inside the project directory (say, topsort
) and run cargo build
to create your executable.
The executable will be created in ./target/debug/topsort
. You can mv
the executable to your project directory for ease of calling it (or you can just pass the full path to other commands).
If you want to use one project with two executables (which is slightly better style, but will not be graded differently for this project), check out the crate
documentation. You'll want two binary crates inside your single project, one for each executable.
Command Line Arguments
Python
Python's sys
module provides access to command line arguments. To import the module, add import sys
to your program. The expression
sys.argv
produces a list of strings corresponding to command line arguments. Note that the program's name comprises the first element of this list, so sys.argv[1]
returns the first user-specified command line argument.
Rust
Rust's corresponding crate is also named sys
, and you can import it with use std::env;
(see the book chapter).
You can then access the arguments with:
let args: Vec<String> = env::args().collect();
Standard I/O
Python
Python's libraries and built-in functions offer several ways of accessing standard input and output. We suggest accessing standard input and output through the sys
module, also used for accessing command line arguments.
sys.stdin.readlines()
produces a list of strings corresponding to each line of standard input.
Similarly,
sys.stdout.write("Hello World!")
writes "Hello World!" to standard output (as does print("Hello World!")
).
Rust
You'll want to import use std::fs;
to interact with the filesystem. Then, for example (from the book chapter):
let contents = fs::read_to_string(file_path)
.expect("Should have been able to read the file");
The simplest way to write to standard out is with println!(...)
. See the documentation for writing multiple lines if needed.
Opening a new process
For your oracle
executable to evaluate your topsort
executable, the oracle
process needs to run topsort
as a subprocess (relevant CS240 slides). As you may recall from CS240, there are specific ways for processes to communicate. When a parents and child process need to communicate only at the start and end of the child process, the easiest way to communicate is by "piping" data between their standard input and output.
Each of these streams of data can consist of raw bytes, but in this case, we'll write strings of data with the graph input/output text formatting described in the assignment.
Python
Python's subprocess
module provides an interface for running other processes from within a program. For example, in oracle
, you'll need to run a topsort program. To import the module, add import subprocess
to your program. To start a process and bind its handle to a variable, use a statement of the following form:
import subprocess
process = subprocess.Popen(
["/usr/bin/tsort"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
Additional arguments to the process follow in the list of the first argument. One way to write to the standard input of process
is to use the communicate
method:
in_str = "1 2\n3 4\n"
in_data = in_str.encode('utf-8')
(out_data, err_data) = process.communicate(in_data)
out_str = out_data.decode('utf-8')
err_str = err_data.decode('utf-8')
out_str
contains the standard output and err_str
contains the standard error.
Rust
Here is a modified example from the documentation of starting a subprocess (in this case, the ls
utility with argument -a
):
let mut p = Popen::create(&["ls", "-a"], PopenConfig {
stdout: Redirection::Pipe, ..Default::default()
})?;
let (out, err) = p.communicate(None)?;
out
contains the standard output and err
contains the standard error.
Running from Command Line
Suppose you have a file named myfile.txt
with the following content:
7 11
7 8
3 10
5 11
3 8
8 9
11 10
11 2
11 9
To run your topsort
or your system's built-in tsort
program with this data, use a command of the following form to redirect the contents of the file as standard input:
./topsort < myfile.txt
Similarly, to invoke your oracle
, run:
./oracle /usr/bin/tsort 50
./oracle ./topsort 50
Attribution
The Python components of this guide are cribbed directly from Brown University's Logic for Systems Lab 0. Thank you to the Brown UTAs for writing it (including but not limited to past Alexa circa 2016 ).
Executables for PBT
Executables are how software programs run and interact with other software. Programming their interaction is a useful skill to have–-as is being able to pick up the specific syntax for process interactions within a new programming language by using the language's documentation.
Here, we'll go over some of the basics, but I encourage you to reference online documentation and tutorials liberally for this part!
You should follow along for either Python or Rust here, not both.
Python
Python comes pre-installed on CS Linux and on Macs. If you would like to install Python in your own computer and have not yet, grab version 3.X from the Python website.
Python's official online documentation also provides an introduction.
Rust
Install Rust (any version, default to current) from the Rust website.
Making Executables
For Property-Based Testing (PBT), you need to create two executable programs:
topsort
andoracle
.Files need executable permissions in order to be executed (figures). To set executable permissions, run
and
To learn the details of Unix permissions, we recommend reviewing the relevant parts of this Wikipedia article.
Python
To make a file executable, we must provide an interpreter directive (colloquially known as the "hashbang" on Unix machines). You'll want to create the files
topsort
andoracle
(note: no.py
in the name) and temporarily add the following lines to the top of each:#!/usr/bin/python3 print("Hello World!")
To test your programs, run
./topsort
and./oracle
.The
#!
line serves as the interpreter directive, and the Python interpreter will run the file's code when executed.Rust
For Rust, the Rust Book has a great walkthrough of creating a project with an executable.
You can choose whether or not to create two separate Rust projects, one per executable, or to put both executables in the same project.
To make two separate project, run:
To create one project (where you'll need to later rename the executable), run:
cd
inside the project directory (say,topsort
) and runcargo build
to create your executable.The executable will be created in
./target/debug/topsort
. You canmv
the executable to your project directory for ease of calling it (or you can just pass the full path to other commands).If you want to use one project with two executables (which is slightly better style, but will not be graded differently for this project), check out the
crate
documentation. You'll want two binary crates inside your single project, one for each executable.Command Line Arguments
Python
Python's
sys
module provides access to command line arguments. To import the module, addimport sys
to your program. The expressionproduces a list of strings corresponding to command line arguments. Note that the program's name comprises the first element of this list, so
sys.argv[1]
returns the first user-specified command line argument.Rust
Rust's corresponding crate is also named
sys
, and you can import it withuse std::env;
(see the book chapter).You can then access the arguments with:
Standard I/O
Python
Python's libraries and built-in functions offer several ways of accessing standard input and output. We suggest accessing standard input and output through the
sys
module, also used for accessing command line arguments.produces a list of strings corresponding to each line of standard input.
Similarly,
sys.stdout.write("Hello World!")
writes "Hello World!" to standard output (as does
print("Hello World!")
).Rust
You'll want to import
use std::fs;
to interact with the filesystem. Then, for example (from the book chapter):The simplest way to write to standard out is with
println!(...)
. See the documentation for writing multiple lines if needed.Opening a new process
For your
oracle
executable to evaluate yourtopsort
executable, theoracle
process needs to runtopsort
as a subprocess (relevant CS240 slides). As you may recall from CS240, there are specific ways for processes to communicate. When a parents and child process need to communicate only at the start and end of the child process, the easiest way to communicate is by "piping" data between their standard input and output.Each of these streams of data can consist of raw bytes, but in this case, we'll write strings of data with the graph input/output text formatting described in the assignment.
Python
Python's
subprocess
module provides an interface for running other processes from within a program. For example, inoracle
, you'll need to run a topsort program. To import the module, addimport subprocess
to your program. To start a process and bind its handle to a variable, use a statement of the following form:import subprocess process = subprocess.Popen( ["/usr/bin/tsort"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE )
Additional arguments to the process follow in the list of the first argument. One way to write to the standard input of
process
is to use thecommunicate
method:in_str = "1 2\n3 4\n" in_data = in_str.encode('utf-8') (out_data, err_data) = process.communicate(in_data) out_str = out_data.decode('utf-8') err_str = err_data.decode('utf-8')
out_str
contains the standard output anderr_str
contains the standard error.Rust
Here is a modified example from the documentation of starting a subprocess (in this case, the
ls
utility with argument-a
):out
contains the standard output anderr
contains the standard error.Running from Command Line
Suppose you have a file named
myfile.txt
with the following content:To run your
topsort
or your system's built-intsort
program with this data, use a command of the following form to redirect the contents of the file as standard input:Similarly, to invoke your
oracle
, run:Attribution
The Python components of this guide are cribbed directly from Brown University's Logic for Systems Lab 0. Thank you to the Brown UTAs for writing it (including but not limited to past Alexa circa 2016 ).