From b873e786ab02a6644ebdfd2db56b2b1e937fb92b Mon Sep 17 00:00:00 2001 From: Jim Youngquist Date: Tue, 17 Mar 2015 18:16:10 -0700 Subject: [PATCH] started docs on ipython_protocol. need to finish --- src/cpp_plot.hpp | 6 +- src/ipython_protocol.cc | 24 ++++--- src/ipython_protocol.hpp | 131 ++++++++++++++++++++++++++++++++------- 3 files changed, 128 insertions(+), 33 deletions(-) diff --git a/src/cpp_plot.hpp b/src/cpp_plot.hpp index 8e56e7f..fefcf65 100644 --- a/src/cpp_plot.hpp +++ b/src/cpp_plot.hpp @@ -20,7 +20,8 @@ std::string LoadFile(std::string filename); * * The NumpyArray is named at construction with the name of the python * variable into which its data will be placed. The name is immutable, but - * the data held by this array is not. + * the data held by this array is not. The data must be of type + * NumpyArray::dtype. * * Usage: \code @@ -38,6 +39,9 @@ std::string LoadFile(std::string filename); */ class NumpyArray { public: + /** The type expected by numpy when reading the raw data buffer. The data + * passed to NumpyArray must be of this type. + */ typedef double dtype; //-------------------------------------------------- diff --git a/src/ipython_protocol.cc b/src/ipython_protocol.cc index 2cba820..037093b 100644 --- a/src/ipython_protocol.cc +++ b/src/ipython_protocol.cc @@ -5,6 +5,10 @@ #include "ipython_protocol.hpp" +/// Delimeter used by the iPython messaging protocol to separate ZMQ +/// identities from message data. +static const std::string DELIM{""}; + std::string GetUuid (void) { uuid_t uuid; char uuid_str[37] = {'\0'}; @@ -49,7 +53,7 @@ IPyKernelConfig::IPyKernelConfig (const std::string &jsonConfigFile) { std::vector IPythonMessage::GetMessageParts (void) const { - return {header_, parent_, metadata_, content_}; + return {header, parent, metadata, content}; } @@ -57,13 +61,13 @@ IPythonMessage MessageBuilder::BuildExecuteRequest ( const std::string &code) const { IPythonMessage message{ident_}; - message.header_["msg_type"] = "execute_request"; - message.content_["code"] = code; - message.content_["silent"] = false; - message.content_["store_history"] = true; - message.content_["user_variables"] = Json::Value(Json::arrayValue); - message.content_["user_expressions"] = Json::Value(Json::objectValue); - message.content_["allow_stdin"] = false; + message.header["msg_type"] = "execute_request"; + message.content["code"] = code; + message.content["silent"] = false; + message.content["store_history"] = true; + message.content["user_variables"] = Json::Value(Json::arrayValue); + message.content["user_expressions"] = Json::Value(Json::objectValue); + message.content["allow_stdin"] = false; return message; } @@ -141,9 +145,9 @@ void ShellConnection::RunCode (const std::string &code) { bool ShellConnection::HasVariable (const std::string &variable_name) { IPythonMessage command = message_builder_.BuildExecuteRequest("None"); - command.content_["user_variables"].append(variable_name); + command.content["user_variables"].append(variable_name); IPythonMessage response = Send(command); - return response.content_["user_variables"][variable_name]["status"] + return response.content["user_variables"][variable_name]["status"] .asString() == "ok"; } diff --git a/src/ipython_protocol.hpp b/src/ipython_protocol.hpp index 4f458c8..846aa44 100644 --- a/src/ipython_protocol.hpp +++ b/src/ipython_protocol.hpp @@ -20,65 +20,146 @@ extern "C" { struct IPyKernelConfig; // forward def +/// Function object that computes an HMAC hash from a vector of JSON values. typedef std::function)> HmacFn; -enum class PortType {SHELL, IOPUB, STDIN, HB}; -const std::string DELIM{""}; +/// The different kinds of socket ports that an iPython kernel listens on. +enum class PortType {SHELL, IOPUB, STDIN, HB}; +//-------------------------------------------------- +/** \brief Generates a new UUID + */ std::string GetUuid(void); + +//-------------------------------------------------- +/** \brief Returns a string URI like "tcp://hostname:port" from its args. + * + * \param config Config containing hostname and transport type. + * \param port Port the host is listening on. + */ std::string BuildUri(const IPyKernelConfig &config, PortType port); +//====================================================================== +/** \brief Configuration values for connecting to an iPython kernel. See + * http://ipython.org/ipython-doc/1/development/messaging.html for more + * details. + */ struct IPyKernelConfig { + /// The port the shell socket is listening on size_t shell_port; + + /// The port the iopub socket is listening on size_t iopub_port; + + /// The port the stdin socket is listening on size_t stdin_port; + + /// The port the shell is listening on size_t hb_port; + + /// The IP address the kernel is listening on std::string ip; + + /// The transport used by the kernel, e.g., "tcp" std::string transport; + + /// \brief The hash function used to generate HMACs. Only sha-256 is + /// supported at this time. std::string signature_scheme; + + /// The key used for HMAC generation std::string key; + //-------------------------------------------------- + /** \brief Constructs a new IPyKernelConfig from a JSON file on disk, such + * as the one generated by \code ipython kernel --pylab \endcode. + */ IPyKernelConfig (const std::string &jsonConfigFile); }; -class IPythonMessage { -public: - Json::Value header_; - Json::Value parent_; - Json::Value metadata_; - Json::Value content_; +//====================================================================== +/** \brief Struct that wraps up the different data (i.e. not metadata like + * HMAC) fields for an IPython ZMQ General Message Format. See + * http://ipython.org/ipython-doc/1/development/messaging.html#general-message-format + * for more detail. + */ +struct IPythonMessage { + /// The header = {'msg_id', 'username', 'session', 'msg_type'} + Json::Value header; + + /// The header of this message's parent. To associate response with request + Json::Value parent; + /// Metadata for this message. Seems to be unused? + Json::Value metadata; + + /// Content payload for this message. msg_type dependant. + Json::Value content; + + //-------------------------------------------------- + /** \brief Constructs an empty message for a specific session. + * + * \param ident identity of the session this message belongs to. + */ explicit IPythonMessage(const std::string &ident) - : header_{Json::objectValue}, - parent_{Json::objectValue}, - metadata_{Json::objectValue}, - content_{Json::objectValue} + : header{Json::objectValue}, + parent{Json::objectValue}, + metadata{Json::objectValue}, + content{Json::objectValue} { char username[80]; getlogin_r(username, 80); - header_["username"] = std::string(username); - header_["session"] = ident; - header_["msg_id"] = GetUuid(); + header["username"] = std::string(username); + header["session"] = ident; + header["msg_id"] = GetUuid(); } + //-------------------------------------------------- + /** \brief Constructs a message with predefined fields. + * + * \param message_parts A vector of JSON objects to assign to each of the + * fields. It assumes the elements will be in order + * [header, parent_header, metadata, content]. + */ explicit IPythonMessage(const std::vector &message_parts) - : header_(message_parts[0]), - parent_(message_parts[1]), - metadata_(message_parts[2]), - content_(message_parts[3]) + : header(message_parts[0]), + parent(message_parts[1]), + metadata(message_parts[2]), + content(message_parts[3]) {} std::vector GetMessageParts(void) const; }; +//====================================================================== +/** \brief Class of factory methods for constructing various kinds of + * IPythonMessages associated with a particular session. + * + * Usage: +\code + MessageBuilder builder("my_session_id"); + IPythonMessage command = builder.BuildExecuteRequest("print 'Hello!'"); +\endcode + */ class MessageBuilder { public: - MessageBuilder(const std::string &ident) : ident_(ident) {} - + //-------------------------------------------------- + /** \brief Constructs a new MessageBuilder associated with a specific + * session. + * + * \param ident the identity of the session, any arbitrary string, but . + */ + explicit MessageBuilder(const std::string &ident) : ident_(ident) {} + + //-------------------------------------------------- + /** \brief Returns a new ExecuteRequest message. + * + * \param code The code to be run in an iPython kernel. + */ IPythonMessage BuildExecuteRequest (const std::string &code) const ; private: @@ -86,8 +167,14 @@ class MessageBuilder { }; +//====================================================================== +/** \brief A connection to the shell socket of an iPython kernel. + * + * TODO fill this out! + */ class ShellConnection { public: + // ShellConnection(const IPyKernelConfig &config, zmq::context_t &context, const HmacFn &hmac_fn) : hmac_fn_{hmac_fn}, @@ -113,7 +200,7 @@ class ShellConnection { class IPythonSession { public: - IPythonSession (const IPyKernelConfig &config) + explicit IPythonSession (const IPyKernelConfig &config) : config_{config}, zmq_context_{1}, hmac_fn_{std::bind(&IPythonSession::ComputeHMAC_, this,