diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 690bf97..8b42f08 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,28 +11,34 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12-dev"] - os: [ubuntu-20.04] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + os: [ubuntu-24.04] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install Linux dependencies run: | - sudo apt-get install -y libattr1-dev - sudo apt-get install -y pkg-config - sudo apt-get install -y gcc - sudo apt-get install -y libfuse-dev + sudo apt-get update + sudo apt-get install -y libattr1-dev libfuse-dev + sudo apt-get install -y pkg-config gcc + + - name: Install Python dependencies (misc) + run: pip install setuptools pytest sphinx - name: Install Python dependencies - run: | - test/ci-install.sh + run: pip install "Cython>=3" - name: Test run: | - test/ci-test.sh + set -e + python setup.py build_cython + python setup.py build_ext --inplace + pytest -v -rs test/ + + sphinx-build -b html rst doc/html diff --git a/.gitignore b/.gitignore index dccc4b3..1ade135 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ doc/doctrees *.pyc src/llfuse.c src/llfuse*.so +.idea +.pytest_cache diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..d725739 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,28 @@ +# .readthedocs.yaml - Read the Docs configuration file. +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details. + +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.11" + jobs: + post_checkout: + - git fetch --unshallow + pre_install: + - pip install -r requirements.d/rtd.txt + - python setup.py build_cython + - python setup.py build_ext --inplace + apt_packages: + - build-essential + - pkg-config + - libfuse-dev + +python: + install: + - method: pip + path: . + +sphinx: + configuration: rst/conf.py diff --git a/Changes.rst b/Changes.rst index 5678697..39bfb8d 100644 --- a/Changes.rst +++ b/Changes.rst @@ -6,6 +6,59 @@ **WARNING**: Python-LLFUSE is no longer actively developed. +Release 1.6.0 (not yet released) +================================ + +- Drop Python 3.8 and 3.9 support. +- Drop testing on Cython 0.29.x + + +Release 1.5.2 (2025-12-22) +========================== + +- Support and test on Python 3.14 also. +- CI: test on Ubuntu 24.04 +- Cythonized using Cython 3.2.3. +- setup.py: + + - use SPDX license metadata (the old style was deprecated), + also require setuptools >= 78.1.1, #104 + - remove tests_require (not supported anymore) +- get rid of sphinx build warnings, #56 +- README: link to mfusepy project + + +Release 1.5.1 (2024-08-31) +========================== + +- Support and test on Python 3.13 also. +- Cythonized using Cython 3.0.11 to get Python 3.13 support. +- Misc. CI and readthedocs related changes. + + +Release 1.5.0 (2023-08-08) +========================== + +- Note: this is first pyfuse3 release supporting the Cython 3.0.0 release. +- Cythonized using Cython 3.0.0 release. +- Drop Python 3.5, 3.6, 3.7 support, see #69. + Minimum requirement is Python 3.8 now. +- Get rid of PyEval_InitThreads, #55. +- CI: also test on python 3.12 / cython 3.0 release +- Tell Cython that callbacks may raise exceptions, #90. +- Misc. CI, testing, build related fixes/improvements. + + +Release 1.4.4 (2023-05-21) +========================== + +- CI: use the matrix for cy/py combinations, support and test on Cython 3 beta +- cy3: cdef void* f(void* d) noexcept with gil, #78 +- cy3: cdef nogil -> noexcept nogil +- tests: use shutil.which() instead of which(1) executable +- tests/examples: fix tmpfs: backport fix from pyfuse3 +- tests/examples: fix lltest: add statfs implementation, remove -l from umount + Release 1.4.3 (2023-05-09) ========================== diff --git a/Include/fuse_lowlevel.pxd b/Include/fuse_lowlevel.pxd index 63ffcbe..ea23ee0 100644 --- a/Include/fuse_lowlevel.pxd +++ b/Include/fuse_lowlevel.pxd @@ -54,66 +54,69 @@ cdef extern from "" nogil: int FUSE_SET_ATTR_ATIME_NOW int FUSE_SET_ATTR_MTIME_NOW + # Request handlers + # We allow these functions to raise exceptions because we will catch them + # when checking exception status on return from fuse_session_process_buf(). struct fuse_lowlevel_ops: - void (*init) (void *userdata, fuse_conn_info *conn) - void (*destroy) (void *userdata) - void (*lookup) (fuse_req_t req, fuse_ino_t parent, const_char *name) - void (*forget) (fuse_req_t req, fuse_ino_t ino, ulong_t nlookup) + void (*init) (void *userdata, fuse_conn_info *conn) except * + void (*destroy) (void *userdata) except * + void (*lookup) (fuse_req_t req, fuse_ino_t parent, const_char *name) except * + void (*forget) (fuse_req_t req, fuse_ino_t ino, ulong_t nlookup) except * void (*getattr) (fuse_req_t req, fuse_ino_t ino, - fuse_file_info *fi) + fuse_file_info *fi) except * void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct_stat *attr, - int to_set, fuse_file_info *fi) - void (*readlink) (fuse_req_t req, fuse_ino_t ino) + int to_set, fuse_file_info *fi) except * + void (*readlink) (fuse_req_t req, fuse_ino_t ino) except * void (*mknod) (fuse_req_t req, fuse_ino_t parent, const_char *name, - mode_t mode, dev_t rdev) + mode_t mode, dev_t rdev) except * void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const_char *name, - mode_t mode) - void (*unlink) (fuse_req_t req, fuse_ino_t parent, const_char *name) - void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const_char *name) + mode_t mode) except * + void (*unlink) (fuse_req_t req, fuse_ino_t parent, const_char *name) except * + void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const_char *name) except * void (*symlink) (fuse_req_t req, const_char *link, fuse_ino_t parent, - const_char *name) + const_char *name) except * void (*rename) (fuse_req_t req, fuse_ino_t parent, const_char *name, - fuse_ino_t newparent, const_char *newname) + fuse_ino_t newparent, const_char *newname) except * void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, - const_char *newname) + const_char *newname) except * void (*open) (fuse_req_t req, fuse_ino_t ino, - fuse_file_info *fi) + fuse_file_info *fi) except * void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, - fuse_file_info *fi) + fuse_file_info *fi) except * void (*write) (fuse_req_t req, fuse_ino_t ino, const_char *buf, - size_t size, off_t off, fuse_file_info *fi) + size_t size, off_t off, fuse_file_info *fi) except * void (*flush) (fuse_req_t req, fuse_ino_t ino, - fuse_file_info *fi) + fuse_file_info *fi) except * void (*release) (fuse_req_t req, fuse_ino_t ino, - fuse_file_info *fi) + fuse_file_info *fi) except * void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync, - fuse_file_info *fi) + fuse_file_info *fi) except * void (*opendir) (fuse_req_t req, fuse_ino_t ino, - fuse_file_info *fi) + fuse_file_info *fi) except * void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, - fuse_file_info *fi) + fuse_file_info *fi) except * void (*releasedir) (fuse_req_t req, fuse_ino_t ino, - fuse_file_info *fi) + fuse_file_info *fi) except * void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync, - fuse_file_info *fi) - void (*statfs) (fuse_req_t req, fuse_ino_t ino) + fuse_file_info *fi) except * + void (*statfs) (fuse_req_t req, fuse_ino_t ino) except * void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const_char *name, - const_char *value, size_t size, int flags) + const_char *value, size_t size, int flags) except * void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const_char *name, - size_t size) - void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size) - void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const_char *name) - void (*access) (fuse_req_t req, fuse_ino_t ino, int mask) + size_t size) except * + void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size) except * + void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const_char *name) except * + void (*access) (fuse_req_t req, fuse_ino_t ino, int mask) except * void (*create) (fuse_req_t req, fuse_ino_t parent, const_char *name, - mode_t mode, fuse_file_info *fi) + mode_t mode, fuse_file_info *fi) except * void (*write_buf) (fuse_req_t req, fuse_ino_t ino, fuse_bufvec *bufv, - off_t off, fuse_file_info *fi) + off_t off, fuse_file_info *fi) except * void (*retrieve_reply) (fuse_req_t req, void *cookie, fuse_ino_t ino, - off_t offset, fuse_bufvec *bufv) + off_t offset, fuse_bufvec *bufv) except * void (*forget_multi) (fuse_req_t req, size_t count, - fuse_forget_data *forgets) + fuse_forget_data *forgets) except * void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode, - off_t offset, off_t length, fuse_file_info *fi) + off_t offset, off_t length, fuse_file_info *fi) except * int fuse_reply_err(fuse_req_t req, int err) void fuse_reply_none(fuse_req_t req) @@ -164,7 +167,7 @@ cdef extern from "" nogil: int fuse_session_receive_buf(fuse_session *se, fuse_buf *buf, fuse_chan **chp) void fuse_session_process_buf(fuse_session *se, - fuse_buf *buf, fuse_chan *ch) + fuse_buf *buf, fuse_chan *ch) except * void fuse_session_remove_chan(fuse_chan *ch) void fuse_session_reset(fuse_session *se) void fuse_session_exit(fuse_session *se) diff --git a/MANIFEST.in b/MANIFEST.in index a7e080f..13f07bb 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -7,6 +7,7 @@ graft rst graft util graft test prune test/.cache +prune test/.pytest_cache exclude MANIFEST.in recursive-include src *.pyx *.pxi *.c *.h global-exclude *.pyc diff --git a/README.rst b/README.rst index ef822d2..46ec1cc 100644 --- a/README.rst +++ b/README.rst @@ -14,6 +14,8 @@ The Python-LLFUSE Module Python-LLFUSE is no longer actively developed and just receiving community-contributed maintenance to keep it alive for some time. +A good alternative for some use cases might be `mfusepy `_. + Python-LLFUSE is a set of Python bindings for the low level FUSE_ API. It requires at least FUSE 2.8.0 and supports both Python 2.x and 3.x. Like FUSE itself, Python-LLFUSE is developed for Linux systems, @@ -38,7 +40,7 @@ Contributing The Python-LLFUSE source code is available on GitHub_. -.. __: http://www.rath.org/llfuse-docs/ +.. __: https://llfuse.readthedocs.io/ .. _FUSE: http://github.com/libfuse/libfuse .. _FUSE mailing list: https://lists.sourceforge.net/lists/listinfo/fuse-devel .. _issue tracker: https://github.com/python-llfuse/python-llfuse/issues diff --git a/developer-notes/release-process.md b/developer-notes/release-process.md index 6bcea4e..80d607f 100644 --- a/developer-notes/release-process.md +++ b/developer-notes/release-process.md @@ -6,7 +6,7 @@ # Releasing a new version # * `export DEVELOPER_MODE=0` # or just not have it set - * Bump version in `setup.py` + * Bump version in `setup.py` and version/release in `rst/conf.py` * Add release date to `Changes.rst` * Check `git status` to avoid undesired files in the tarball. * `./setup.py build_cython` @@ -14,8 +14,7 @@ * Extract tarball in temporary directory, * `./setup.py build_ext --inplace && python3 -m pytest test` * Run tests under valgrind. Build python `--with-valgrind --with-pydebug`, then `valgrind --trace-children=yes "--trace-children-skip=*mount*" python-dbg -m pytest test/` - * `./setup.py build_sphinx` - * `./setup.py upload_docs` + * `sphinx-build -b html rst doc/html` * `./util/sdist-sign 1.2.3` # use real version number, have GPG ready * `./util/upload-pypi 1.2.3` # use real version number, have twine installed * git commit, git tag -s diff --git a/examples/lltest.py b/examples/lltest.py index 37f8927..d090d61 100755 --- a/examples/lltest.py +++ b/examples/lltest.py @@ -91,7 +91,25 @@ def readdir(self, fh, off): # only one entry if off == 0: - yield (self.hello_name, self.getattr(self.hello_inode), 1) + yield (self.hello_name, self.getattr(self.hello_inode), self.hello_inode) + + def statfs(self, ctx): + stat_ = llfuse.StatvfsData() + + stat_.f_bsize = 512 + stat_.f_frsize = 512 + + size = 1024 * stat_.f_frsize + stat_.f_blocks = size // stat_.f_frsize + stat_.f_bfree = max(size // stat_.f_frsize, 1024) + stat_.f_bavail = stat_.f_bfree + + inodes = 100 + stat_.f_files = inodes + stat_.f_ffree = max(inodes, 100) + stat_.f_favail = stat_.f_ffree + + return stat_ def open(self, inode, flags, ctx): if inode != self.hello_inode: diff --git a/examples/tmpfs.py b/examples/tmpfs.py index 544ead9..553fd4d 100755 --- a/examples/tmpfs.py +++ b/examples/tmpfs.py @@ -146,7 +146,10 @@ def lookup(self, inode_p, name, ctx=None): def getattr(self, inode, ctx=None): - row = self.get_row('SELECT * FROM inodes WHERE id=?', (inode,)) + try: + row = self.get_row('SELECT * FROM inodes WHERE id=?', (inode,)) + except NoSuchRowError: + raise(llfuse.FUSEError(errno.ENOENT)) entry = llfuse.EntryAttributes() entry.st_ino = inode @@ -258,8 +261,8 @@ def _replace(self, inode_p_old, name_old, inode_p_new, name_new, def link(self, inode, new_inode_p, new_name, ctx): entry_p = self.getattr(new_inode_p) if entry_p.st_nlink == 0: - log.warn('Attempted to create entry %s with unlinked parent %d', - new_name, new_inode_p) + log.warning('Attempted to create entry %s with unlinked parent %d', + new_name, new_inode_p) raise FUSEError(errno.EINVAL) self.cursor.execute("INSERT INTO contents (name, inode, parent_inode) VALUES(?,?,?)", @@ -346,8 +349,8 @@ def create(self, inode_parent, name, mode, flags, ctx): def _create(self, inode_p, name, mode, ctx, rdev=0, target=None): if self.getattr(inode_p).st_nlink == 0: - log.warn('Attempted to create entry %s with unlinked parent %d', - name, inode_p) + log.warning('Attempted to create entry %s with unlinked parent %d', + name, inode_p) raise FUSEError(errno.EINVAL) now_ns = int(time() * 1e9) diff --git a/pyproject.toml b/pyproject.toml index fed528d..ef5db1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,3 @@ [build-system] -requires = ["setuptools"] +requires = ["setuptools >= 78.1.1"] build-backend = "setuptools.build_meta" diff --git a/requirements.d/rtd.txt b/requirements.d/rtd.txt new file mode 100644 index 0000000..f6629e0 --- /dev/null +++ b/requirements.d/rtd.txt @@ -0,0 +1 @@ +cython diff --git a/rst/conf.py b/rst/conf.py index fd8f42e..8c5b968 100644 --- a/rst/conf.py +++ b/rst/conf.py @@ -52,16 +52,16 @@ # General information about the project. project = 'Python-LLFUSE' -copyright = '2010-2015, Nikolaus Rath' +copyright = '2010-2025, Nikolaus Rath' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '1.0' +version = '1.6.0' # The full version, including alpha/beta/rc tags. -release = '1.0' +release = version + '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -74,7 +74,7 @@ #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. -unused_docs = [ ] +unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. diff --git a/rst/install.rst b/rst/install.rst index f1d656e..ad8220f 100644 --- a/rst/install.rst +++ b/rst/install.rst @@ -62,13 +62,13 @@ Development Version If you have checked out the unstable development version from the repository, a bit more effort is required. You need to also have -Cython_ (0.29.21 or newer) and Sphinx_ (1.1 or newer) installed, and the +Cython_ (Version >= 3) and Sphinx_ (1.1 or newer) installed, and the necessary commands are:: python setup.py build_cython python setup.py build_ext --inplace python -m pytest test/ - python setup.py build_sphinx + sphinx-build -b html rst doc/html python setup.py install diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index a3f8b20..0000000 --- a/setup.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[build_sphinx] -source-dir = rst -build-dir = doc diff --git a/setup.py b/setup.py index a4b462e..46cc313 100755 --- a/setup.py +++ b/setup.py @@ -34,7 +34,6 @@ import setuptools from setuptools import Extension -from distutils.version import LooseVersion basedir = os.path.abspath(os.path.dirname(__file__)) sys.path.insert(0, os.path.join(basedir, 'util')) @@ -45,25 +44,13 @@ if DEVELOPER_MODE: print('running in developer mode') warnings.resetwarnings() - # We can't use `error`, because e.g. Sphinx triggers a - # DeprecationWarning. warnings.simplefilter('default') -# Add src to load path, important for Sphinx autodoc -# to work properly -sys.path.insert(0, os.path.join(basedir, 'src')) -LLFUSE_VERSION = '1.4.3' +LLFUSE_VERSION = '1.6.0' def main(): - try: - from sphinx.application import Sphinx #pylint: disable-msg=W0612 - except ImportError: - pass - else: - fix_docutils() - with open(os.path.join(basedir, 'README.rst')) as fh: long_desc = fh.read() @@ -118,22 +105,19 @@ def main(): author='Nikolaus Rath', author_email='Nikolaus@rath.org', url='https://github.com/python-llfuse/python-llfuse/', - license='LGPL', + license='LGPL-2.0-or-later', + license_files=['LICENSE'], classifiers=['Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', + 'Programming Language :: Python :: 3.14', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: System :: Filesystems', - 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', 'Operating System :: POSIX :: Linux', 'Operating System :: MacOS :: MacOS X', 'Operating System :: POSIX :: BSD :: FreeBSD'], @@ -141,19 +125,12 @@ def main(): keywords=['FUSE', 'python' ], package_dir={'': 'src'}, packages=setuptools.find_packages('src'), - python_requires='>=3.5', - tests_require=['pytest >= 3.4.0'], + python_requires='>=3.10', provides=['llfuse'], ext_modules=[Extension('llfuse', c_sources, extra_compile_args=compile_args, extra_link_args=link_args)], - cmdclass={'upload_docs': upload_docs, - 'build_cython': build_cython }, - command_options={ - 'build_sphinx': { - 'version': ('setup.py', LLFUSE_VERSION), - 'release': ('setup.py', LLFUSE_VERSION), - }}, + cmdclass={'build_cython': build_cython}, ) @@ -188,21 +165,6 @@ def pkg_config(pkg, cflags=True, ldflags=False, min_ver=None): return cflags.decode('us-ascii').split() -class upload_docs(setuptools.Command): - user_options = [] - boolean_options = [] - description = "Upload documentation" - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - subprocess.check_call(['rsync', '-aHv', '--del', os.path.join(basedir, 'doc', 'html') + '/', - 'ebox.rath.org:/srv/www.rath.org/llfuse-docs/']) - class build_cython(setuptools.Command): user_options = [] boolean_options = [] @@ -221,11 +183,7 @@ def run(self): stderr=subprocess.STDOUT) except OSError: raise SystemExit('Cython needs to be installed for this command') - - hit = re.match('^Cython version (.+)$', version) - if not hit or LooseVersion(hit.group(1)) < "0.29": - # in fact, we need a very recent Cython version to support the latest pythons - raise SystemExit('Need Cython 0.29 or newer, found ' + version) + print(f"Using {version.strip()}.") cmd = ['cython', '-Wextra', '--force', '-3', '--fast-fail', '--directive', 'embedsignature=True', '--include-dir', @@ -246,30 +204,6 @@ def run(self): if subprocess.call(cmd + [path + '.pyx']) != 0: raise SystemExit('Cython compilation failed') -def fix_docutils(): - '''Work around https://bitbucket.org/birkenfeld/sphinx/issue/1154/''' - - import docutils.parsers - from docutils.parsers import rst - old_getclass = docutils.parsers.get_parser_class - - # Check if bug is there - try: - old_getclass('rst') - except AttributeError: - pass - else: - return - - def get_parser_class(parser_name): - """Return the Parser class from the `parser_name` module.""" - if parser_name in ('rst', 'restructuredtext'): - return rst.Parser - else: - return old_getclass(parser_name) - docutils.parsers.get_parser_class = get_parser_class - - assert docutils.parsers.get_parser_class('rst') is rst.Parser if __name__ == '__main__': main() diff --git a/src/darwin_compat.c b/src/darwin_compat.c index c9cbafe..4daf634 100644 --- a/src/darwin_compat.c +++ b/src/darwin_compat.c @@ -10,6 +10,11 @@ #include #include +static void _unlock_mutex(void *mutex) +{ + pthread_mutex_unlock((pthread_mutex_t *)mutex); +} + /* * Semaphore implementation based on: * @@ -152,7 +157,7 @@ darwin_sem_timedwait(darwin_sem_t *sem, const struct timespec *abs_timeout) return -1; } - pthread_cleanup_push((void(*)(void*))&pthread_mutex_unlock, + pthread_cleanup_push(&_unlock_mutex, &sem->__data.local.count_lock); pthread_mutex_lock(&sem->__data.local.count_lock); @@ -213,7 +218,7 @@ darwin_sem_wait(darwin_sem_t *sem) /* Must be volatile or will be clobbered by longjmp */ volatile int res = 0; - pthread_cleanup_push((void(*)(void*))&pthread_mutex_unlock, + pthread_cleanup_push(&_unlock_mutex, &sem->__data.local.count_lock); pthread_mutex_lock(&sem->__data.local.count_lock); diff --git a/src/fuse_api.pxi b/src/fuse_api.pxi index 23c69df..fa731a6 100644 --- a/src/fuse_api.pxi +++ b/src/fuse_api.pxi @@ -272,7 +272,7 @@ def main(workers=None, handle_signals=True): and the function to return. *SIGINT* (Ctrl-C) will thus *not* result in a `KeyboardInterrupt` exception while this function is runnning. Note setting *handle_signals* to `False` means you must handle the signals - by yourself and call `stop` to make the `main` returns. + by yourself and call ``stop`` to make the `main` returns. When the function returns because the file system has received an unmount request it will return `None`. If it returns because it has received a @@ -372,7 +372,7 @@ ctypedef struct worker_data_t: void* buf size_t bufsize -cdef void* worker_start(void* data) with gil: +cdef void* worker_start(void* data) noexcept with gil: cdef worker_data_t *wd cdef int res cdef uintptr_t tid @@ -387,7 +387,7 @@ cdef void* worker_start(void* data) with gil: session_loop(wd.buf, wd.bufsize) except: fuse_session_exit(session) - tid = wd.thread_id + tid = wd.thread_id log.error('FUSE worker thread %d terminated with exception, ' 'aborting processing', tid) res = pthread_mutex_lock(&exc_info_mutex) @@ -424,7 +424,6 @@ cdef session_loop_mt(workers): sigaddset(&newset, signal.SIGHUP); sigaddset(&newset, signal.SIGQUIT); - PyEval_InitThreads() bufsize = fuse_chan_bufsize(channel) wd = calloc_or_raise(workers, sizeof(worker_data_t)) try: diff --git a/src/llfuse.h b/src/llfuse.h index 6e0b5e0..c6c2061 100644 --- a/src/llfuse.h +++ b/src/llfuse.h @@ -14,7 +14,7 @@ the terms of the GNU LGPL. #ifdef __linux__ #define PLATFORM PLATFORM_LINUX -#elif __FreeBSD_kernel__&&__GLIBC__ +#elif __FreeBSD_kernel__ && __GLIBC__ #define PLATFORM PLATFORM_LINUX #elif __FreeBSD__ #define PLATFORM PLATFORM_BSD diff --git a/src/llfuse.pyx b/src/llfuse.pyx index f8a0971..a8f5597 100644 --- a/src/llfuse.pyx +++ b/src/llfuse.pyx @@ -95,7 +95,6 @@ cdef extern from *: EDEADLK cdef extern from "Python.h" nogil: - void PyEval_InitThreads() int PY_SSIZE_T_MAX # Actually passed as -D to cc (and defined in setup.py) diff --git a/src/misc.pxi b/src/misc.pxi index 7d75dc9..79a02ff 100644 --- a/src/misc.pxi +++ b/src/misc.pxi @@ -199,7 +199,7 @@ cdef class Lock: def yield_(self, count=1): '''Yield global lock to a different thread - A call to `~Lock.yield_` is roughly similar to:: + A call to ``~Lock.yield_`` is roughly similar to:: for i in range(count): if no_threads_waiting_for(lock): @@ -207,8 +207,8 @@ cdef class Lock: lock.release() lock.acquire() - However, when using `~Lock.yield_` it is guaranteed that the lock will - actually be passed to a different thread (the above pseude-code may + However, when using ``~Lock.yield_`` it is guaranteed that the lock will + actually be passed to a different thread (the above pseudocode may result in the same thread re-acquiring the lock *count* times). ''' @@ -685,13 +685,13 @@ cdef inline encap_ptr(void *ptr): cap.ptr = ptr return cap -cdef void signal_handler(int sig, siginfo_t *si, void* ctx) nogil: +cdef void signal_handler(int sig, siginfo_t *si, void* ctx) noexcept nogil: global exit_reason if session != NULL: fuse_session_exit(session) exit_reason = sig -cdef void do_nothing(int sig, siginfo_t *si, void* ctx) nogil: +cdef void do_nothing(int sig, siginfo_t *si, void* ctx) noexcept nogil: pass cdef int sigaction_p(int sig, sigaction_t *sa, diff --git a/test/ci-install.sh b/test/ci-install.sh deleted file mode 100755 index eb65339..0000000 --- a/test/ci-install.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -set -e - -pip install pytest cython "sphinx<7.0" -cython --version diff --git a/test/ci-test.sh b/test/ci-test.sh deleted file mode 100755 index fd20ffd..0000000 --- a/test/ci-test.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -set -e - -python setup.py build_cython -python setup.py build_ext --inplace -python -m pytest test/ - -python setup.py build_sphinx diff --git a/test/conftest.py b/test/conftest.py index fa19c6a..9564053 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -38,7 +38,7 @@ def pytest_addoption(parser): # example, if a request handler raises an exception, the server first signals an # error to FUSE (causing the test to fail), and then logs the exception. Without # the extra delay, the exception will go into nowhere. -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_pyfunc_call(pyfuncitem): outcome = yield failed = outcome.excinfo is not None diff --git a/test/test_api.py b/test/test_api.py index 733111b..8ff84d4 100755 --- a/test/test_api.py +++ b/test/test_api.py @@ -76,7 +76,7 @@ def test_xattr(): llfuse.setxattr(fh.name, key, value) except OSError as exc: if exc.errno == errno.ENOTSUP: - pytest.skip('ACLs not supported for %s' % fh.name) + pytest.skip('xattrs not supported for %s' % fh.name) raise assert _getxattr_helper(fh.name, key) == value diff --git a/test/util.py b/test/util.py index 43f9485..79e9dbc 100644 --- a/test/util.py +++ b/test/util.py @@ -10,6 +10,7 @@ import platform +import shutil import subprocess import pytest import os @@ -30,15 +31,9 @@ def fuse_test_marker(): return pytest.mark.uses_fuse() skip = lambda x: pytest.mark.skip(reason=x) - # Python 2.x: Popen is not a context manager... - which = subprocess.Popen(['which', 'fusermount'], stdout=subprocess.PIPE, - universal_newlines=True) - try: - fusermount_path = which.communicate()[0].strip() - finally: - which.wait() + fusermount_path = shutil.which("fusermount") - if not fusermount_path or which.returncode != 0: + if fusermount_path is None: return skip("Can't find fusermount executable") if not os.path.exists('/dev/fuse'): @@ -106,7 +101,7 @@ def cleanup(mnt_dir): def umount(mount_process, mnt_dir): if platform.system() == 'Darwin': - subprocess.check_call(['umount', '-l', mnt_dir]) + subprocess.check_call(['umount', mnt_dir]) else: subprocess.check_call(['fusermount', '-z', '-u', mnt_dir]) assert not os.path.ismount(mnt_dir) diff --git a/util/upload-pypi b/util/upload-pypi index b6338e4..f3d3ff1 100755 --- a/util/upload-pypi +++ b/util/upload-pypi @@ -8,11 +8,11 @@ if [ "$R" = "" ]; then fi if [ "$2" = "test" ]; then - export TWINE_REPOSITORY_URL=https://test.pypi.org/legacy/ + export TWINE_REPOSITORY=testllfuse else - export TWINE_REPOSITORY_URL= + export TWINE_REPOSITORY=llfuse fi D=dist/llfuse-$R.tar.gz -twine upload $D.asc $D +twine upload $D