diff --git a/pyosys/generator.py b/pyosys/generator.py index 65d886cf0..139a705f5 100644 --- a/pyosys/generator.py +++ b/pyosys/generator.py @@ -551,6 +551,13 @@ class PyosysWrapperGenerator(object): function, metadata.name, python_name_override ) + # HACK: Make ObjRange work until a proper one is implemented upstream + return_type = PyosysType.from_type(function.return_type) + if return_type.base == "ObjRange": + definition_args[1] = ( + f"[]({metadata.name} &s) {{ return s.{function.name.segments[-1].format()}().to_vector(); }}" + ) + print( f"\t\t\t.{definition_fn}({', '.join(definition_args)})", file=self.f, diff --git a/pyosys/ostream.h b/pyosys/ostream.h new file mode 100644 index 000000000..6b657264d --- /dev/null +++ b/pyosys/ostream.h @@ -0,0 +1,100 @@ +// ------------------------------------------------------- +// Written by Mohamed Gaber in 2025 +// ------------------------------------------------------- +// This header is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to +// ------------------------------------------------------- +// +// pybind11 bridging header from objects supporting write() and flush() +// to iostream, unbuffered +// +#include +#include + +namespace pybind11 { + +#ifdef PYTHON_OSTREAM_NO_PROPAGATE_EXC +#include + +#define PYTHON_OSTREAM_HANDLE_EXC(e, retval) { + fprintf(stderr, "%s\n", e.what()); \ + return retval; \ +} +#else +#define PYTHON_OSTREAM_HANDLE_EXC(e, retval) throw +#endif + +class python_ostream : public std::ostream, public std::basic_streambuf { +public: + using traits_type = std::char_traits; + + python_ostream(object writable): std::ostream(this), writable_(writable) { + #ifndef PYTHON_OSTREAM_NO_PROPAGATE_EXC + exceptions(std::ios::failbit | std::ios::badbit); + #endif + }; + + int sync() override { + gil_scoped_acquire ga; + try { + writable_.attr("flush")(); + return 0; + } catch (error_already_set &e) { + PYTHON_OSTREAM_HANDLE_EXC(e, -1); + } + } + + std::streamsize xsputn(const char *s, std::streamsize count) override { + gil_scoped_acquire ga; + try { + auto result = writable_.attr("write")(str(s, count)); + return cast(result); + } catch (error_already_set &e) { + PYTHON_OSTREAM_HANDLE_EXC(e, 0); + } + } + + int overflow(int ch = traits_type::eof()) override { + if (ch == traits_type::eof()) { + return traits_type::eof(); + } + try { + gil_scoped_acquire ga; + char target = ch; + auto written = cast(writable_.attr("write")(str(&target, 1))); + if (written == 0) { // can only be 0 or 1 + return traits_type::eof(); + } + } catch (error_already_set &e) { + PYTHON_OSTREAM_HANDLE_EXC(e, traits_type::eof()); + } + return ch; + } +private: + object writable_; // keep reference while this object is alive +}; + +#undef PYTHON_OSTREAM_EXC +} // namespace pybind11 diff --git a/pyosys/wrappers_tpl.cc b/pyosys/wrappers_tpl.cc index aa257e1b6..34ac22b09 100644 --- a/pyosys/wrappers_tpl.cc +++ b/pyosys/wrappers_tpl.cc @@ -28,6 +28,7 @@ #include "kernel/yosys_common.h" #include "pyosys/hashlib.h" +#include "pyosys/ostream.h" namespace py = pybind11; @@ -165,6 +166,14 @@ namespace pyosys { } }; + // not upstreamed bec it leaks memory + void log_to_stream(py::object o) + { + auto output = new py::python_ostream(o); + Yosys::log_streams.clear(); + Yosys::log_streams.push_back(output); + } + PYBIND11_MODULE(libyosys, m) { // this code is run on import m.doc() = "python access to libyosys"; @@ -189,6 +198,7 @@ namespace pyosys { m.def("log_file_warning", [](std::string_view file, int line, std::string s) { log_formatted_file_warning(file, line, s); }); m.def("log_error", [](std::string s) { log_formatted_error(s); }); m.def("log_file_error", [](std::string_view file, int line, std::string s) { log_formatted_file_error(file, line, s); }); + m.def("log_to_stream", &log_to_stream); // Namespace to host global objects auto global_variables = py::class_(m, "Globals");