Skip to content

Update python-parser's cpython dependency #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 27, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions python-parser/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "python-parser"
version = "0.2.1"
version = "0.2.2"
authors = ["Tibor Benke <[email protected]>"]
build = "build.rs"

Expand All @@ -9,7 +9,7 @@ crate-type = ["rlib", "dylib"]

[dependencies]
syslog-ng-common = { path = "../syslog-ng-rs/syslog-ng-common" }
cpython = { git = "https://github.com/ihrwein/rust-cpython.git" }
cpython = { git = "https://github.com/ihrwein/rust-cpython.git", branch = "make-generated-type-public" }
log = "0.3"
env_logger = "0.3"

Expand Down
73 changes: 51 additions & 22 deletions python-parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ pub mod options {

pub struct PythonParser<P: Pipe> {
parser: PyObject,
_marker: PhantomData<P>
_marker: PhantomData<P>,
}

pub struct PythonParserBuilder<P: Pipe> {
module: Option<String>,
class: Option<String>,
options: Vec<(String, String)>,
_marker: PhantomData<P>
_marker: PhantomData<P>,
}

impl<P: Pipe> PythonParserBuilder<P> {
Expand All @@ -41,7 +41,7 @@ impl<P: Pipe> PythonParserBuilder<P> {
debug!("Trying to load Python module, module='{}'", module_name);
py.import(module_name)
}
pub fn load_class<'p>(py: Python<'p>, module: &PyModule, class_name: &str) -> PyResult<PyObject> {
pub fn load_class<'p>(py: Python<'p>, module: &PyModule, class_name: &str) -> PyResult<PyObject> {
debug!("Trying to load Python class, class='{}'", class_name);
module.get(py, class_name)
}
Expand All @@ -53,13 +53,15 @@ impl<P: Pipe> PythonParserBuilder<P> {
debug!("Instantiating the options dict");
let options = PyDict::new(py);
for &(ref k, ref v) in init_options {
debug!("Adding values to the options dict, key='{}', value='{}'", k, v);
debug!("Adding values to the options dict, key='{}', value='{}'",
k,
v);
try!(options.set_item(py, k, v));
}
Ok(options)
}
fn call_init<'p>(py: Python<'p>, instance: &PyObject, options: PyDict) -> PyResult<()> {
let init_result = try!(instance.call_method(py, "init", (&options, ), None));
let init_result = try!(instance.call_method(py, "init", (&options,), None));
if init_result == Python::None(py) {
Ok(())
} else {
Expand All @@ -82,7 +84,11 @@ impl<P: Pipe> PythonParserBuilder<P> {
Ok(parser_instance)
}

pub fn load_and_init_class<'p>(py: Python<'p>, module_name: &str, class_name: &str, options: &[(String, String)]) -> PyResult<PyObject> {
pub fn load_and_init_class<'p>(py: Python<'p>,
module_name: &str,
class_name: &str,
options: &[(String, String)])
-> PyResult<PyObject> {
let module = try!(Self::load_module(py, module_name));
let mut dict = module.dict(py);

Expand All @@ -94,17 +100,33 @@ impl<P: Pipe> PythonParserBuilder<P> {
}

fn python_register_callbacks(py: Python, dict: &mut PyDict) -> PyResult<()> {
try!(python_register_callback(py, dict, "error", py_fn!(python_error_callback(error_message: &str))));
try!(python_register_callback(py, dict, "info", py_fn!(python_info_callback(info_message: &str))));
try!(python_register_callback(py, dict, "trace", py_fn!(python_trace_callback(trace_message: &str))));
try!(python_register_callback(py, dict, "warning", py_fn!(python_warning_callback(warning_message: &str))));
try!(python_register_callback(py, dict, "debug", py_fn!(python_debug_callback(debug_message: &str))));
try!(python_register_callback(py,
dict,
"error",
py_fn!(py, python_error_callback(error_message: &str))));
try!(python_register_callback(py,
dict,
"info",
py_fn!(py, python_info_callback(info_message: &str))));
try!(python_register_callback(py,
dict,
"trace",
py_fn!(py, python_trace_callback(trace_message: &str))));
try!(python_register_callback(py,
dict,
"warning",
py_fn!(py, python_warning_callback(warning_message: &str))));
try!(python_register_callback(py,
dict,
"debug",
py_fn!(py, python_debug_callback(debug_message: &str))));
Ok(())
}

fn python_register_callback<F: ToPyObject>(py: Python, dict: &mut PyDict, name: &str, function: F) -> PyResult<()> {
if try!(dict.contains(py, name)) {
warn!("Already implemented {}() function, omitting callback definition.", name);
warn!("Already implemented {}() function, omitting callback definition.",
name);
} else {
try!(dict.set_item(py, name, function));
}
Expand Down Expand Up @@ -142,7 +164,7 @@ impl<P: Pipe> Clone for PythonParserBuilder<P> {
module: self.module.clone(),
class: self.class.clone(),
options: self.options.clone(),
_marker: PhantomData
_marker: PhantomData,
}
}
}
Expand All @@ -154,17 +176,17 @@ impl<P: Pipe> ParserBuilder<P> for PythonParserBuilder<P> {
module: None,
class: None,
options: Vec::new(),
_marker: PhantomData
_marker: PhantomData,
}
}
fn option(&mut self, name: String, value: String) -> Result<(), Error> {
match name.borrow() {
options::MODULE => {
self.module = Some(value);
},
}
options::CLASS => {
self.class = Some(value);
},
}
_ => {
self.options.push((name, value));
}
Expand All @@ -180,11 +202,17 @@ impl<P: Pipe> ParserBuilder<P> for PythonParserBuilder<P> {

match PythonParserBuilder::<P>::load_and_init_class(py, &module_name, &class_name, &self.options) {
Ok(parser_instance) => {
debug!("Python parser successfully initialized, class='{}'", &class_name);
Ok(PythonParser {parser: parser_instance, _marker: PhantomData})
},
debug!("Python parser successfully initialized, class='{}'",
&class_name);
Ok(PythonParser {
parser: parser_instance,
_marker: PhantomData,
})
}
Err(error) => {
let err_msg = format!("Failed to create Python parser, class='{}', error='{:?}'", class_name, error);
let err_msg = format!("Failed to create Python parser, class='{}', error='{:?}'",
class_name,
error);
Err(Error::verbatim_error(err_msg))
}
}
Expand Down Expand Up @@ -215,11 +243,12 @@ impl<P: Pipe> Parser<P> for PythonParser<P> {
match self.call_parse(py, pylogmsg, input) {
Ok(result) => result,
Err(error) => {
error!("Failed to extract return value of parse() method: {:?}", error);
error!("Failed to extract return value of parse() method: {:?}",
error);
false
}
}
},
}
// I didn't find a way to test this case :-(
Err(error) => {
error!("Failed to create PyLogMessage: {:?}", error);
Expand Down
63 changes: 29 additions & 34 deletions python-parser/src/py_logmsg.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,38 @@
use syslog_ng_common::LogMessage;

use cpython::{Python, ToPyObject, NoArgs,PyObject, PyResult, PyString};
use cpython::rustobject::{TypeBuilder, PyRustObject};
use std::cell::RefCell;
use cpython::{Python, PyResult, PyErr};
use cpython::exc::UnicodeDecodeError;

fn getitem(py: Python, slf: &PyRustObject<LogMessage>, arg: &str) -> PyResult<PyString> {
if let Some(value) = slf.get(py).get(arg) {
let value = String::from_utf8_lossy(value);
Ok(PyString::new(py, &value))
} else {
Ok(PyString::new(py, ""))
}
}

fn setitem(py: Python, slf: &PyRustObject<LogMessage>, key: &str, value: &str) -> PyResult<NoArgs> {
let msg = slf.get_mut(py);
msg.insert(key, value.as_bytes());
Ok(NoArgs)
}

pub struct PyLogMessage(PyRustObject<LogMessage>);
py_class!(class PyLogMessage |py| {
data logmsg: RefCell<LogMessage>;

impl PyLogMessage {
pub fn new<'p>(py: Python<'p>, logmsg: LogMessage) -> PyResult<PyLogMessage> {
let mut b = TypeBuilder::<LogMessage>::new(py, "PyLogMessage");
b.add("__getitem__", py_method!(getitem(arg: &str)));
b.add("__setitem__", py_method!(setitem(key: &str, value: &str)));
trace!("Trying to finish construction PyLogMessage");
let built_type = try!(b.finish());
let instance = built_type.create_instance(py, logmsg, ());
Ok(PyLogMessage(instance))
def __getitem__(&self, key: String) -> PyResult<String> {
let key: &str = key.as_ref();
let logmsg = self.logmsg(py).borrow();
if let Some(value) = logmsg.get(key) {
match ::std::str::from_utf8(value) {
Ok(value) => Ok(value.to_string()),
Err(err) => {
let u = try!(UnicodeDecodeError::new_utf8(py, value, err));
Err(PyErr::from_instance(py, u))
}
}
} else {
Ok("".to_string())
}
}
}

impl ToPyObject for PyLogMessage {
type ObjectType = PyObject;
fn to_py_object(&self, py: Python) -> Self::ObjectType {
self.0.to_py_object(py)
def __setitem__(&self, key: String, value: String) -> PyResult<()> {
let key: &str = key.as_ref();
let mut msg = self.logmsg(py).borrow_mut();
msg.insert(key, value.as_bytes());
Ok(())
}
fn into_py_object(self, _py: Python) -> PyObject {
self.0.into_py_object(_py)
});

impl PyLogMessage {
pub fn new(py: Python, logmsg: LogMessage) -> PyResult<PyLogMessage> {
PyLogMessage::create_instance(py, RefCell::new(logmsg))
}
}
5 changes: 4 additions & 1 deletion python-parser/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use syslog_ng_common::{ParserBuilder, mock, GlobalConfig};
use PythonParser;
use PythonParserBuilder;

pub fn build_parser_with_options(module_name: &str, class_name: &str, options: &[(&str, &str)]) -> PythonParser<mock::MockPipe> {
pub fn build_parser_with_options(module_name: &str,
class_name: &str,
options: &[(&str, &str)])
-> PythonParser<mock::MockPipe> {
let cfg = GlobalConfig::new(0x0308);
let mut builder = PythonParserBuilder::new(cfg);
builder.option(::options::MODULE.to_owned(), module_name.to_owned()).ok().unwrap();
Expand Down
66 changes: 40 additions & 26 deletions python-parser/tests/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,25 @@ use std::sync::Mutex;
#[derive(Eq, PartialEq, Debug)]
struct SimplifiedLogRecord {
level: LogLevel,
formatted_message: String
formatted_message: String,
}

impl SimplifiedLogRecord {
pub fn new<S: Into<String>>(level: LogLevel, msg: S) -> SimplifiedLogRecord {
SimplifiedLogRecord {
level: level,
formatted_message: msg.into()
formatted_message: msg.into(),
}
}
}

struct MockLogger {
pub messages: Arc<Mutex<Vec<SimplifiedLogRecord>>>
pub messages: Arc<Mutex<Vec<SimplifiedLogRecord>>>,
}

impl MockLogger {
pub fn new() -> MockLogger {
MockLogger {
messages: Arc::new(Mutex::new(Vec::new()))
}
MockLogger { messages: Arc::new(Mutex::new(Vec::new())) }
}
}

Expand All @@ -61,40 +59,54 @@ fn init_logging(logger: MockLogger) -> Result<(), log::SetLoggerError> {
}

fn logging_callbacks_can_be_used_from_init_method(messages: Arc<Mutex<Vec<SimplifiedLogRecord>>>) {
let expected = [
SimplifiedLogRecord::new(LogLevel::Info, "INFO"),
SimplifiedLogRecord::new(LogLevel::Warn, "WARNING"),
SimplifiedLogRecord::new(LogLevel::Trace, "TRACE"),
SimplifiedLogRecord::new(LogLevel::Error, "ERROR"),
SimplifiedLogRecord::new(LogLevel::Debug, "DEBUG"),
];
let expected = [SimplifiedLogRecord::new(LogLevel::Info, "INFO"),
SimplifiedLogRecord::new(LogLevel::Warn, "WARNING"),
SimplifiedLogRecord::new(LogLevel::Trace, "TRACE"),
SimplifiedLogRecord::new(LogLevel::Error, "ERROR"),
SimplifiedLogRecord::new(LogLevel::Debug, "DEBUG")];
let cfg = GlobalConfig::new(0x0308);
let mut builder = PythonParserBuilder::<MockPipe>::new(cfg);
builder.option(options::MODULE.to_owned(), "_test_module".to_owned()).ok().unwrap();
builder.option(options::CLASS.to_owned(), "LoggingIsUsedInInitMethod".to_owned()).ok().unwrap();
builder.option(options::CLASS.to_owned(),
"LoggingIsUsedInInitMethod".to_owned())
.ok()
.unwrap();
let _ = builder.build();
let lock = messages.lock().unwrap();
for i in &expected {
assert!((*lock).contains(i), "This item wasn't found in the expected messages: {:?}", i);
assert!((*lock).contains(i),
"This item wasn't found in the expected messages: {:?}",
i);
}
}

fn logging_callbacks_are_not_overriden_if_they_are_already_defined(messages: Arc<Mutex<Vec<SimplifiedLogRecord>>>) {
let expected = [
SimplifiedLogRecord::new(LogLevel::Warn, "Already implemented info() function, omitting callback definition."),
SimplifiedLogRecord::new(LogLevel::Warn, "Already implemented warning() function, omitting callback definition."),
SimplifiedLogRecord::new(LogLevel::Warn, "Already implemented trace() function, omitting callback definition."),
SimplifiedLogRecord::new(LogLevel::Warn, "Already implemented error() function, omitting callback definition."),
SimplifiedLogRecord::new(LogLevel::Warn, "Already implemented debug() function, omitting callback definition."),
];
let expected = [SimplifiedLogRecord::new(LogLevel::Warn,
"Already implemented info() function, omitting callback definition."),
SimplifiedLogRecord::new(LogLevel::Warn,
"Already implemented warning() function, omitting callback definition."),
SimplifiedLogRecord::new(LogLevel::Warn,
"Already implemented trace() function, omitting callback definition."),
SimplifiedLogRecord::new(LogLevel::Warn,
"Already implemented error() function, omitting callback definition."),
SimplifiedLogRecord::new(LogLevel::Warn,
"Already implemented debug() function, omitting callback definition.")];
let cfg = GlobalConfig::new(0x0308);
let mut builder = PythonParserBuilder::<MockPipe>::new(cfg);
builder.option(options::MODULE.to_owned(), "_test_module.test_logging".to_owned()).ok().unwrap();
builder.option(options::CLASS.to_owned(), "LoggingCallbacksAreNotOverriden".to_owned()).ok().unwrap();
builder.option(options::MODULE.to_owned(),
"_test_module.test_logging".to_owned())
.ok()
.unwrap();
builder.option(options::CLASS.to_owned(),
"LoggingCallbacksAreNotOverriden".to_owned())
.ok()
.unwrap();
let _ = builder.build();
let lock = messages.lock().unwrap();
for i in &expected {
assert!((*lock).contains(i), "This item wasn't found in the expected messages: {:?}", i);
assert!((*lock).contains(i),
"This item wasn't found in the expected messages: {:?}",
i);
}
}

Expand All @@ -103,7 +115,9 @@ fn set_up() -> Arc<Mutex<Vec<SimplifiedLogRecord>>> {
let messages = logger.messages.clone();
let _ = init_logging(logger);
SYSLOG_NG_INITIALIZED.call_once(|| {
unsafe { syslog_ng_global_init(); }
unsafe {
syslog_ng_global_init();
}
});
env::set_var("PYTHONPATH", env::current_dir().unwrap());
messages
Expand Down
Loading