aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArthur Zamarin <arthurzam@gentoo.org>2022-08-24 19:54:27 +0300
committerArthur Zamarin <arthurzam@gentoo.org>2022-08-24 20:58:54 +0300
commit52b80f10f3cf082f53ee9a1a7b9e80b53a933116 (patch)
tree9981e280b791041ef7e57c8c95ad1ed10932bddc
parentremove cython _sequences.pyx (diff)
downloadsnakeoil-52b80f10f3cf082f53ee9a1a7b9e80b53a933116.tar.gz
snakeoil-52b80f10f3cf082f53ee9a1a7b9e80b53a933116.tar.bz2
snakeoil-52b80f10f3cf082f53ee9a1a7b9e80b53a933116.zip
remove cython _posix.pyx
Signed-off-by: Arthur Zamarin <arthurzam@gentoo.org>
-rw-r--r--src/snakeoil/_posix.pyi10
-rw-r--r--src/snakeoil/_posix.pyx287
-rw-r--r--src/snakeoil/osutils/__init__.py12
-rw-r--r--src/snakeoil/process/__init__.py21
-rw-r--r--tests/test_osutils.py49
5 files changed, 4 insertions, 375 deletions
diff --git a/src/snakeoil/_posix.pyi b/src/snakeoil/_posix.pyi
deleted file mode 100644
index 4bbb9ea2..00000000
--- a/src/snakeoil/_posix.pyi
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-def normpath(old_path: str) -> str:
- ...
-
-def join(*args: str) -> str:
- ...
-
-def closerange(start: int, end: int) -> None:
- ...
diff --git a/src/snakeoil/_posix.pyx b/src/snakeoil/_posix.pyx
deleted file mode 100644
index e812ee62..00000000
--- a/src/snakeoil/_posix.pyx
+++ /dev/null
@@ -1,287 +0,0 @@
-# distutils: language = c
-# cython: language_level = 3
-
-from cpython.bytes cimport PyBytes_AS_STRING
-from libc.string cimport strdup
-from libc.stdio cimport snprintf
-from libc.stdlib cimport atoi, malloc, free
-from posix.unistd cimport close, getpid
-
-
-cdef extern from "ctype.h" nogil:
- int isdigit(int c)
-
-
-cdef inline void SKIP_SLASHES(char **s):
- """Skip slashes in a given string."""
- while (b'/' == s[0][0]):
- s[0] += 1
-
-
-cdef bytes _chars(s):
- """Convert input string to bytes."""
- if isinstance(s, unicode):
- # encode to the specific encoding used inside of the module
- return (<unicode>s).encode('utf8')
- elif isinstance(s, bytes):
- return s
- else:
- raise TypeError("arg must be str or bytes, not %s" % type(s).__name__)
-
-
-def normpath(old_path):
- """Normalize a path entry."""
- cdef char *path = strdup(PyBytes_AS_STRING(_chars(old_path)))
- if not path:
- raise MemoryError()
- cdef char *read = path
- cdef char *new_path = strdup(path)
- if not new_path:
- raise MemoryError()
- cdef char *write = new_path
- cdef int depth = 0
- cdef bint is_absolute = b'/' == path[0]
-
- if is_absolute:
- depth -= 1
-
- while b'\0' != read[0]:
- if b'/' == read[0]:
- write[0] = b'/'
- write += 1
- SKIP_SLASHES(&read)
- depth += 1
- elif b'.' == read[0]:
- if b'.' == read[1] and (b'/' == read[2] or b'\0' == read[2]):
- if depth == 1:
- if is_absolute:
- write = new_path
- else:
- # why -2? because write is at an empty char.
- # we need to jump back past it and /
- write -= 2
- while b'/' != write[0]:
- write -= 1
- write += 1
- depth = 0
- elif depth:
- write -= 2
- while b'/' != write[0]:
- write -= 1
- write += 1
- depth -= 1
- else:
- if is_absolute:
- write = new_path + 1
- else:
- write[0] = b'.'
- write[1] = b'.'
- write[2] = b'/'
- write += 3
- read += 2
- SKIP_SLASHES(&read)
- elif b'/' == read[1]:
- read += 2
- SKIP_SLASHES(&read)
- elif b'\0' == read[1]:
- read += 1
- else:
- write[0] = b'.'
- read += 1
- write += 1
- else:
- while b'/' != read[0] and b'\0' != read[0]:
- write[0] = read[0]
- write += 1
- read += 1
-
- if write - 1 > new_path and b'/' == write[-1]:
- write -= 1
-
- new_path[write - new_path] = 0
-
- cdef bytes py_path
- try:
- py_path = new_path[:write - new_path]
- finally:
- free(new_path)
- free(path)
-
- if isinstance(old_path, unicode):
- return py_path.decode('utf-8', 'strict')
- return py_path
-
-
-def join(*args):
- """Join multiple path items."""
- cdef ssize_t end = len(args)
- cdef ssize_t start = 0, length = 0, i = 0
- cdef bint leading_slash = False
- cdef char **paths = <char **>malloc(end * sizeof(char *))
-
- if not end:
- raise TypeError("join takes at least one argument (0 given)")
-
- for i in range(end):
- paths[i] = strdup(PyBytes_AS_STRING(_chars(args[i])))
-
- # find the right most item with a prefixed '/', else 0
- if b'/' == paths[i][0]:
- leading_slash = True
- start = i
-
- # know the relevant slice now; figure out the size.
- cdef char *s_start
- cdef char *s_end
- cdef char *s
-
- for i in range(start, end):
- # this is safe because we're checking types above
- s_start = s = paths[i]
- while b'\0' != s[0]:
- s += 1
- if s_start == s:
- continue
- length += s - s_start
- s_end = s
- if i + 1 != end:
- # cut the length down for trailing duplicate slashes
- while s != s_start and b'/' == s[-1]:
- s -= 1
- # allocate for a leading slash if needed
- if (s_end == s and (s_start != s or
- (s_end == s_start and i != start))):
- length += 1
- elif s_start != s:
- length -= s_end - s - 1
-
- # ok... we know the length. allocate a string, and copy it.
- cdef char *ret = <char *>malloc((length + 1) * sizeof(char))
- if not ret:
- raise MemoryError()
-
- cdef char *tmp_s
- cdef char *buf = ret
-
- if leading_slash:
- buf[0] = b'/'
- buf += 1
-
- for i in range(start, end):
- s_start = s = paths[i]
- if i == start and leading_slash:
- # a slash is inserted anyways, thus we skip one ahead
- # so it doesn't gain an extra.
- s_start += 1
- s = s_start
-
- if b'\0' == s[0]:
- continue
- while b'\0' != s[0]:
- buf[0] = s[0]
- buf += 1
- if b'/' == s[0]:
- tmp_s = s + 1
- SKIP_SLASHES(&s)
- if b'\0' == s[0]:
- if i + 1 != end:
- buf -= 1
- else:
- # copy the cracked out trailing slashes on the
- # last item
- while tmp_s < s:
- buf[0] = b'/'
- buf += 1
- tmp_s += 1
- break
- else:
- # copy the cracked out intermediate slashes.
- while tmp_s < s:
- buf[0] = b'/'
- buf += 1
- tmp_s += 1
- else:
- s += 1
-
- if i + 1 != end:
- buf[0] = b'/'
- buf += 1
-
- buf[0] = b'\0'
-
- cdef bytes py_path
- try:
- py_path = ret[:length]
- finally:
- free(ret)
- for i in range(end):
- free(paths[i])
- free(paths)
-
- if isinstance(args[0], unicode):
- return py_path.decode('utf-8', 'strict')
- return py_path
-
-
-cdef void slow_closerange(int start, int end):
- cdef int i
- for i in range(start, end):
- close(i)
-
-
-cdef extern from "dirent.h" nogil:
- cdef struct dirent:
- char *d_name
- ctypedef struct DIR
- int dirfd(DIR *dirp)
- DIR *opendir(char *name)
- int closedir(DIR *dirp)
- dirent *readdir(DIR *dirp)
- int readdir_r(DIR *dirp, dirent *entry, dirent **result)
-
-
-def closerange(int start, int end):
- """Close a range of fds."""
- cdef int i, fd_dir
-
- if start >= end:
- return
-
- cdef DIR *dir_handle
- cdef dirent *entry
- # this is sufficient for a 64-bit pid_t
- cdef char path[32]
-
- # Note that the version I submitted to python upstream has this in a
- # ALLOW_THREADS block; snakeoil doesn't since it's pointless.
- # Realistically the only time this code is ever ran is immediately post
- # fork- where no threads can be running. Thus no gain to releasing the GIL
- # then reacquiring it, thus we skip it.
-
- snprintf(path, sizeof(path), "/proc/%i/fd", getpid())
-
- dir_handle = opendir(path)
- if dir_handle == NULL:
- slow_closerange(start, end)
- return
-
- fd_dir = dirfd(dir_handle)
-
- if fd_dir < 0:
- closedir(dir_handle)
- slow_closerange(start, end)
- return
-
- while True:
- entry = readdir(dir_handle)
- if entry == NULL:
- break
-
- if not isdigit(entry.d_name[0]):
- continue
-
- i = atoi(entry.d_name)
- if i >= start and i < end and i != fd_dir:
- close(i)
-
- closedir(dir_handle)
diff --git a/src/snakeoil/osutils/__init__.py b/src/snakeoil/osutils/__init__.py
index 0523132b..d4c888ba 100644
--- a/src/snakeoil/osutils/__init__.py
+++ b/src/snakeoil/osutils/__init__.py
@@ -254,7 +254,7 @@ def abspath(path):
return path
-def native_normpath(mypath):
+def normpath(mypath: str) -> str:
"""normalize path- //usr/bin becomes /usr/bin, /usr/../bin becomes /bin
see :py:func:`os.path.normpath` for details- this function differs from
@@ -266,17 +266,9 @@ def native_normpath(mypath):
return newpath[1:]
return newpath
-native_join = os.path.join
-
-try:
- from .._posix import join, normpath
-except ImportError:
- normpath = native_normpath
- join = native_join
-
# convenience. importing join into a namespace is ugly, pjoin less so
-pjoin = join
+pjoin = join = os.path.join
@steal_docs(os.access)
diff --git a/src/snakeoil/process/__init__.py b/src/snakeoil/process/__init__.py
index b086b838..00db14c2 100644
--- a/src/snakeoil/process/__init__.py
+++ b/src/snakeoil/process/__init__.py
@@ -99,23 +99,4 @@ class ProcessNotFound(Exception):
super().__init__(f'nonexistent process: {pid}')
-def _native_closerange(from_fd, to_fd):
- for fd in range(from_fd, to_fd):
- try:
- os.close(fd)
- except EnvironmentError:
- pass
-
-try:
- if os.uname()[0].lower() != 'linux':
- # the optimized closerange works for sure on linux/glibc; for others
- # whitelist expand this as needed.
- raise ImportError()
- from .._posix import closerange
-
- # monkey patch os.closerange with the saner version;
- # this makes subprocess.Popen calls less noisy, and slightly faster.
- # only do this if we can drop our optimized version in.
- os.closerange = closerange
-except ImportError:
- closerange = getattr(os, 'closerange', _native_closerange)
+closerange = os.closerange
diff --git a/tests/test_osutils.py b/tests/test_osutils.py
index 2a1668b1..aea0ddab 100644
--- a/tests/test_osutils.py
+++ b/tests/test_osutils.py
@@ -235,7 +235,7 @@ class TestAbsSymlink:
class Test_Native_NormPath:
- func = staticmethod(osutils.native_normpath)
+ func = staticmethod(osutils.normpath)
def test_normpath(self):
f = self.func
@@ -268,51 +268,6 @@ class Test_Native_NormPath:
check(b'/f\xc3\xb6\xc3\xb3/..', b'/')
-@pytest.mark.skipif(osutils.normpath is osutils.native_normpath, reason="extension isn't compiled")
-class Test_Cpy_NormPath(Test_Native_NormPath):
- func = staticmethod(osutils.normpath)
-
-
-@pytest.mark.skipif(osutils.join is osutils.native_join, reason="extension isn't compiled")
-class Test_Cpy_Join:
-
- def test_reimplementation(self):
- vals = [
- [""],
- ["foo"],
- ["", "foo"],
- ["foo", "dar"],
- ["foo", "/bar"],
- ["/bar", "dar"],
- ["/bar", "../dar"],
- ["", "../dar"],
- ["/bár", "dãr"],
- [b"/b\xc3\xa1r", b"d\xc3\xa3r"],
- ]
-
- for x in vals:
- assert osutils.native_join(*x) == osutils.join(*x), \
- "for %r, expected %r, got %r" % (
- val, osutils.native_join(*x), osutils.join(*x))
-
- # proper type checking was done in py3.5
- @pytest.mark.skipif(sys.hexversion < 0x03050000, reason='requires >=py3.5')
- def test_reimplementation_errors(self):
- # various type errors
- errors = [
- [],
- [1],
- ["foo", 1],
- ["foo", "/bar", []],
- ]
-
- for x in errors:
- with pytest.raises(TypeError):
- osutils.native_join(*x)
- with pytest.raises(TypeError):
- osutils.join(*x)
-
-
@pytest.mark.skipif(os.getuid() != 0, reason="these tests must be ran as root")
class TestAccess:
@@ -481,8 +436,6 @@ class TestMount:
Test_cpy_readdir_loaded = mk_cpy_loadable_testcase(
"snakeoil.osutils._readdir", "snakeoil.osutils", "listdir", "listdir")
-Test_cpy_posix_loaded = mk_cpy_loadable_testcase(
- "snakeoil._posix", "snakeoil.osutils", "normpath", "normpath")
class TestSizeofFmt: