Skip to content

Commit 1017cab

Browse files
committed
Enable Pyright type checking, and fix all reported issues.
1 parent d521be5 commit 1017cab

File tree

10 files changed

+93
-39
lines changed

10 files changed

+93
-39
lines changed

.github/workflows/test.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ jobs:
4040

4141
- name: Type check (mypy)
4242
run: uv run mypy .
43-
43+
44+
- name: Type check (pyright)
45+
run: uv run pyright
46+
4447
- name: Run tests
4548
run: uv run pytest -v -rs test/
4649

examples/hello.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@
2727
import os
2828
import stat
2929
from argparse import ArgumentParser
30+
from typing import cast
3031

3132
import trio
3233

3334
import pyfuse3
35+
from pyfuse3 import FileHandleT, FileInfo, InodeT
3436

3537
try:
3638
import faulthandler
@@ -45,7 +47,7 @@ class TestFs(pyfuse3.Operations):
4547
def __init__(self):
4648
super(TestFs, self).__init__()
4749
self.hello_name = b"message"
48-
self.hello_inode = pyfuse3.ROOT_INODE+1
50+
self.hello_inode = cast(InodeT, pyfuse3.ROOT_INODE+1)
4951
self.hello_data = b"hello world\n"
5052

5153
async def getattr(self, inode, ctx=None):
@@ -77,7 +79,8 @@ async def lookup(self, parent_inode, name, ctx=None):
7779
async def opendir(self, inode, ctx):
7880
if inode != pyfuse3.ROOT_INODE:
7981
raise pyfuse3.FUSEError(errno.ENOENT)
80-
return inode
82+
# For simplicity, we use the inode as file handle
83+
return FileHandleT(inode)
8184

8285
async def readdir(self, fh, start_id, token):
8386
assert fh == pyfuse3.ROOT_INODE
@@ -93,7 +96,8 @@ async def open(self, inode, flags, ctx):
9396
raise pyfuse3.FUSEError(errno.ENOENT)
9497
if flags & os.O_RDWR or flags & os.O_WRONLY:
9598
raise pyfuse3.FUSEError(errno.EACCES)
96-
return pyfuse3.FileInfo(fh=inode)
99+
# For simplicity, we use the inode as file handle
100+
return FileInfo(fh=FileHandleT(inode))
97101

98102
async def read(self, fh, off, size):
99103
assert fh == self.hello_inode

examples/hello_asyncio.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@
2828
import os
2929
import stat
3030
from argparse import ArgumentParser
31+
from typing import cast
3132

3233
import pyfuse3
3334
import pyfuse3.asyncio
35+
from pyfuse3 import FileHandleT, FileInfo, InodeT
3436

3537
try:
3638
import faulthandler
@@ -46,7 +48,7 @@ class TestFs(pyfuse3.Operations):
4648
def __init__(self):
4749
super(TestFs, self).__init__()
4850
self.hello_name = b"message"
49-
self.hello_inode = pyfuse3.ROOT_INODE+1
51+
self.hello_inode = cast(InodeT, pyfuse3.ROOT_INODE+1)
5052
self.hello_data = b"hello world\n"
5153

5254
async def getattr(self, inode, ctx=None):
@@ -78,7 +80,8 @@ async def lookup(self, parent_inode, name, ctx=None):
7880
async def opendir(self, inode, ctx):
7981
if inode != pyfuse3.ROOT_INODE:
8082
raise pyfuse3.FUSEError(errno.ENOENT)
81-
return inode
83+
# For simplicity, we use the inode as file handle
84+
return FileHandleT(inode)
8285

8386
async def readdir(self, fh, start_id, token):
8487
assert fh == pyfuse3.ROOT_INODE
@@ -103,7 +106,8 @@ async def open(self, inode, flags, ctx):
103106
raise pyfuse3.FUSEError(errno.ENOENT)
104107
if flags & os.O_RDWR or flags & os.O_WRONLY:
105108
raise pyfuse3.FUSEError(errno.EACCES)
106-
return pyfuse3.FileInfo(fh=inode)
109+
# For simplicity, we use the inode as file handle
110+
return FileInfo(fh=FileHandleT(inode))
107111

108112
async def read(self, fh, off, size):
109113
assert fh == self.hello_inode

examples/passthroughfs.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ def _getattr(self, path=None, fd=None):
130130
assert not(fd is None and path is None)
131131
try:
132132
if fd is None:
133+
assert path is not None
133134
stat = os.lstat(path)
134135
else:
135136
stat = os.fstat(fd)
@@ -160,7 +161,8 @@ async def readlink(self, inode, ctx):
160161
return fsencode(target)
161162

162163
async def opendir(self, inode, ctx):
163-
return inode
164+
# For simplicity, we use the inode as file handle
165+
return FileHandleT(inode)
164166

165167
async def readdir(self, fh, start_id, token):
166168
path = self._inode_to_path(fh)
@@ -231,13 +233,13 @@ async def symlink(self, parent_inode, name, target, ctx):
231233
path = os.path.join(parent, name)
232234
try:
233235
os.symlink(target, path)
234-
os.chown(path, ctx.uid, ctx.gid, follow_symlinks=False)
236+
os.lchown(path, ctx.uid, ctx.gid)
235237
except OSError as exc:
236238
assert exc.errno is not None
237239
raise FUSEError(exc.errno)
238240
stat = os.lstat(path)
239241
self._add_path(stat.st_ino, path)
240-
return await self.getattr(stat.st_ino)
242+
return await self.getattr(InodeT(stat.st_ino))
241243

242244
async def rename(self, parent_inode_old, name_old, parent_inode_new, name_new,
243245
flags, ctx):

examples/tmpfs.py

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@
2929
from argparse import ArgumentParser
3030
from collections import defaultdict
3131
from time import time
32+
from typing import cast
3233

3334
import trio
3435

3536
import pyfuse3
36-
from pyfuse3 import FUSEError
37+
from pyfuse3 import FileHandleT, FileInfo, FUSEError, InodeT
3738

3839
try:
3940
import faulthandler
@@ -124,9 +125,9 @@ def get_row(self, *a, **kw):
124125
return row
125126

126127
async def lookup(self, parent_inode, name, ctx=None):
127-
if name == '.':
128+
if name == b'.':
128129
inode = parent_inode
129-
elif name == '..':
130+
elif name == b'..':
130131
inode = self.get_row("SELECT * FROM contents WHERE inode=?",
131132
(parent_inode,))['parent_inode']
132133
else:
@@ -136,7 +137,7 @@ async def lookup(self, parent_inode, name, ctx=None):
136137
except NoSuchRowError:
137138
raise(pyfuse3.FUSEError(errno.ENOENT))
138139

139-
return await self.getattr(inode, ctx)
140+
return await self.getattr(InodeT(inode), ctx)
140141

141142

142143
async def getattr(self, inode, ctx=None):
@@ -170,19 +171,22 @@ async def readlink(self, inode, ctx):
170171
return self.get_row('SELECT * FROM inodes WHERE id=?', (inode,))['target']
171172

172173
async def opendir(self, inode, ctx):
173-
return inode
174+
# For simplicity, we use the inode as file handle
175+
return FileHandleT(inode)
174176

175177
async def readdir(self, fh, start_id, token):
176178
if start_id == 0:
177-
start_id = -1
179+
off = -1
180+
else:
181+
off = start_id
178182

179183
cursor2 = self.db.cursor()
180184
cursor2.execute("SELECT * FROM contents WHERE parent_inode=? "
181-
'AND rowid > ? ORDER BY rowid', (fh, start_id))
185+
'AND rowid > ? ORDER BY rowid', (fh, off))
182186

183187
for row in cursor2:
184188
pyfuse3.readdir_reply(
185-
token, row['name'], await self.getattr(row['inode']), row['rowid'])
189+
token, row['name'], await self.getattr(InodeT(row['inode'])), row['rowid'])
186190

187191
async def unlink(self, parent_inode, name, ctx):
188192
entry = await self.lookup(parent_inode, name)
@@ -224,16 +228,14 @@ async def rename(self, parent_inode_old, name_old, parent_inode_new, name_new,
224228

225229
entry_old = await self.lookup(parent_inode_old, name_old)
226230

231+
entry_new = None
227232
try:
228-
entry_new = await self.lookup(parent_inode_new, name_new)
233+
entry_new = await self.lookup(parent_inode_new, name_new if isinstance(name_new, bytes) else name_new.encode())
229234
except pyfuse3.FUSEError as exc:
230235
if exc.errno != errno.ENOENT:
231236
raise
232-
target_exists = False
233-
else:
234-
target_exists = True
235237

236-
if target_exists:
238+
if entry_new is not None:
237239
self._replace(parent_inode_old, name_old, parent_inode_new, name_new,
238240
entry_old, entry_new)
239241
else:
@@ -335,12 +337,10 @@ async def statfs(self, ctx):
335337
return stat_
336338

337339
async def open(self, inode, flags, ctx):
338-
# Yeah, unused arguments
339-
#pylint: disable=W0613
340340
self.inode_open_count[inode] += 1
341341

342-
# Use inodes as a file handles
343-
return pyfuse3.FileInfo(fh=inode)
342+
# For simplicity, we use the inode as file handle
343+
return FileInfo(fh=FileHandleT(inode))
344344

345345
async def access(self, inode, mode, ctx):
346346
# Yeah, could be a function and has unused arguments
@@ -351,7 +351,8 @@ async def create(self, parent_inode, name, mode, flags, ctx):
351351
#pylint: disable=W0612
352352
entry = await self._create(parent_inode, name, mode, ctx)
353353
self.inode_open_count[entry.st_ino] += 1
354-
return (pyfuse3.FileInfo(fh=entry.st_ino), entry)
354+
# For simplicity, we use the inode as file handle
355+
return (FileInfo(fh=FileHandleT(entry.st_ino)), entry)
355356

356357
async def _create(self, parent_inode, name, mode, ctx, rdev=0, target=None):
357358
if (await self.getattr(parent_inode)).st_nlink == 0:
@@ -364,7 +365,7 @@ async def _create(self, parent_inode, name, mode, ctx, rdev=0, target=None):
364365
'ctime_ns, target, rdev) VALUES(?, ?, ?, ?, ?, ?, ?, ?)',
365366
(ctx.uid, ctx.gid, mode, now_ns, now_ns, now_ns, target, rdev))
366367

