python/src/PythonFileLike.h (52 lines of code) (raw):
// Copyright 2022-2023 Spotify AB
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <mutex>
#include <optional>
#include <string>
namespace py = pybind11;
namespace PythonException {
// Check if there's a Python exception pending in the interpreter.
inline bool isPending() {
py::gil_scoped_acquire acquire;
return PyErr_Occurred() != nullptr;
}
// If an exception is pending, raise it as a C++ exception to break the current
// control flow and result in an error being thrown in Python later.
inline void raise() {
py::gil_scoped_acquire acquire;
if (PyErr_Occurred()) {
py::error_already_set existingError;
throw existingError;
}
}
}; // namespace PythonException
/**
* A base class for file-like Python object wrappers.
*/
class PythonFileLike {
public:
PythonFileLike(py::object fileLike) : fileLike(fileLike) {}
std::string getRepresentation() {
py::gil_scoped_acquire acquire;
return py::repr(fileLike).cast<std::string>();
}
std::optional<std::string> getFilename() {
// Some Python file-like objects expose a ".name" property.
// If this object has that property, return its value;
// otherwise return an empty optional.
py::gil_scoped_acquire acquire;
if (py::hasattr(fileLike, "name")) {
return py::str(fileLike.attr("name")).cast<std::string>();
} else {
return {};
}
}
bool isSeekable() {
py::gil_scoped_acquire acquire;
return fileLike.attr("seekable")().cast<bool>();
}
long long getPosition() {
py::gil_scoped_acquire acquire;
return fileLike.attr("tell")().cast<long long>();
}
bool setPosition(long long pos) {
py::gil_scoped_acquire acquire;
if (fileLike.attr("seekable")().cast<bool>()) {
fileLike.attr("seek")(pos);
}
return fileLike.attr("tell")().cast<long long>() == pos;
}
py::object getFileLikeObject() { return fileLike; }
protected:
py::object fileLike;
};