File indexing completed on 2025-02-16 05:12:14

0001 // Copyright (c) 2021 The Pybind Development Team.
0002 // All rights reserved. Use of this source code is governed by a
0003 // BSD-style license that can be found in the LICENSE file.
0004 
0005 #pragma once
0006 
0007 #include "../cast.h"
0008 #include "../pybind11.h"
0009 #include "../pytypes.h"
0010 
0011 #include "../detail/common.h"
0012 #include "../detail/descr.h"
0013 
0014 #include <string>
0015 
0016 #ifdef __has_include
0017 #  if defined(PYBIND11_CPP17) && __has_include(<filesystem>) && \
0018       PY_VERSION_HEX >= 0x03060000
0019 #    include <filesystem>
0020 #    define PYBIND11_HAS_FILESYSTEM 1
0021 #  endif
0022 #endif
0023 
0024 #if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL)
0025 #    error                                                                                        \
0026         "#include <filesystem> is not available. (Use -DPYBIND11_HAS_FILESYSTEM_IS_OPTIONAL to ignore.)"
0027 #endif
0028 
0029 PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
0030 PYBIND11_NAMESPACE_BEGIN(detail)
0031 
0032 #if defined(PYBIND11_HAS_FILESYSTEM)
0033 template<typename T> struct path_caster {
0034 
0035 private:
0036     static PyObject* unicode_from_fs_native(const std::string& w) {
0037 #if !defined(PYPY_VERSION)
0038         return PyUnicode_DecodeFSDefaultAndSize(w.c_str(), ssize_t(w.size()));
0039 #else
0040         // PyPy mistakenly declares the first parameter as non-const.
0041         return PyUnicode_DecodeFSDefaultAndSize(
0042             const_cast<char*>(w.c_str()), ssize_t(w.size()));
0043 #endif
0044     }
0045 
0046     static PyObject* unicode_from_fs_native(const std::wstring& w) {
0047         return PyUnicode_FromWideChar(w.c_str(), ssize_t(w.size()));
0048     }
0049 
0050 public:
0051     static handle cast(const T& path, return_value_policy, handle) {
0052         if (auto py_str = unicode_from_fs_native(path.native())) {
0053             return module_::import("pathlib").attr("Path")(reinterpret_steal<object>(py_str))
0054                    .release();
0055         }
0056         return nullptr;
0057     }
0058 
0059     bool load(handle handle, bool) {
0060         // PyUnicode_FSConverter and PyUnicode_FSDecoder normally take care of
0061         // calling PyOS_FSPath themselves, but that's broken on PyPy (PyPy
0062         // issue #3168) so we do it ourselves instead.
0063         PyObject* buf = PyOS_FSPath(handle.ptr());
0064         if (!buf) {
0065             PyErr_Clear();
0066             return false;
0067         }
0068         PyObject* native = nullptr;
0069         if constexpr (std::is_same_v<typename T::value_type, char>) {
0070             if (PyUnicode_FSConverter(buf, &native) != 0) {
0071                 if (auto c_str = PyBytes_AsString(native)) {
0072                     // AsString returns a pointer to the internal buffer, which
0073                     // must not be free'd.
0074                     value = c_str;
0075                 }
0076             }
0077         } else if constexpr (std::is_same_v<typename T::value_type, wchar_t>) {
0078             if (PyUnicode_FSDecoder(buf, &native) != 0) {
0079                 if (auto c_str = PyUnicode_AsWideCharString(native, nullptr)) {
0080                     // AsWideCharString returns a new string that must be free'd.
0081                     value = c_str;  // Copies the string.
0082                     PyMem_Free(c_str);
0083                 }
0084             }
0085         }
0086         Py_XDECREF(native);
0087         Py_DECREF(buf);
0088         if (PyErr_Occurred()) {
0089             PyErr_Clear();
0090             return false;
0091         }
0092         return true;
0093     }
0094 
0095     PYBIND11_TYPE_CASTER(T, const_name("os.PathLike"));
0096 };
0097 
0098 template<> struct type_caster<std::filesystem::path>
0099     : public path_caster<std::filesystem::path> {};
0100 #endif // PYBIND11_HAS_FILESYSTEM
0101 
0102 PYBIND11_NAMESPACE_END(detail)
0103 PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)