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 nb = nanobind;
namespace PythonException {
// Check if there's a Python exception pending in the interpreter.
inline bool isPending() {
nb::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() {
nb::gil_scoped_acquire acquire;
if (PyErr_Occurred()) {
nb::python_error existingError;
throw existingError;
}
}
}; // namespace PythonException
/**
* A base class for file-like Python object wrappers.
*/
class PythonFileLike {
public:
PythonFileLike(nb::object fileLike) : fileLike(fileLike) {}
std::string getRepresentation() {
nb::gil_scoped_acquire acquire;
return nb::cast<std::string>(nb::repr(fileLike));
}
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.
nb::gil_scoped_acquire acquire;
if (nb::hasattr(fileLike, "name")) {
return nb::cast<std::string>(nb::str(nb::handle(fileLike.attr("name"))));
} else {
return {};
}
}
bool isSeekable() {
nb::gil_scoped_acquire acquire;
return nb::cast<bool>(fileLike.attr("seekable")());
}
long long getPosition() {
nb::gil_scoped_acquire acquire;
return nb::cast<long long>(fileLike.attr("tell")());
}
bool setPosition(long long pos) {
nb::gil_scoped_acquire acquire;
if (nb::cast<bool>(fileLike.attr("seekable")())) {
fileLike.attr("seek")(pos);
}
return nb::cast<long long>(fileLike.attr("tell")()) == pos;
}
nb::object getFileLikeObject() { return fileLike; }
protected:
nb::object fileLike;
};