This is the first patch of a series which merges a pure python
implementation of the kernel-doc parser. It adds the prerequisites which
are needed by the pure python implementation (which comes later in the
series).

The fspath module in this patch is a reduced implementation of pypi's
fspath [1]. As an alternative to this patch we can add an external
dependence which can be installed from PyPi with 'pip install fspath'
see [2] (but I guess we won't more external dependencies).

[1] https://pypi.python.org/pypi/fspath/
{2] https://return42.github.io/fspath/

Signed-off-by: Markus Heiser <markus.hei...@darmarit.de>
---
 Documentation/sphinx/fspath.py | 435 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 435 insertions(+)
 create mode 100644 Documentation/sphinx/fspath.py

diff --git a/Documentation/sphinx/fspath.py b/Documentation/sphinx/fspath.py
new file mode 100644
index 0000000..554b8a2
--- /dev/null
+++ b/Documentation/sphinx/fspath.py
@@ -0,0 +1,435 @@
+# -*- coding: utf-8; mode: python -*-
+u"""
+Handling path names and executables more comfortable.
+"""
+
+# 
==============================================================================
+# imports
+# 
==============================================================================
+
+import io
+import os
+from os import path
+import re
+import shutil
+import subprocess
+import sys
+from glob import iglob
+
+import six
+
+# 
==============================================================================
+class FSPath(six.text_type):
+# 
==============================================================================
+
+    u"""
+    A path name to a file or folder.
+
+    Handling path names more comfortable, e.g.:
+
+    * concatenate path names with the division operator ``/``
+    * call functions like *mkdir* on path names
+    * get properties like *EXISTS*
+
+    .. code-block:: python
+
+       >>> folder = fspath.FSPath("/tmp")
+       >>> folder.EXISTS
+       True
+       >>> (folder / "subfolder").makedirs()
+       >>> list(folder.reMatchFind(".*sub"))
+       ['/tmp/subfolder']
+       >>> (folder / "test.txt").FILENAME
+       'test'
+       >>> (folder / "test.txt").DIRNAME
+       '/tmp'
+       >>> six.print_("topfolder" / folder)
+       topfolder/temp
+       >>> six.print_(folder + "addedstr")
+       tmpaddedstr
+       >>> six.print_((folder/"foo"/"bar.txt").splitpath())
+       (u'/tmp/foo', u'bar.txt')
+       >>> six.print_(folder / "foo" / "../bar.txt")
+       tmp/bar.txt
+    """
+
+    def __new__(cls, pathname):
+        u"""Constructor of a path name object.
+
+        Regardless of how the encoding of the file system is, the ``pathname``
+        is converted to unicode. The conversion of byte strings is based the
+        default encodings.
+
+        To issue "File-system Encoding" See also:
+
+        * https://docs.python.org/3.5/howto/unicode.html#unicode-filenames
+        """
+        pathname = path.normpath(path.expanduser(six.text_type(pathname)))
+        return super(FSPath, cls).__new__(cls, pathname)
+
+    @property
+    def VALUE(self):
+        u"""string of the path name"""
+        return six.text_type(self)
+
+    @property
+    def EXISTS(self):
+        u"""True if file/pathname exist"""
+        return path.exists(self)
+
+    @property
+    def SIZE(self):
+        u"""Size in bytes"""
+        return path.getsize(self)
+
+    @property
+    def isReadable(self):
+        u"""True if file/path is readable"""
+        return os.access(self, os.R_OK)
+
+    @property
+    def isWriteable(self):
+        u"""True if file/path is writeable"""
+        return os.access(self, os.W_OK)
+
+    @property
+    def isExecutable(self):
+        u"""True if file is executable"""
+        return os.access(self, os.X_OK)
+
+    @property
+    def ISDIR(self):
+        u"""True if path is a folder"""
+        return path.isdir(self)
+
+    @property
+    def ISFILE(self):
+        u"""True if path is a file"""
+        return path.isfile(self)
+
+    @property
+    def ISLINK(self):
+        u"""True if path is a symbolic link"""
+        return path.islink(self)
+
+    @property
+    def DIRNAME(self):
+        u"""The path name of the folder, where the file is located
+
+        E.g.: ``/path/to/folder/filename.ext`` --> ``/path/to/folder``
+        """
+        return self.__class__(path.dirname(self))
+
+    @property
+    def BASENAME(self):
+        u"""The path name with suffix, but without the folder name.
+
+        E.g.: ``/path/to/folder/filename.ext`` --> ``filename.ext``
+        """
+        return self.__class__(path.basename(self))
+
+    @property
+    def FILENAME(self):
+        u"""The path name without folder and suffix.
+
+        E.g.: ``/path/to/folder/filename.ext`` --> ``filename``
+
+        """
+        return self.__class__(path.splitext(path.basename(self))[0])
+
+    @property
+    def SUFFIX(self):
+        u"""The filename suffix
+
+        E.g.: ``/path/to/folder/filename.ext`` --> ``.ext``
+
+        """
+        return self.__class__(path.splitext(self)[1])
+
+    @property
+    def SKIPSUFFIX(self):
+        u"""The complete file name without suffix.
+
+        E.g.: ``/path/to/folder/filename.ext`` --> ``/path/to/folder/filename``
+        """
+        return self.__class__(path.splitext(self)[0])
+
+    @property
+    def ABSPATH(self):
+        u"""The absolute pathname
+
+        E.g: ``../to/../to/folder/filename.ext`` --> 
``/path/to/folder/filename.ext``
+
+        """
+        return self.__class__(path.abspath(self))
+
+    @property
+    def REALPATH(self):
+        u"""The real pathname without symbolic links."""
+        return self.__class__(path.realpath(self))
+
+    @property
+    def POSIXPATH(self):
+        u"""The path name in *POSIX* notation.
+
+        Helpfull if you are on MS-Windows and need the POSIX name.
+        """
+        if os.sep == "/":
+            return six.text_type(self)
+        else:
+            p = six.text_type(self)
+            if p[1] == ":":
+                p = "/" + p.replace(":", "", 1)
+            return p.replace(os.sep, "/")
+
+    @property
+    def NTPATH(self):
+        u"""The path name in the Windows (NT) notation.
+        """
+        if os.sep == "\\":
+            return six.text_type(self)
+        else:
+            return six.text_type(self).replace(os.sep, "\\")
+
+    @property
+    def EXPANDVARS(self):
+        u"""Path with environment variables expanded."""
+        return self.__class__(path.expandvars(self))
+
+    @property
+    def EXPANDUSER(self):
+        u"""Path with an initial component of ~ or ~user replaced by that 
user's home."""
+        return self.__class__(path.expanduser(self))
+
+    @classmethod
+    def getHOME(cls):
+        u"""User's home folder."""
+        return cls(path.expanduser("~"))
+
+    def makedirs(self, mode=0o775):
+        u"""Recursive directory creation, default mode is 0o775 (octal)."""
+        if not self.ISDIR:
+            return os.makedirs(self, mode)
+
+    def __div__(self, pathname):
+        return self.__class__(self.VALUE + os.sep + six.text_type(pathname))
+    __truediv__ = __div__
+
+    def __rdiv__(self, pathname):
+        return self.__class__(six.text_type(pathname) + os.sep + self.VALUE)
+
+    def __add__(self, other):
+        return self.__class__(self.VALUE + six.text_type(other))
+
+    def __radd__(self, other):
+        return self.__class__(six.text_type(other) + self.VALUE)
+
+    def relpath(self, start):
+        return self.__class__(path.relpath(self, start))
+
+    def splitpath(self):
+        head, tail = path.split(self)
+        return (self.__class__(head), self.__class__(tail))
+
+    def listdir(self):
+        for name in os.listdir(self):
+            yield self.__class__(name)
+
+    def glob(self, pattern):
+        for name in  iglob(self / pattern ):
+            yield self.__class__(name)
+
+    def walk(self, topdown=True, onerror=None, followlinks=False):
+        for dirpath, dirnames, filenames in os.walk(self, topdown, onerror, 
followlinks):
+            yield (self.__class__(dirpath),
+                   [self.__class__(x) for x in dirnames],
+                   [self.__class__(x) for x in filenames] )
+
+    def reMatchFind(self, name, isFile=True, isDir=True, followlinks=False):
+
+        # find first e.g: next(myFolder.reMatchFind(r".*name.*"), None)
+
+        name_re = re.compile(name)
+        for folder, dirnames, filenames in self.walk(followlinks=followlinks):
+            if isDir:
+                for d_name in [x for x in dirnames if name_re.match(x)]:
+                    yield folder / d_name
+            if isFile:
+                for f_name in [x for x in filenames if name_re.match(x)]:
+                    yield folder / f_name
+
+    def suffix(self, newSuffix):
+        return self.__class__(self.SKIPSUFFIX + newSuffix)
+
+    def copyfile(self, dest, preserve=False):
+        u"""Copy the file src to the file or directory dest.
+
+        Argument preserve copies permission bits.
+        """
+        if preserve:
+            shutil.copy2(self, dest)
+        else:
+            shutil.copy(self, dest)
+
+    def copytree(self, dest, symlinks=False, ignore=None):
+        u"""Recursively copy the entire directory tree"""
+        shutil.copytree(self, dest, symlinks, ignore)
+
+    def move(self, dest):
+        u"""Move path to another location (dest)"""
+        shutil.move(self, dest)
+
+    def delete(self):
+        u"""remove file/folder"""
+        if self.ISDIR:
+            self.rmtree()
+        else:
+            os.remove(self)
+
+    def rmtree(self, ignore_errors=False, onerror=None):
+        u"""remove tree"""
+        shutil.rmtree(self, ignore_errors, onerror)
+
+    def openTextFile(
+            self, mode = 'r', encoding = 'utf-8'
+            , errors = 'strict', buffering = 1
+            , newline = None):
+        u"""Open file as text file"""
+        return io.open(
+            self, mode=mode, encoding=encoding
+            , errors=errors, buffering=buffering
+            , newline=newline)
+
+    def readFile(self, encoding='utf-8', errors='strict'):
+        u"""read entire file"""
+        with self.openTextFile(encoding=encoding, errors=errors) as f:
+            return f.read()
+
+    def Popen(self, *args, **kwargs):
+        u"""Get a ``subprocess.Popen`` object (``proc``).
+
+        The path name of the self-object is the programm to call. The program
+        arguments are given py ``*args`` and the ``*kwargs`` are passed to the
+        ``subprocess.Popen`` constructor. The ``universal_newlines=True`` is
+        true by default.
+
+        see https://docs.python.org/3/library/subprocess.html#popen-constructor
+
+        .. code-block:: python
+
+           import six
+           from fspath import FSPath
+           proc = FSPath("arp").Popen("-a",)
+           stdout, stderr = proc.communicate()
+           retVal = proc.returncode
+           six.print_("stdout: %s" % stdout)
+           six.print_("stderr: %s" % stderr)
+           six.print_("exit code = %d" % retVal)
+
+        """
+
+        defaults = {
+            'stdout'             : subprocess.PIPE,
+            'stderr'             : subprocess.PIPE,
+            'stdin'              : subprocess.PIPE,
+            'universal_newlines' : True
+            }
+        defaults.update(kwargs)
+        return subprocess.Popen([self,] + list(args), **defaults)
+
+
+# 
==============================================================================
+def which(fname, findall=True):
+# 
==============================================================================
+    u"""
+    Searches the fname in the enviroment ``PATH``.
+
+    This *which* is not POSIX conform, it searches the fname (without 
extension)
+    and the fname with one of the ".exe", ".cmd", ".bat" extension. If nothing
+    is found, ``None` is returned if something matches, a list (``set``) is
+    returned. With option ``findall=False`` the first match is returned or
+    ``None``, if nothing is found.
+    """
+    exe = ["", ".exe", ".cmd", ".bat"]
+    if sys.platform != 'win32':
+        exe = [""]
+    envpath = os.environ.get('PATH', None) or os.defpath
+
+    locations = set()
+    for folder in envpath.split(os.pathsep):
+        for ext in exe:
+            fullname = FSPath(folder + os.sep + fname + ext)
+            if fullname.ISFILE:
+                if not findall:
+                    return fullname
+                locations.add(fullname)
+    return locations or None
+
+# 
==============================================================================
+def callEXE(cmd, *args, **kwargs):
+# 
==============================================================================
+
+    u"""
+    Synchronous command call ``cmd`` with arguments ``*args`` .
+
+    The ``*kwargs`` are passed to the ``subprocess.Popen`` constructor. The
+    return value is a three-digit tuple ``(stdout, stderr, rc)``.
+
+    .. code-block:: python
+
+       import six
+       from fspath import callEXE
+       out, err, rc = callEXE("arp", "-a")
+
+       six.print_("stdout: %s" % out)
+       six.print_("stderr: %s" % err)
+       six.print_("exit code = %d" % rc)
+    """
+
+    exe = which(cmd, findall=False)
+    if exe is None:
+        raise IOError('command "%s" not availble!' % cmd)
+    proc = exe.Popen(*args, **kwargs)
+    stdout, stderr = proc.communicate()
+    rc = proc.returncode
+    return (stdout, stderr, rc)
+
+
+# 
==============================================================================
+class DevNull(object):  # pylint: disable=R0903
+# 
==============================================================================
+
+    """A dev/null file descriptor."""
+    def write(self, *args, **kwargs):
+        pass
+
+DevNull = DevNull()
+
+# 
==============================================================================
+class OS_ENV(dict):
+# 
==============================================================================
+
+    u"""
+    Environment object (singleton).
+
+    .. code-block:: python
+
+       >>> if OS_ENV.get("SHELL") is None:
+               OS_ENV.SHELL = "/bin/bash"
+       >>> OS_ENV.MY_NAME
+       '/bin/bash'
+    """
+    @property
+    def __dict__(self):
+        return os.environ
+
+    def __getattr__(self, attr):
+        return os.environ[attr]
+
+    def __setattr__(self, attr, val):
+        os.environ[attr] = val
+
+    def get(self, attr, default=None):
+        return os.environ.get(attr, default)
+
+OS_ENV = OS_ENV()
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-doc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to