Skip to content

Commit

Permalink
python bindings: add type annotations
Browse files Browse the repository at this point in the history
This is just a first pass, trying to make the annotations make sense. We
probably need to adjust some of them for packaging before the next
release.

The type annotations for libpathrs.so should be sufficient but it's a
bit unfortunate that ffi.NULL can't be listed as a possible return value
(a-la None with Optional). This makes the typing for errorinfo a little
dodgy, and the Error._fetch typing also is not entirely idiomatic (maybe
we need to have an overload to indicate that only actual ErrorIds return
an Error). It would be nice if we could do Literal[0..] but that syntax
doesn't exist.

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
  • Loading branch information
cyphar committed Oct 8, 2024
1 parent 77a3d34 commit ad81670
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 99 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Fixes ###
- python bindings: add a minimal README for PyPI.
- python bindings: actually export `PROC_ROOT`.
- python bindings: add type annotations and `py.typed` to allow for downstream
users to get proper type annotations for the API.

## [0.1.1] - 2024-10-01 ##

Expand Down
2 changes: 1 addition & 1 deletion contrib/bindings/python/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ PIP ?= pip3

SRC_FILES := $(wildcard *.py pathrs/*.py)

dist: $(SRC_FILES)
dist: $(SRC_FILES) pyproject.toml
$(PYTHON) -m build

.PHONY: clean
Expand Down
2 changes: 1 addition & 1 deletion contrib/bindings/python/pathrs/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
/__pycache__/
/_libpathrs_cffi*
/_libpathrs_cffi.*
19 changes: 19 additions & 0 deletions contrib/bindings/python/pathrs/_libpathrs_cffi/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# libpathrs: safe path resolution on Linux
# Copyright (C) 2019-2024 Aleksa Sarai <cyphar@cyphar.com>
# Copyright (C) 2019-2024 SUSE LLC
#
# 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.

import cffi

ffi: cffi.FFI
83 changes: 83 additions & 0 deletions contrib/bindings/python/pathrs/_libpathrs_cffi/lib.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# libpathrs: safe path resolution on Linux
# Copyright (C) 2019-2024 Aleksa Sarai <cyphar@cyphar.com>
# Copyright (C) 2019-2024 SUSE LLC
#
# 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.

import cffi

from typing import Optional, overload, type_check_only, Union

# TODO: Remove this once we only support Python >= 3.10.
from typing_extensions import TypeAlias, Literal

CBuffer: TypeAlias = cffi.FFI.CData # char[n]
CString: TypeAlias = cffi.FFI.CData # char *

# pathrs_errorinfo_t *
@type_check_only
class CError:
saved_errno: int
description: CString

ErrorId: TypeAlias = int
RawFd: TypeAlias = int

# TODO: We actually return Union[CError, cffi.FFI.NULL] but we can't express
# this using the typing stubs for CFFI...
def pathrs_errorinfo(err_id: Union[ErrorId, int]) -> CError: ...
def pathrs_errorinfo_free(err: CError) -> None: ...

ProcfsBase: TypeAlias = int

PATHRS_PROC_ROOT: ProcfsBase
PATHRS_PROC_SELF: ProcfsBase
PATHRS_PROC_THREAD_SELF: ProcfsBase

def pathrs_proc_open(
base: ProcfsBase, path: CString, flags: int
) -> Union[RawFd, ErrorId]: ...
def pathrs_proc_readlink(
base: ProcfsBase, path: CString, linkbuf: CBuffer, linkbuf_size: int
) -> Union[int, ErrorId]: ...
def pathrs_root_open(path: CString) -> Union[RawFd, ErrorId]: ...
def pathrs_reopen(fd: RawFd, flags: int) -> Union[RawFd, ErrorId]: ...
def pathrs_resolve(rootfd: RawFd, path: CString) -> Union[RawFd, ErrorId]: ...
def pathrs_resolve_nofollow(rootfd: RawFd, path: CString) -> Union[RawFd, ErrorId]: ...
def pathrs_creat(
rootfd: RawFd, path: CString, flags: int, filemode: int
) -> Union[RawFd, ErrorId]: ...
def pathrs_rename(
rootfd: RawFd, src: CString, dst: CString, flags: int
) -> Union[Literal[0], ErrorId]: ...
def pathrs_rmdir(rootfd: RawFd, path: CString) -> Union[Literal[0], ErrorId]: ...
def pathrs_unlink(rootfd: RawFd, path: CString) -> Union[Literal[0], ErrorId]: ...
def pathrs_remove_all(rootfd: RawFd, path: CString) -> Union[RawFd, ErrorId]: ...
def pathrs_mkdir(
rootfd: RawFd, path: CString, mode: int
) -> Union[Literal[0], ErrorId]: ...
def pathrs_mkdir_all(
rootfd: RawFd, path: CString, mode: int
) -> Union[Literal[0], ErrorId]: ...
def pathrs_mknod(
rootfd: RawFd, path: CString, mode: int, dev: int
) -> Union[Literal[0], ErrorId]: ...
def pathrs_hardlink(
rootfd: RawFd, path: CString, target: CString
) -> Union[Literal[0], ErrorId]: ...
def pathrs_symlink(
rootfd: RawFd, path: CString, target: CString
) -> Union[Literal[0], ErrorId]: ...
def pathrs_readlink(
rootfd: RawFd, path: CString, linkbuf: CBuffer, linkbuf_size: int
) -> Union[int, ErrorId]: ...
Loading

0 comments on commit ad81670

Please sign in to comment.