Skip to content

Commit

Permalink
python bindings: switch to setuptools
Browse files Browse the repository at this point in the history
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
  • Loading branch information
cyphar committed Jul 31, 2024
1 parent d977d7a commit c7728ed
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 25 deletions.
4 changes: 2 additions & 2 deletions contrib/bindings/python/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
/__pycache__/
/_pathrs*
/dist/
/*.egg-info/
2 changes: 2 additions & 0 deletions contrib/bindings/python/pathrs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/__pycache__/
/_libpathrs_cffi*
21 changes: 21 additions & 0 deletions contrib/bindings/python/pathrs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/python3
# 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.

from ._pathrs import *

# TODO: Figure out a way to keep this version up-to-date with Cargo.toml.
__version__ = "0.0.2"
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import copy
import fcntl

from _pathrs import ffi, lib as libpathrs_so
from ._libpathrs_cffi import ffi, lib as libpathrs_so

__all__ = [
# core api
Expand Down Expand Up @@ -102,22 +102,21 @@ def pprint(self, out=sys.stdout):
INTERNAL_ERROR = Error("tried to fetch libpathrs error but no error found")


def fileno(file):
def _fileno(file):
if isinstance(file, int):
# file is a plain fd
return file
else:
# Assume there is a fileno method.
return file.fileno()


def clonefile(file):
def _clonefile(file):
return fcntl.fcntl(fileno(file), fcntl.F_DUPFD_CLOEXEC)


class WrappedFd(object):
def __init__(self, file):
fd = fileno(file)
fd = _fileno(file)
if isinstance(file, io.IOBase):
# If this is a regular open file, we need to make a copy because
# you cannot leak files and so the GC might close it from
Expand Down Expand Up @@ -161,7 +160,7 @@ def close(self):
def clone(self):
if self.isclosed():
raise ValueError("cannot clone closed file")
return self.__class__(clonefile(self))
return self.__class__(_clonefile(self))

def __copy__(self):
# A "shallow copy" of a file is the same as a deep copy.
Expand All @@ -175,7 +174,7 @@ def __del__(self):


# XXX: This is _super_ ugly but so is the one in CPython.
def convert_mode(mode):
def _convert_mode(mode):
mode = set(mode)
flags = os.O_CLOEXEC

Expand Down Expand Up @@ -216,7 +215,7 @@ def convert_mode(mode):
PROC_THREAD_SELF = libpathrs_so.PATHRS_PROC_THREAD_SELF

def proc_open(base, path, mode="r", extra_flags=0):
flags = convert_mode(mode) | extra_flags
flags = _convert_mode(mode) | extra_flags
return proc_open_raw(base, path, flags).fdopen(mode)

def proc_open_raw(base, path, flags):
Expand Down Expand Up @@ -256,7 +255,7 @@ def from_file(cls, file):
return cls(file)

def reopen(self, mode="r", extra_flags=0):
flags = convert_mode(mode) | extra_flags
flags = _convert_mode(mode) | extra_flags
return self.reopen_raw(flags).fdopen(mode)

def reopen_raw(self, flags):
Expand Down Expand Up @@ -315,7 +314,7 @@ def readlink(self, path):

def creat(self, path, filemode, mode="r", extra_flags=0):
path = _cstr(path)
flags = convert_mode(mode) | extra_flags
flags = _convert_mode(mode) | extra_flags
fd = libpathrs_so.pathrs_creat(self.fileno(), path, flags, filemode)
if fd < 0:
raise Error._fetch(fd) or INTERNAL_ERROR
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/python3
# libpathrs: safe path resolution on Linux
# Copyright (C) 2019-2021 Aleksa Sarai <cyphar@cyphar.com>
# Copyright (C) 2019-2021 SUSE LLC
# 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.
Expand All @@ -19,11 +19,10 @@
# build of libpathrs, and can be redistributed alongside the pathrs.py wrapping
# library). It's much better than the ABI-mode of CFFI.

# TODO: Make this work properly with setuptools -- this might take some work.

import re
import os
import sys

import cffi

def load_hdr(ffi, hdr_path):
Expand All @@ -42,25 +41,24 @@ def load_hdr(ffi, hdr_path):
# Load the header.
ffi.cdef(hdr)

def compile_module(**kwargs):
def create_ffibuilder(**kwargs):
ffibuilder = cffi.FFI()
ffibuilder.cdef("typedef uint32_t dev_t;")

# We need to use cdef to tell cffi what functions we need to FFI to. But we
# don't need the structs (I hope).
for include_dir in kwargs["include_dirs"]:
for include_dir in kwargs.get("include_dirs", []):
pathrs_hdr = os.path.join(include_dir, "pathrs.h")
if os.path.exists(pathrs_hdr):
load_hdr(ffibuilder, pathrs_hdr)

# Add a source and link to libpathrs.
ffibuilder.set_source("_pathrs", "#include <pathrs.h>",
ffibuilder.set_source("_libpathrs_cffi", "#include <pathrs.h>",
libraries=["pathrs"], **kwargs)

# Compile the cffi module.
ffibuilder.compile(verbose=True)
return ffibuilder

def main():
def find_ffibuilder():
# Figure out where the libpathrs source dir is.
ROOT_DIR = None
candidate = os.path.dirname(sys.path[0] or os.getcwd())
Expand Down Expand Up @@ -91,8 +89,16 @@ def main():
lib_paths = [os.path.dirname(path) for path in lib_paths]

# Compile the libpathrs module.
compile_module(include_dirs=[os.path.join(ROOT_DIR, "include")],
library_dirs=lib_paths)
return create_ffibuilder(include_dirs=[os.path.join(ROOT_DIR, "include")],
library_dirs=lib_paths)

if __name__ == "__main__":
main()
# Compile the cffi module if running outside of setuptools.
ffibuilder = find_ffibuilder()
ffibuilder.compile(verbose=True)
else:
# Use the system libraries if running inside setuptools.
ffibuilder = create_ffibuilder(include_dirs=[
"/usr/include",
"/usr/local/include"
])
58 changes: 58 additions & 0 deletions contrib/bindings/python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/python3
# 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.

[build-system]
requires = [
"setuptools>=68",
"wheel",
"cffi>=1.10.0"
]
build-backend = "setuptools.build_meta"

[project]
name = "libpathrs"
# TODO: Figure out a way to keep this version up-to-date with Cargo.toml.
version = "0.0.2"
description = "Python bindings for libpathrs, a safe path resolution library for Linux."
keywords = ["libpathrs", "pathrs"]
license = { file = "COPYING.APACHE-2.0" }
authors = [
{name = "Aleksa Sarai", email = "cyphar@cyphar.com"},
]
maintainers = [
{name = "Aleksa Sarai", email = "cyphar@cyphar.com"},
]
classifiers = [
"Topic :: Security",
"Topic :: System :: Filesystems",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Natural Language :: English",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Topic :: Software Development :: Libraries :: Python Modules",
]

requires-python = ">= 3.8"
dependencies = [
"cffi>=1.10.0"
]

[project.urls]
Homepage = "https://github.com/openSUSE/libpathrs"
Repository = "https://github.com/openSUSE/libpathrs"
24 changes: 24 additions & 0 deletions contrib/bindings/python/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/python3
# 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 setuptools

setuptools.setup(
ext_package="pathrs",
platforms=["Linux"],
cffi_modules=["pathrs/pathrs_build.py:ffibuilder"],
)

0 comments on commit c7728ed

Please sign in to comment.