Vinayak Mehta

Day 58 — I miss the Python standard library

Nov 01, 2020 · recurse-center

Today I worked on translating cutypr to Rust.

I found that I could use pyo3 to start the IPython kernel from Rust! So I wrote a start_kernel() Python function to start the IPython kernel, returned some useful information (the key required to sign messages + ports for all kernel channels) from it, and then called it from Rust using pyo3!


  fn start_kernel(py: Python) -> Value {
      let locals = [("jupyterm", py.import("jupyterm").unwrap())].into_py_dict(py);
      let code = "jupyterm.start_kernel()";
      let kernel_info_str: &str = py
          .eval(code, None, Some(&locals))
          .unwrap()
          .extract()
          .unwrap();
      let kernel_info: Value = serde_json::from_str(kernel_info_str).unwrap();
      kernel_info
  }

  fn main() {
      let mut kernel_info: Value = serde_json::from_str("{}").unwrap();

      // start the Python kernel
      // TODO: also shut it down
      Python::with_gil(|py| {
          kernel_info = start_kernel(py);
      });
  }

I still need to figure out how to shutdown the kernel after everything is done though!

After that I looked into how to create a REPL in Rust. These are some things I missed from Python and its standard library:

  • The input() function: Getting user input in Python is very straightforward with the input() function. It also supports adding prompts, like input(">>> "). I couldn't find something similar (maybe an input! macro?) in Rust. There are multiple crates that implement some form of this though. I found read-human in this forum answer, and reused its code directly.

    If you want to get user input (and also print a prompt), you need to first print the prompt using the print! macro, and then get the input using io::stdin().read_line:

  use std::io::{self, Write};

  fn main() {
      let mut code = String::new();
      loop {
          code.clear();

          print!(">>> ");
          io::stdout().flush().unwrap();
          io::stdin().read_line(&mut code).unwrap();
      }
  }

  • dir() and type() functions: When debugging Python code, I usually fire up the REPL and use dir() to look at all the things an object provides, and type() to check types of variables.

    I found this Rust REPL, this print_type_of() function, and the :? formatting marker for println! pretty useful for debugging though!

  fn print_type_of(_: &T) {
      println!("{}", std::any::type_name::())
  }

  • json and datetime modules: In Rust, serde seems to be the most widely used crate to serialize/deserialize data. I need to look for a nice alternative to the Python datetime module. (os and pathlib modules too!)

  • hmac and hashlib modules: I used these modules yesterday to sign messages before sending them to the IPython kernel.

    I found the hmac and sha2 crates, but their final result does not seem to be a primitive Rust type. It's something called a GenericArray, which comes from another crate! I need to figure out how to use it.


  >> print_type_of(&code_bytes);
  generic_array::GenericArray, typenum::bit::B0>, typenum::bit::B0>, typenum::bit::B0>, typenum::bit::B0>, typenum::bit::B0>>