1 // Copyright (c) 2021 The Pybind Development Team. 2 // All rights reserved. Use of this source code is governed by a 3 // BSD-style license that can be found in the LICENSE file. 4 5 #pragma once 6 7 #include "../cast.h" 8 #include "../pybind11.h" 9 #include "../pytypes.h" 10 11 #include "../detail/common.h" 12 #include "../detail/descr.h" 13 14 #include <string> 15 16 #ifdef __has_include 17 # if defined(PYBIND11_CPP17) && __has_include(<filesystem>) && \ 18 PY_VERSION_HEX >= 0x03060000 19 # include <filesystem> 20 # define PYBIND11_HAS_FILESYSTEM 1 21 # endif 22 #endif 23 24 #if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL) 25 # error \ 26 "#include <filesystem> is not available. (Use -DPYBIND11_HAS_FILESYSTEM_IS_OPTIONAL to ignore.)" 27 #endif 28 29 PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 30 PYBIND11_NAMESPACE_BEGIN(detail) 31 32 #if defined(PYBIND11_HAS_FILESYSTEM) 33 template<typename T> struct path_caster { 34 35 private: unicode_from_fs_nativepath_caster36 static PyObject* unicode_from_fs_native(const std::string& w) { 37 #if !defined(PYPY_VERSION) 38 return PyUnicode_DecodeFSDefaultAndSize(w.c_str(), ssize_t(w.size())); 39 #else 40 // PyPy mistakenly declares the first parameter as non-const. 41 return PyUnicode_DecodeFSDefaultAndSize( 42 const_cast<char*>(w.c_str()), ssize_t(w.size())); 43 #endif 44 } 45 unicode_from_fs_nativepath_caster46 static PyObject* unicode_from_fs_native(const std::wstring& w) { 47 return PyUnicode_FromWideChar(w.c_str(), ssize_t(w.size())); 48 } 49 50 public: castpath_caster51 static handle cast(const T& path, return_value_policy, handle) { 52 if (auto py_str = unicode_from_fs_native(path.native())) { 53 return module_::import("pathlib").attr("Path")(reinterpret_steal<object>(py_str)) 54 .release(); 55 } 56 return nullptr; 57 } 58 loadpath_caster59 bool load(handle handle, bool) { 60 // PyUnicode_FSConverter and PyUnicode_FSDecoder normally take care of 61 // calling PyOS_FSPath themselves, but that's broken on PyPy (PyPy 62 // issue #3168) so we do it ourselves instead. 63 PyObject* buf = PyOS_FSPath(handle.ptr()); 64 if (!buf) { 65 PyErr_Clear(); 66 return false; 67 } 68 PyObject* native = nullptr; 69 if constexpr (std::is_same_v<typename T::value_type, char>) { 70 if (PyUnicode_FSConverter(buf, &native) != 0) { 71 if (auto c_str = PyBytes_AsString(native)) { 72 // AsString returns a pointer to the internal buffer, which 73 // must not be free'd. 74 value = c_str; 75 } 76 } 77 } else if constexpr (std::is_same_v<typename T::value_type, wchar_t>) { 78 if (PyUnicode_FSDecoder(buf, &native) != 0) { 79 if (auto c_str = PyUnicode_AsWideCharString(native, nullptr)) { 80 // AsWideCharString returns a new string that must be free'd. 81 value = c_str; // Copies the string. 82 PyMem_Free(c_str); 83 } 84 } 85 } 86 Py_XDECREF(native); 87 Py_DECREF(buf); 88 if (PyErr_Occurred()) { 89 PyErr_Clear(); 90 return false; 91 } 92 return true; 93 } 94 95 PYBIND11_TYPE_CASTER(T, const_name("os.PathLike")); 96 }; 97 98 template<> struct type_caster<std::filesystem::path> 99 : public path_caster<std::filesystem::path> {}; 100 #endif // PYBIND11_HAS_FILESYSTEM 101 102 PYBIND11_NAMESPACE_END(detail) 103 PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 104