// Copyright (c) 2021 The Pybind Development Team. // All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #pragma once #include #include #include #include #include #include #if defined(PYBIND11_HAS_FILESYSTEM) # include #elif defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM) # include #else # error "Neither #include nor #include (__VA_ARGS__)) #endif #if defined(PYBIND11_HAS_FILESYSTEM) || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM) template struct path_caster { private: static PyObject *unicode_from_fs_native(const std::string &w) { # if !defined(PYPY_VERSION) return PyUnicode_DecodeFSDefaultAndSize(w.c_str(), ssize_t(w.size())); # else // PyPy mistakenly declares the first parameter as non-const. return PyUnicode_DecodeFSDefaultAndSize(const_cast(w.c_str()), ssize_t(w.size())); # endif } static PyObject *unicode_from_fs_native(const std::wstring &w) { return PyUnicode_FromWideChar(w.c_str(), ssize_t(w.size())); } public: static handle cast(const T &path, return_value_policy, handle) { if (auto py_str = unicode_from_fs_native(path.native())) { return module_::import("pathlib") .attr("Path")(reinterpret_steal(py_str)) .release(); } return nullptr; } bool load(handle handle, bool) { // PyUnicode_FSConverter and PyUnicode_FSDecoder normally take care of // calling PyOS_FSPath themselves, but that's broken on PyPy (PyPy // issue #3168) so we do it ourselves instead. PyObject *buf = PyOS_FSPath(handle.ptr()); if (!buf) { PyErr_Clear(); return false; } PyObject *native = nullptr; if constexpr (std::is_same_v) { if (PyUnicode_FSConverter(buf, PYBIND11_REINTERPRET_CAST_VOID_PTR_IF_NOT_PYPY(&native)) != 0) { if (auto *c_str = PyBytes_AsString(native)) { // AsString returns a pointer to the internal buffer, which // must not be free'd. value = c_str; } } } else if constexpr (std::is_same_v) { if (PyUnicode_FSDecoder(buf, PYBIND11_REINTERPRET_CAST_VOID_PTR_IF_NOT_PYPY(&native)) != 0) { if (auto *c_str = PyUnicode_AsWideCharString(native, nullptr)) { // AsWideCharString returns a new string that must be free'd. value = c_str; // Copies the string. PyMem_Free(c_str); } } } Py_XDECREF(native); Py_DECREF(buf); if (PyErr_Occurred()) { PyErr_Clear(); return false; } return true; } PYBIND11_TYPE_CASTER(T, io_name("os.PathLike | str | bytes", "pathlib.Path")); }; #endif // PYBIND11_HAS_FILESYSTEM || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM) #if defined(PYBIND11_HAS_FILESYSTEM) template <> struct type_caster : public path_caster {}; #elif defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM) template <> struct type_caster : public path_caster {}; #endif PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)