mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-30 16:58:40 -04:00
Implement Python 3.6 fspath protocol; allow passing a pathlib.Path wherever Filename is expected
The Python 3.6 fspath protocol allows passing Filename objects into any Python standard library calls that take a path.
This commit is contained in:
parent
3fa5b6b4ee
commit
e778c529b2
@ -38,7 +38,6 @@ Filename(const char *filename) {
|
||||
(*this) = filename;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ -84,6 +83,20 @@ Filename(Filename &&from) NOEXCEPT :
|
||||
}
|
||||
#endif // USE_MOVE_SEMANTICS
|
||||
|
||||
/**
|
||||
* Creates an empty Filename.
|
||||
*/
|
||||
INLINE Filename::
|
||||
Filename() :
|
||||
_dirname_end(0),
|
||||
_basename_start(0),
|
||||
_basename_end(string::npos),
|
||||
_extension_start(string::npos),
|
||||
_hash_start(string::npos),
|
||||
_hash_end(string::npos),
|
||||
_flags(0) {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ -155,14 +168,6 @@ pattern_filename(const string &filename) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE Filename::
|
||||
~Filename() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ -233,7 +238,7 @@ operator = (string &&filename) NOEXCEPT {
|
||||
*/
|
||||
INLINE Filename &Filename::
|
||||
operator = (Filename &&from) NOEXCEPT {
|
||||
_filename = MOVE(from._filename);
|
||||
_filename = move(from._filename);
|
||||
_dirname_end = from._dirname_end;
|
||||
_basename_start = from._basename_start;
|
||||
_basename_end = from._basename_end;
|
||||
|
@ -55,20 +55,22 @@ public:
|
||||
};
|
||||
|
||||
INLINE Filename(const char *filename);
|
||||
|
||||
PUBLISHED:
|
||||
INLINE Filename(const string &filename = "");
|
||||
INLINE Filename(const string &filename);
|
||||
INLINE Filename(const wstring &filename);
|
||||
INLINE Filename(const Filename ©);
|
||||
Filename(const Filename &dirname, const Filename &basename);
|
||||
INLINE ~Filename();
|
||||
|
||||
#ifdef USE_MOVE_SEMANTICS
|
||||
INLINE Filename(string &&filename) NOEXCEPT;
|
||||
INLINE Filename(Filename &&from) NOEXCEPT;
|
||||
#endif
|
||||
|
||||
PUBLISHED:
|
||||
INLINE Filename();
|
||||
Filename(const Filename &dirname, const Filename &basename);
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
EXTENSION(Filename(PyObject *path));
|
||||
|
||||
EXTENSION(PyObject *__reduce__(PyObject *self) const);
|
||||
#endif
|
||||
|
||||
@ -118,6 +120,7 @@ PUBLISHED:
|
||||
INLINE char operator [] (size_t n) const;
|
||||
|
||||
EXTENSION(PyObject *__repr__() const);
|
||||
EXTENSION(PyObject *__fspath__() const);
|
||||
|
||||
INLINE string substr(size_t begin) const;
|
||||
INLINE string substr(size_t begin, size_t end) const;
|
||||
|
@ -14,6 +14,115 @@
|
||||
#include "filename_ext.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
#ifndef CPPPARSER
|
||||
extern Dtool_PyTypedObject Dtool_Filename;
|
||||
#endif // CPPPARSER
|
||||
|
||||
/**
|
||||
* Constructs a Filename object from a str, bytes object, or os.PathLike.
|
||||
*/
|
||||
void Extension<Filename>::
|
||||
__init__(PyObject *path) {
|
||||
nassertv(path != NULL);
|
||||
nassertv(_this != NULL);
|
||||
|
||||
Py_ssize_t length;
|
||||
|
||||
if (PyUnicode_CheckExact(path)) {
|
||||
wchar_t *data;
|
||||
#if PY_VERSION_HEX >= 0x03020000
|
||||
data = PyUnicode_AsWideCharString(path, &length);
|
||||
#else
|
||||
length = PyUnicode_GET_SIZE(path);
|
||||
data = (wchar_t *)alloca(sizeof(wchar_t) * (length + 1));
|
||||
PyUnicode_AsWideChar((PyUnicodeObject *)path, data, length);
|
||||
#endif
|
||||
(*_this) = wstring(data, length);
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03020000
|
||||
PyMem_Free(data);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (PyBytes_CheckExact(path)) {
|
||||
char *data;
|
||||
PyBytes_AsStringAndSize(path, &data, &length);
|
||||
(*_this) = string(data, length);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Py_TYPE(path) == &Dtool_Filename._PyType) {
|
||||
// Copy constructor.
|
||||
(*_this) = *((Filename *)((Dtool_PyInstDef *)path)->_ptr_to_object);
|
||||
return;
|
||||
}
|
||||
|
||||
PyObject *path_str;
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03060000
|
||||
// It must be an os.PathLike object. Check for an __fspath__ method.
|
||||
PyObject *fspath = PyObject_GetAttrString((PyObject *)Py_TYPE(path), "__fspath__");
|
||||
if (fspath == NULL) {
|
||||
PyErr_Format(PyExc_TypeError, "expected str, bytes or os.PathLike object, not %s", Py_TYPE(path)->tp_name);
|
||||
return;
|
||||
}
|
||||
|
||||
path_str = PyObject_CallFunctionObjArgs(fspath, path, NULL);
|
||||
Py_DECREF(fspath);
|
||||
#else
|
||||
// There is no standard path protocol before Python 3.6, but let's try and
|
||||
// support taking pathlib paths anyway. We don't version check this to
|
||||
// allow people to use backports of the pathlib module.
|
||||
if (PyObject_HasAttrString(path, "_format_parsed_parts")) {
|
||||
path_str = PyObject_Str(path);
|
||||
} else {
|
||||
#if PY_VERSION_HEX >= 0x03040000
|
||||
PyErr_Format(PyExc_TypeError, "expected str, bytes, Path or Filename object, not %s", Py_TYPE(path)->tp_name);
|
||||
#elif PY_MAJOR_VERSION >= 3
|
||||
PyErr_Format(PyExc_TypeError, "expected str, bytes or Filename object, not %s", Py_TYPE(path)->tp_name);
|
||||
#else
|
||||
PyErr_Format(PyExc_TypeError, "expected str or unicode object, not %s", Py_TYPE(path)->tp_name);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (path_str == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (PyUnicode_CheckExact(path_str)) {
|
||||
wchar_t *data;
|
||||
#if PY_VERSION_HEX >= 0x03020000
|
||||
data = PyUnicode_AsWideCharString(path_str, &length);
|
||||
#else
|
||||
length = PyUnicode_GET_SIZE(path_str);
|
||||
data = (wchar_t *)alloca(sizeof(wchar_t) * (length + 1));
|
||||
PyUnicode_AsWideChar((PyUnicodeObject *)path_str, data, length);
|
||||
#endif
|
||||
(*_this) = Filename::from_os_specific_w(wstring(data, length));
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03020000
|
||||
PyMem_Free(data);
|
||||
#endif
|
||||
|
||||
} else if (PyBytes_CheckExact(path_str)) {
|
||||
char *data;
|
||||
PyBytes_AsStringAndSize(path_str, &data, &length);
|
||||
(*_this) = Filename::from_os_specific(string(data, length));
|
||||
|
||||
} else {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyErr_Format(PyExc_TypeError, "expected str or bytes object, not %s", Py_TYPE(path_str)->tp_name);
|
||||
#else
|
||||
PyErr_Format(PyExc_TypeError, "expected str or unicode object, not %s", Py_TYPE(path_str)->tp_name);
|
||||
#endif
|
||||
}
|
||||
Py_DECREF(path_str);
|
||||
}
|
||||
|
||||
/**
|
||||
* This special Python method is implement to provide support for the pickle
|
||||
* module.
|
||||
@ -62,6 +171,16 @@ __repr__() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows a Filename object to be passed to any Python function that accepts
|
||||
* an os.PathLike object.
|
||||
*/
|
||||
PyObject *Extension<Filename>::
|
||||
__fspath__() const {
|
||||
wstring filename = _this->to_os_specific_w();
|
||||
return PyUnicode_FromWideChar(filename.data(), (Py_ssize_t)filename.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* This variant on scan_directory returns a Python list of strings on success,
|
||||
* or None on failure.
|
||||
|
@ -29,8 +29,11 @@
|
||||
template<>
|
||||
class Extension<Filename> : public ExtensionBase<Filename> {
|
||||
public:
|
||||
void __init__(PyObject *path);
|
||||
|
||||
PyObject *__reduce__(PyObject *self) const;
|
||||
PyObject *__repr__() const;
|
||||
PyObject *__fspath__() const;
|
||||
PyObject *scan_directory() const;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user