From 2176e1513b6e81fc7643984128fa97b8fa4ab37d Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Thu, 10 Feb 2022 16:06:30 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=8D=20Be=20more=20lenient=20when=20acc?= =?UTF-8?q?epting=20args=20to=20file=20I/O=20(#1819)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PyArbor I/O routines now try to cast `filename` arguments to a string. This allows passing `pathlib.Path` objects. Example ```py here = Path(__file__).parent cat = A.load_catalogue(here / 'local-catalogue.so') ``` --- python/cable_cell_io.cpp | 38 +++++++++++++++++++++----------------- python/mechanism.cpp | 3 ++- python/morphology.cpp | 16 ++++++++++------ python/util.hpp | 27 +++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 24 deletions(-) create mode 100644 python/util.hpp diff --git a/python/cable_cell_io.cpp b/python/cable_cell_io.cpp index b5c1272c..2ff3ac8b 100644 --- a/python/cable_cell_io.cpp +++ b/python/cable_cell_io.cpp @@ -9,14 +9,18 @@ #include <arborio/cableio.hpp> #include "error.hpp" +#include "util.hpp" #include "strprintf.hpp" namespace pyarb { -arborio::cable_cell_component load_component(const std::string& fname) { +namespace py = pybind11; + +arborio::cable_cell_component load_component(py::object fn) { + const auto fname = util::to_path(fn); std::ifstream fid{fname}; if (!fid.good()) { - throw pyarb_error("Can't open file '{}'" + fname); + throw arb::file_not_found_error(fname); } auto component = arborio::parse_component(fid); if (!component) { @@ -26,13 +30,13 @@ arborio::cable_cell_component load_component(const std::string& fname) { }; template<typename T> -void write_component(const T& component, const std::string& fname) { - std::ofstream fid(fname); +void write_component(const T& component, py::object fn) { + std::ofstream fid(util::to_path(fn)); arborio::write_component(fid, component, arborio::meta_data{}); } -void write_component(const arborio::cable_cell_component& component, const std::string& fname) { - std::ofstream fid(fname); +void write_component(const arborio::cable_cell_component& component, py::object fn) { + std::ofstream fid(util::to_path(fn)); arborio::write_component(fid, component); } @@ -43,40 +47,40 @@ void register_cable_loader(pybind11::module& m) { "Load arbor-component (decor, morphology, label_dict, cable_cell) from file."); m.def("write_component", - [](const arborio::cable_cell_component& d, const std::string& fname) { - return write_component(d, fname); + [](const arborio::cable_cell_component& d, py::object fn) { + return write_component(d, fn); }, pybind11::arg_v("object", "the cable_component object."), - pybind11::arg_v("filename", "the name of the file."), + pybind11::arg_v("filename", "the path of the file."), "Write cable_component to file."); m.def("write_component", - [](const arb::decor& d, const std::string& fname) { - return write_component<arb::decor>(d, fname); + [](const arb::decor& d, py::object fn) { + return write_component<arb::decor>(d, fn); }, pybind11::arg_v("object", "the decor object."), pybind11::arg_v("filename", "the name of the file."), "Write decor to file."); m.def("write_component", - [](const arb::label_dict& d, const std::string& fname) { - return write_component<arb::label_dict>(d, fname); + [](const arb::label_dict& d, py::object fn) { + return write_component<arb::label_dict>(d, fn); }, pybind11::arg_v("object", "the label_dict object."), pybind11::arg_v("filename", "the name of the file."), "Write label_dict to file."); m.def("write_component", - [](const arb::morphology& d, const std::string& fname) { - return write_component<arb::morphology>(d, fname); + [](const arb::morphology& d, py::object fn) { + return write_component<arb::morphology>(d, fn); }, pybind11::arg_v("object", "the morphology object."), pybind11::arg_v("filename", "the name of the file."), "Write morphology to file."); m.def("write_component", - [](const arb::cable_cell& d, const std::string& fname) { - return write_component<arb::cable_cell>(d, fname); + [](const arb::cable_cell& d, py::object fn) { + return write_component<arb::cable_cell>(d, fn); }, pybind11::arg_v("object", "the cable_cell object."), pybind11::arg_v("filename", "the name of the file."), diff --git a/python/mechanism.cpp b/python/mechanism.cpp index a774cad4..80db1030 100644 --- a/python/mechanism.cpp +++ b/python/mechanism.cpp @@ -11,6 +11,7 @@ #include "arbor/mechinfo.hpp" +#include "util.hpp" #include "conversion.hpp" #include "strprintf.hpp" @@ -197,7 +198,7 @@ void register_mechanisms(pybind11::module& m) { m.def("default_catalogue", [](){return arb::global_default_catalogue();}); m.def("allen_catalogue", [](){return arb::global_allen_catalogue();}); m.def("bbp_catalogue", [](){return arb::global_bbp_catalogue();}); - m.def("load_catalogue", [](const std::string& fn){return arb::load_catalogue(fn);}); + m.def("load_catalogue", [](pybind11::object fn) { return arb::load_catalogue(util::to_string(fn)); }); // arb::mechanism_desc // For specifying a mechanism in the cable_cell interface. diff --git a/python/morphology.cpp b/python/morphology.cpp index a5d3fb0d..8f4293ca 100644 --- a/python/morphology.cpp +++ b/python/morphology.cpp @@ -20,6 +20,7 @@ #include <arborio/neuroml.hpp> #endif +#include "util.hpp" #include "error.hpp" #include "proxy.hpp" #include "strprintf.hpp" @@ -250,10 +251,11 @@ void register_morphology(py::module& m) { // Function that creates a morphology from an swc file. // Wraps calls to C++ functions arborio::parse_swc() and arborio::load_swc_arbor(). m.def("load_swc_arbor", - [](std::string fname) { + [](py::object fn) { + const auto fname = util::to_path(fn); std::ifstream fid{fname}; if (!fid.good()) { - throw pyarb_error(util::pprintf("can't open file '{}'", fname)); + throw arb::file_not_found_error(fname); } try { auto data = arborio::parse_swc(fid); @@ -275,10 +277,11 @@ void register_morphology(py::module& m) { " are no gaps in the resulting morphology."); m.def("load_swc_neuron", - [](std::string fname) { + [](py::object fn) { + const auto fname = util::to_path(fn); std::ifstream fid{fname}; if (!fid.good()) { - throw pyarb_error(util::pprintf("can't open file '{}'", fname)); + throw arb::file_not_found_error(fname); } try { auto data = arborio::parse_swc(fid); @@ -383,10 +386,11 @@ void register_morphology(py::module& m) { neuroml // constructors .def(py::init( - [](std::string fname) { + [](py::object fn) { + const auto fname = util::to_path(fn); std::ifstream fid{fname}; if (!fid.good()) { - throw pyarb_error(util::pprintf("can't open file '{}'", fname)); + throw arb::file_not_found_error(fname); } try { std::string string_data((std::istreambuf_iterator<char>(fid)), diff --git a/python/util.hpp b/python/util.hpp new file mode 100644 index 00000000..fe1b6b07 --- /dev/null +++ b/python/util.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include <pybind11/pybind11.h> + +#include "strprintf.hpp" + +namespace pyarb { +namespace util { + +namespace py = pybind11; + +inline +std::string to_path(py::object fn) { + if (py::isinstance<py::str>(fn)) { + return std::string{py::str(fn)}; + } + else if (py::isinstance(fn, + py::module_::import("pathlib").attr("Path"))) { + return std::string{py::str(fn)}; + } + throw std::runtime_error( + util::strprintf("Cannot convert objects of type '{}' to a path-like.", + std::string{py::str(fn.get_type())})); +} + +} +} -- GitLab