367-
inode = self.cursor.lastrowid
368+
inode = cast(InodeT, self.cursor.lastrowid)
368369
self.db.execute("INSERT INTO contents(name, inode, parent_inode) VALUES(?,?,?)",
369370
(name, inode, parent_inode))
370371
return await self.getattr(inode)
@@ -386,12 +387,13 @@ async def write(self, fh, off, buf):
386387
return len(buf)
387388

388389
async def release(self, fh):
389-
self.inode_open_count[fh] -= 1
390+
inode = cast(InodeT, fh)
391+
self.inode_open_count[inode] -= 1
390392

391-
if self.inode_open_count[fh] == 0:
392-
del self.inode_open_count[fh]
393-
if (await self.getattr(fh)).st_nlink == 0:
394-
self.cursor.execute("DELETE FROM inodes WHERE id=?", (fh,))
393+
if self.inode_open_count[inode] == 0:
394+
del self.inode_open_count[inode]
395+
if (await self.getattr(inode)).st_nlink == 0:
396+
self.cursor.execute("DELETE FROM inodes WHERE id=?", (inode,))
395397

396398
class NoUniqueValueError(Exception):
397399
def __str__(self):

pyproject.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Homepage = "https://github.com/libfuse/pyfuse3"
3232

3333
[dependency-groups]
3434
dev = [
35+
"pyright>=1.1.407",
3536
"mypy>=1.19.1",
3637
"pytest >= 3.4.0",
3738
"pytest-trio",
@@ -77,3 +78,14 @@ disallow_untyped_defs = false
7778
check_untyped_defs = true
7879
warn_redundant_casts = true
7980
warn_unused_ignores = true
81+
82+
[tool.pyright]
83+
typeCheckingMode = "standard"
84+
exclude = [ "**/__pycache__",
85+
"**/.*",
86+
"util/",
87+
"rst/conf.py" ]
88+
89+
# Need for pyright to resolve tests importing tests/util.py (when pytest runs the
90+
# test, it adds the tests/ directory to sys.path)
91+
extraPaths = ['test']

src/pyfuse3/_pyfuse3.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,9 +334,9 @@ async def symlink(
334334
async def rename(
335335
self,
336336
parent_inode_old: InodeT,
337-
name_old: str,
337+
name_old: FileNameT,
338338
parent_inode_new: InodeT,
339-
name_new: str,
339+
name_new: FileNameT,
340340
flags: FlagT,
341341
ctx: "RequestContext"
342342
) -> None:

test/test_api.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def test_syncfs():
4242
pyfuse3.syncfs('.')
4343

4444
def _getxattr_helper(path, name):
45+
errno = None
4546
try:
4647
value = pyfuse3.getxattr(path, name)
4748
except OSError as exc:

test/test_fs.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import trio
2929

3030
import pyfuse3
31-
from pyfuse3 import FUSEError
31+
from pyfuse3 import FileHandleT, FileInfo, FUSEError
3232
from util import cleanup, fuse_test_marker, umount, wait_for_mount
3333

3434
pytestmark = fuse_test_marker()
@@ -212,7 +212,8 @@ async def lookup(self, parent_inode, name, ctx=None):
212212
async def opendir(self, inode, ctx):
213213
if inode != pyfuse3.ROOT_INODE:
214214
raise pyfuse3.FUSEError(errno.ENOENT)
215-
return inode
215+
# For simplicity, we use the inode as file handle
216+
return FileHandleT(inode)
216217

217218
async def readdir(self, fh, start_id, token):
218219
assert fh == pyfuse3.ROOT_INODE
@@ -226,7 +227,8 @@ async def open(self, inode, flags, ctx):
226227
raise pyfuse3.FUSEError(errno.ENOENT)
227228
if flags & os.O_RDWR or flags & os.O_WRONLY:
228229
raise pyfuse3.FUSEError(errno.EACCES)
229-
return pyfuse3.FileInfo(fh=inode)
230+
# For simplicity, we use the inode as file handle
231+
return FileInfo(fh=FileHandleT(inode))
230232

231233
async def read(self, fh, off, size):
232234
assert fh == self.hello_inode

uv.lock

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)