Source code for filesysobjects.FileSysObjects

# -*- coding: utf-8 -*-
"""The FileSysObjects package provides operations on paths, path parts and side branches.

For extended additional information refer to the manuals, offline, or online at
"https://pythonhosted.org/pyfilesysobjects/".
Create manuals from sources by: "python setup.py build_sphinx"
and "python setup.py build_epydoc"

The current version calls 'os.path.normpath' by default - when 'raw' is
not selected. This is consistent for all path related parameters including
search paths: start, top, plist, spath, etc.. Thus generally clears double
slashes, but also replaces symbolic links, so later literal post processing
e.g. for match based processing should be normalized too.

There is one exception due to for leading '//' and '\\\\', see option 'ias'
and  IEEE Std 1003.1(TM), UNC, and SMB/CIFS for Pathname Resolution.
Current supported URIs for filenames are: 'file://', 'smb://', and
'cifs://'.

The following options are generic and common to multiple interfaces:

    **spath**: An existing path to be added to an entry
        from 'plist'. The following cases are supported,
        for further specifics refer to the interfaces.

        0. Independent path entry:
           spath is absolute, just added.

        1. Subpath of current directory
           spath is relative and present in
           current working directory, added
           py prefixing 'pwd'.

        2. Arbitrary side-branch of a provided path
           spath is relative, searched in plist
           for an insertion hook, added when
           found as absolute.

        3. Pattern matching - see manual 'Semi-Literals'
        and shortcut tables in manual:
           regexpr: Regular expressions are applicable for
               match on 'plist' only. Thus the part to be
               matched on the file system is required to be a
               literal.
           glob: Glob expressions are applicable on the file system
               itself only, thus the part to be matched on the
               'plist' is required to be a literal.

        4. Is absolute path:
            Is checked to be a sub path of at least one of 'plist',
            than applied.

    **start**: Start directory or file, when a file is provided the
        directory portion is used as the starting pointer.

        Each part is compared separately, but as a whole string.

    **top**: The topmost path within a directory tree as an end point
        for a search operation. This is defined by the end of
        a directory path name string. E.g. the the bottom-up search
        beginning at the start directory::

           start=/a/b/c/d/e/f/g

        is terminated by""::

           top=d

        at::

           /a/b/c/d

        This is used as a match string for processing literally
        on the parts of the provided start directory. The match
        is checked after application of
        'os.path.normpath'. Providing absolute paths still match,
        because of the string, but eventually match multiple times
        when equal sub paths exist and the match order is changed
        to bottom-up search.

        The containment of 'top' within the absolute 'start' path
        is verified.

        Each part is compared separately, but as a whole string.

    **plist**: List of strings to be searched. By default first match
        is used. Each is split into it's components and matched
        separately.

        default := sys.path

    **matchidx=#idx**: Matches on the provided index count only
        ::

           #idx==2 - ignores 0,1 and >2, matches idx==2

    **matchcnt=#num**: The maximal number of matches returned when
        multiple occur::

           #num==0 - all
           #num>0  - number of matches returned

    **matchlvl=#num**: Increment of match for top node when multiple
        are in the path. The counter starts at top, so #num(1) will
        match M(1) in::

            /a/b/M/c/M/d/M/w/e/M/bottom
                 0   1   2     3
                 |   *
                 |
                 `-default

    **matchlvlupward=#num**: Increment of match for top node when multiple
        are in the path. The counter starts from the bottom, so #num(2)
        will match M(2) in::

            /a/b/M/c/M/d/M/w/e/M/bottom
                 3   2   1     0
                     *

    **raw**: Suppress normalization by call of 'os.path.normpath'. The
        caller has than to take care for appropriate measures for
        a feasible match.

    **ias**: Treats for local file names any number of
        subsequent '/' only as one, also leading pattern '//[^/]+'.
        URI prefixes are treated correctly.

        See also "IEEE Std 1003.1(TM), 2013 Edition; Chap. 4.12".

"""
from __future__ import absolute_import
from __builtin__ import True

__author__ = 'Arno-Can Uestuensoez'
__license__ = "Artistic-License-2.0 + Forced-Fairplay-Constraints"
__copyright__ = "Copyright (C) 2010-2016 Arno-Can Uestuensoez @Ingenieurbuero Arno-Can Uestuensoez"
__version__ = '0.1.14'
__uuid__ = '9de52399-7752-4633-9fdc-66c87a9200b8'

__docformat__ = "restructuredtext en"

import os, sys
version = '{0}.{1}'.format(*sys.version_info[:2])
if not version in ('2.6','2.7',):  # pragma: no cover
    raise Exception("Requires Python-2.6.* or higher")

from types import NoneType
import re, glob
import platform

if platform.system() == 'Windows':
    # Seems to require init on Windows before import/call of pysource.
    # Missing that API call, use following dummy for now.
    import inspect
    __dummy4Init = inspect.stack()

from pysourceinfo.PySourceInfo import getCallerModuleFilePathName,getCallerModulePathName, getPythonPathRel,getCallerPathName

[docs]class FileSysObjectsException(Exception): pass
# # for test and development _mydebug = False # _mydebug = True #* # *** static compiled strings *** #* # FIXME: if os.... _CPREP = re.compile(ur"^[/\\\\]*(.*)[/\\\\]*$") # prepares split ISAPPPATHc = re.compile(ur'[/\\\\]{2}([^/\\\\]+)[/\\\\]+(.*)') # splits app part from path - IEEE-1003.1, CIFS/SMB/UNC(simple) PTYPES = ( 'SHARE', 'SMB', 'CIFS', 'SHARE', 'LDSYS', 'LFSYS', 'IAS',) """Known file pathname address types.""" # # *** checks type *** # _COMPT = re.compile(ur""" ((file:///[/\\\\]{2})()([^/\\\\]{0,1}.*)) # file and share |((smb://)()(.*)) # smb |((cifs://)()(.*)) # cifs |(([/\\\\])()([/\\\\][^/\\\\]{0,1}.*)) # share |((file://)([a-zA-Z][:])([/\\\\]*?[/\\\\]{0,1}.*)) # file and drive, could not be a share, thus nothing to ignore |((file://)[/\\\\]*?()([/\\\\]{0,1}.*)) # local file, could not be a share, thus nothing to ignore |(()([a-zA-Z][:])[/\\\\]*?([/\\\\].*)) # drive-path win, could not be a share, thus nothing to ignore |(()([a-zA-Z][:])[/\\\\]*?(.*)) # drive-path win, could not be a share, thus nothing to ignore |((ias://)()(.*)) # ias - internal type for test 'IgnoreAppSeperator' |(()()(.*)) # generic """, re.X ) """Scanner for types""" _COMPTg = [ 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, ] """Entry points into sub strings of types.""" PGTYPE = ( 'SHARE', 'SMB', 'CIFS', 'SHARE', 'LDSYS', 'LFSYS', 'LDSYS', 'LDSYS', 'IAS', 'LFSYS',) """Type labels of scanned regexpr groups.""" # # *** splits into type specifc components *** # _COMPXstr = ur""" ((file:///[/\\\\]{2})(.{0,1}[^/\\\\]+)[/\\\\]+([^/\\\\]+)[/\\\\]*(.*))# 2: ('file:///' +2SEP) +(SPECIALNODE) +varSEP +(share-name) +(path) |((smb://)([^/]{01}[^/\\\\]*)[/\\\\]+([^/\\\\]+)[/\\\\]*(.*)) # 7: ('smb://') +(SPECIALNODE) +varSEP +(share-name) +(path) |((cifs://)([^/]{01}[^/\\\\]*)[/\\\\]+([^/\\\\]+)[/\\\\]*(.*)) # 12: ('cifs://') +(SPECIALNODE) +varSEP +(share-name) +(path) |(([/\\\\]{2})([^/\\\\]+)[/\\\\]*([^/\\\\]+)[/\\\\]+(.*)) # 17: (2SEP) +(SPECIALNODE) +varSEP +(share-name) +(path) # in general a share present - on POSIX too??? |((file://)()([a-zA-Z]:)[/\\\\]*?([/\\\\]{0,1}.*)) # 22: ('file://') +() +varSEP +(drive) +(path) |((file://)()()(.*)) # 27: ('file://') +() +varSEP +() +(path) |(()()([a-zA-Z]:)[/\\\\]*?([/\\\\].*)) # 32: () +() +() +(drive) +(path) |(()()([a-zA-Z]:)[/\\\\]*?(.*)) # 37: () +() +() +(drive) +(path) |((ias://)()()(.*)) # 27: ('ias://') +() +varSEP +() +(path-for-test) |(()()()(.*)) # 42: () +() +nSEP +() +(path) """ """Scanner syntax for types of extended pathname with basic network prefix.""" _COMPX = re.compile(_COMPXstr,re.X) """Compiled scanner for types of extended pathname with basic network prefix.""" _COMPXg = [ 2, 7, 12, 17, 22, 27, 32, 37, 42, 47, ] """Entry points into sub strings of types.""" # # search path, multiple path entries _NOSEP=""" (([\\\\]["""+os.pathsep+"""][^"""+os.pathsep+"""]*)+ | ([^"""+os.pathsep+"""]*) ) """ """The tail of a path atom including possible escaped os.pathsep as ordinary character""" _DUMMY=""" (()|()) """ """Dummy for same group count for processing.""" _CSPLITPATH = re.compile( ur"""(( ((file:///[/\\\\]{2})(.{0,1}[^/\\\\]+)[/\\\\]+([^/\\\\]+)[/\\\\]*("""+_NOSEP+""")) # 4: ('file:///' +2SEP) +(SPECIALNODE) +varSEP +(share-name) +(path) |((smb://)([^/]{01}[^/\\\\]*)[/\\\\]+([^/\\\\]+)[/\\\\]*("""+_NOSEP+""")) # 12: ('smb://') +(SPECIALNODE) +varSEP +(share-name) +(path) |((cifs://)([^/]{01}[^/\\\\]*)[/\\\\]+([^/\\\\]+)[/\\\\]*("""+_NOSEP+""")) # 20: ('cifs://') +(SPECIALNODE) +varSEP +(share-name) +(path) |(([/\\\\]{2})([^/\\\\]+)[/\\\\]*([^/\\\\]+)[/\\\\]+("""+_NOSEP+""")) # 28: (2SEP) +(SPECIALNODE) +varSEP +(share-name) +(path) # in general a share present - on POSIX too??? |((file://)()([a-zA-Z]:)[/\\\\]*?([/\\\\]{0,1}"""+_NOSEP+""")) # 36: ('file://') +() +varSEP +(drive) +(path) |((file://)()()("""+_NOSEP+""")) # 44: ('file://') +() +varSEP +() +(path) |(()()([a-zA-Z]:)([/\\\\]+?"""+_NOSEP+""")) # 52: () +() +() +(drive) +(path) |(()()([a-zA-Z]:)("""+_NOSEP+""")) # 60: () +() +() +(drive) +(path) |(()()([a-zA-Z]:)("""+_DUMMY+""")) # 68: () +() +() +(drive) +() |((ias://)()()("""+_NOSEP+""")) # 76: ('ias://') +() +varSEP +() +(path-for-test) |(()()()([^"""+os.pathsep+"""]+)(()())) # 84: () +() +() +() +(path) |(()()()()(()())(?=["""+os.pathsep+"""])) # 92: () +() +() +() +() )"""+os.pathsep+"""?) # os.pathsep """, re.X ) """The main regular expression for split of PATH variables with support for URIs.""" _CSPLITPATHg = (4, 12, 20, 28, 36, 44, 52, 60, 68, 76, 84, 92,) """Helper with group indexes pointing onto the supported syntax terms.""" PGSTYPE = ( 'SHARE', 'SMB', 'CIFS', 'SHARE', 'LDSYS', 'LFSYS', 'LDSYS', 'LDSYS', 'LDSYS', 'IAS', 'LFSYS', 'LFSYS', ) """Helper with human readable enums for types of path variable elements."""
[docs]def addPathToSearchPath(spath, plist=None, **kargs): """Adds a path to 'plist'. In case of relative paths searches in provided 'plist', or 'kargs[searchplist]'a hook, when found verifies the existence within file system, in case of success adds the completed path to 'plist' the list. In case of 'glob' adds all entries. Args: spath: A path to be added to 'plist'. See common options for details. Valid scope types: * literal : X * re : - * blob : - default := caller-file-position. plist: List to for the storage, and by default search list too. See common options for details. default := sys.path **kargs: append: Append, this is equal to pos=len(plist). checkreal: Checks redundancy by resolving real path, else literally. exist: Checks whether exists, else nothing is done. pos=#pos: A specific position for insertion within range(0,len(plist)). prepend: Prepend, this is equal to pos=0. redundant: Add relative, allow redundant when same is already present. relative=<base>: Add relative sub path to provided base. searchplist: Alternative list to search for checks. Returns: When successful returns insertion position, else a 'value<0'. The insertion position in case of multiple items is the position of the last. Raises: passed through exceptions: """ if type(plist) == NoneType: plist = sys.path _splist = kargs.get('searchplist', plist) pos = 0 relative = None _exist = False _red = False _chkr = False for k, v in kargs.items(): if k == 'prepend': pos = 0 elif k == 'append': pos = -1 elif k == 'pos': if not type(v) is int: raise FileSysObjectsException("Digits required for 'pos'*" + str(pos) + ")") pos = v elif k == 'relative': relative = v.split(os.pathsep) elif k == 'exists': _exist = v elif k == 'redundant': _red = v elif k == 'checkreal': _chkr = v def _add(s): if relative: s = getPythonPathRel(s, relative) if not _red: if _chkr: for sx in map(lambda x:os.path.realpath(x), plist): if os.path.realpath(s) == sx: return else: if s in plist: return if pos == -1: plist.append(s) return len(plist) - 1 else: plist.insert(pos, s) return pos # normalize _start_elems = splitAppPrefix(spath,**kargs) spath = getAppPrefixLocalPath(_start_elems) if _exist: if os.path.isabs(spath) and os.path.exists(spath): return _add(spath) elif os.path.exists(os.path.curdir + os.sep + spath): return _add(os.path.normpath(os.path.curdir + os.sep + spath)) else: for s in _splist[:]: if os.path.exists(s + os.sep + spath): pos = _add(s + os.sep + spath) else: if os.path.isabs(spath): return _add(spath) elif os.path.exists(os.path.curdir + os.sep + spath): return _add(os.path.normpath(os.path.curdir + os.sep + spath)) else: for s in _splist[:]: if os.path.exists(s + os.sep + spath): pos = _add(s + os.sep + spath) return pos
[docs]def clearPath(plist=None, **kargs): """Clears, splits and joins a list of path variables by various criteria. Args: plist: List of paths to be cleared. See common options for details. default := sys.path **kargs: abs: Converts all entries into absolute pathnames. existent: Removes all existing items. For test and verification. ias: Treats for local file names any number of subsequent '/' only as one. See common options for details. non-existent: Removes all items which do not exist. non-redundant: Removes all items which are not redundant. Results e.g. in multiple incarnations of the same file/path type. normpath: Calls 'os.path.normpath' on each result. redundant: Clears all items from redundancies. rel: Converts all entries into relative pathnames. reverse: This reverses the resulting search order from bottom-up to top-down. Takes effect on 'redundant' only. shrink: Drops resulting empty items. split: Forces split of multiple paths items within one item into separate item entries. default := noSplit withinItemOnly: Performs any action for each item of 'plist' only. Returns: When successful returns 'True', else returns either 'False', or raises an exception. Raises: passed through exceptions: """ if plist == None: plist = sys.path _abs = False _existent = False _ias = False _links = False _ne = False _nr = False _normpath = False _redundant = False _rel = False _reverse = False _wio = False _shrink = False _split = False for k, v in kargs.items(): if k == 'abs': _abs = v elif k == 'existent': _existent = v elif k == 'ias': _ias = v elif k == 'links': _links = v elif k == 'non-existent': _ne = v elif k == 'non-redundant': _nr = v elif k == 'normpath': _normpath = v elif k == 'redundant': _redundant = v elif k == 'rel': _rel = v elif k == 'reverse': _reverse = v elif k == 'withinItemOnly': _wio = v elif k == 'shrink': _shrink = v elif k == 'split': _split = v def clearIt(px, ref=None): """the actual workhorse px: patch to process ref: reference path """ if _abs: px = os.path.abspath(px) if _existent and os.path.exists(px): return if _ne and not os.path.exists(px): return if _normpath: px = os.path.normpath(px) if _ias and px[:2] == os.sep + os.sep: px = px[1:] if _rel: px = getPythonPathRel(px, plist) return px def clrred(x): """clear redundancies""" if x in clearPath._clearlst: return clearPath._clearlst.append(x) return x # # -------------------------------------------- # if not _wio: clearPath._clearlst = [] pn = plist[:] # input list if _reverse: #revese input list pn.reverse() for p in range(len(plist)): plist.pop() # clear source for new items - in place of caller # # split items into sub items as separate new items if _split: _pn = [] for p in pn: if p: for px in p.split(os.pathsep): _pn.append(px) pn = _pn # # work out items for p in pn: # each item # within item only if _wio: clearPath._clearlst = [] pn = '' # reverse order if _reverse: plx = p.split(os.pathsep) plx.reverse() else: plx = p.split(os.pathsep) #clear redundancies for p1 in plx: if _redundant: px = clrred(clearIt(p1)) else: px = clearIt(p1) if _shrink: if px: pn += os.pathsep + px else: if px: pn += os.pathsep + px else: pn += os.pathsep if pn: pn = pn[1:] if _reverse and pn: plx = pn.split(os.pathsep) plx.reverse() pn = os.pathsep.join(plx) # shrink if _shrink: if pn: plist.append(pn) else: plist.append(pn) if _reverse: plist.reverse()
[docs]def delPathFromSearchPath(dellist, plist=None, **kargs): """Deletes a list of paths from 'plist'. Args: dellist: A list of paths to be deleted from 'plist'. Valid scope types: * literal : X * re : X * glob : X see kargs[regexpr|glob]. default := None plist: List of search paths. default := sys.path **kargs: The following keys are additional before comparison, on 'dellist' only when no match pattern is provided: case: Calls on both: os.path.normcase esc: Calls on both: escapeFilePath/unescapeFilePath exist: Calls on both: os.path.exists noexist: Calls on both: not os.path.exists normX: Calls on both: normpathX norm: Calls on both: os.path.normpath real: Calls on both: os.path.realpath regexpr|glob: Input is a list of regexpr: regular expressions, just processed by 're.match(dl,pl)' glob: process glob, and check containment in set Returns: When successful returns True, else False. Raises: passed through exceptions """ if type(plist) == NoneType: plist = sys.path if not dellist: return True _exists = False _rg = False _raw = kargs.get('raw', False) _real = kargs.get('real', False) _norm = kargs.get('norm', False) _case = kargs.get('case', False) for k, v in kargs.items(): # @UnusedVariable if k == 'exist': _exist = True _exists = True elif k == 'noexist': _exist = False _exists = True elif k == 'regexpr': _reg = True _glob = False __rg = True elif k == 'glob': _reg = False _glob = True __rg = True # seems to be sure if type(dellist) == str: dellist = [dellist] for dl in dellist: for pl in reversed(plist): if not _raw: if dl and len(dl) > 6 and dl[0:7] == 'file://': dl = os.sep + dl[7:].lstrip(os.sep) if pl and len(pl) > 6 and pl[0:7] == 'file://': pl = os.sep + pl[7:].lstrip(os.sep) if _real: if not _rg: dl = os.path.realpath(dl) pl = os.path.realpath(pl) if _norm: if not _rg: dl = os.path.normpath(dl) pl = os.path.normpath(pl) if _case: if not _rg: dl = os.path.normcase(dl) pl = os.path.normcase(pl) if _exists: if _exist: if not _rg: if not os.path.exists(pl) or not os.path.exists(dl): continue elif os.path.exists(pl): continue if _rg: if _reg: if re.match(dl, pl): plist.pop(plist.index(pl)) elif _glob: if pl in glob.glob(dl): plist.pop(plist.index(pl)) else: if dl == pl: plist.pop(plist.index(pl)) return True
[docs]def findRelPathInSearchPath(spath, plist=None, **kargs): """Searches the provided path list for existing filesystem objects 'spath'. The file system objects are searched as side-branches of the provided plist as a list of hooks(pathx[-1]) suitable to 'spath'. The path items are treated as entry point hooks for specific extension branches. The parameters of name type 'is*' are recommended when the iterator is applied. This enhances the performance vs. the post filtering. Args: spath:=(literal|glob): A path to be hooked into 'plist[]' when present. Could be either a literal, or a glob as an relative or absolute path. Valid scope types: * literal : X * re : - * glob : X See common options for details. plist: List of potential hooks for 'spath'. The following formats are provided: 1. list of single paths - used literally 2. list of search path strings - each search path is split 3. string with search path - split into it's components 4. string with a single path - used literally The default behavior is: * first: #1 * second: #3, this contains #4 The case #2 has to be forced by the key-option: 'subsplit', or to be prepared by the call 'clearPath(split=True,)'. Due to performance the case #1 should be preferred in order to save repetitive automatic conversion. See common options for further details. default := sys.path **kargs: ias: Treats for local file names any number of subsequent '/' only as one. isDir: Is a directory. isFile: Is a file. isLink: Is a symbolic link. isPathByLink: Has a symbolic link in path. matchidx=#idx: Ignore matches '< #idx', return match '== #idx'. Depends on 'reverse' default := 0 # first match noglob: Suppress application of 'glob'. not: Inverts to does not matched defined criteria. raw: Suppress normalization by call of 'os.path.normpath'. reverse: Reversed search order. subsplit: Splits each item part of a 'plist' option. Returns: When successful returns the absolute pathname, else 'None'. For a list refer to iterator. Raises: passed through exceptions: """ if not spath: return if type(plist) is list: pass elif type(plist) is NoneType: plist = sys.path elif type(plist) in (str,unicode): plist = splitPathVar(plist) else: raise FileSysObjectsException("Unknown type:"+str(plist)) raw = False ias = False _rgx = False _rev = False matchidx = 0 _chkT = False _isL = False _isD = False _isF = False _isPL = False _not = False _ng = False _ssplit = False for k, v in kargs.items(): if k == 'matchidx': if not type(v) is int or v < 0: raise FileSysObjectsException("Requires int>0 matchidx=" + str(v)) matchidx = v elif k == 'ias': ias = v elif k == 'not': _not = v elif k == 'raw': raw = v elif k == 'reverse': _rev = v elif k == 'noglob': _ng = v elif k == 'isLink': _chkT = True _isL = v elif k == 'isDir': _chkT = True _isD = v elif k == 'isFile': _chkT = True _isF = v elif k == 'isPathByLink': _chkT = True elif k == 'subsplit': _ssplit = True else: raise FileSysObjectsException("Unknown param: " + str(k) + ":" + str(v)) if _ssplit: # split sub paths, but do not alter the callers source plist = plist[:] clearPath(plist,split=True) # use canonical copy if not raw: if spath[-1] == os.sep: sp = os.path.normpath(spath) + os.sep else: sp = os.path.normpath(spath) if ias: sp = os.sep + sp.lstrip(os.sep) if spath and len(spath) > 6 and spath[0:7] == 'file://': _sp = os.sep + spath[7:].lstrip(os.sep) else: _sp = spath[:] def _checkit(p): _b = True if _chkT: if _isF and not os.path.isfile(p): _b = False elif _isD and not os.path.isdir(p): _b = False elif _isL and not os.path.islink(p): _b = False elif _isPL and not os.path.isfile(p): _b = False return _b # short it up for absolute input of existing paths, thus is a literal too! if os.path.isabs(_sp) and os.path.exists(_sp): # exists as absolute _b = _checkit(_sp) for p in plist: if not p: continue if p.startswith(_sp): _b &= True if _b and matchidx != 0: _b = False matchidx -= 1 return os.path.normpath(_sp) if _b and not _not: return _sp return None # now look for hooks of relative paths in plist if _rev: _pl = reversed(plist) else: _pl = plist for p in _pl: if not p: continue _b = True if os.path.isabs(_sp): _px = normpathX(_sp) else: _px = normpathX(os.path.abspath(p + os.sep + _sp)) if os.path.exists(_px): _b = _checkit(_px) if _b and matchidx != 0: _b = False matchidx -= 1 if _b and not _not: return _px continue if not _ng: # try a glob for gm in glob.glob(_px): _b = _checkit(gm) if _b and matchidx != 0: _b = False matchidx -= 1 if _b and not _not: return gm continue return None
[docs]def findRelPathInSearchPathIter(spath, plist=None, **kargs): """Iterates all matches in plist, see findRelPathInSearchPath. """ if type(plist) == NoneType: plist = sys.path for pl in plist: #TODO: matchidx = 0 kargs['matchidx'] = matchidx while True: r = findRelPathInSearchPath(spath, [pl], **kargs) if r: yield r kargs['matchidx'] += 1 else: break pass
[docs]def getHome(): """Gets home directory with complete local file path name, eventually drive. """ if sys.platform in ('win32',): return os.environ['HOMEDRIVE']+os.environ['HOMEPATH'] elif sys.platform in ('linux2', 'cygwin', 'darwin', ): return os.environ['HOME'] else: # eventually may not yet work if not unix return os.environ['HOME']
[docs]def getTopFromPathString(spath, plist=None, **kargs): """Searches for a partial path in search paths from a provided list. Searches for a given path component by various constraints on each string of provided 'plist' until the match of a break condition. The match is performed by default left-to-right, which results in top-down scan of a path hierarchy, or right-to-left as an upward bottom-up search. Performs string operations only, the file system is neither checked, not utilized. Args: spath: A path to be added to 'plist'. See common options for details. plist: List of search strings for match. See common options for details. default := sys.path **kargs: abs: Return absolute path. hook: Returns the found part of the 'plist' entry only. ias: Treats for local file names any number of subsequent '/' only as one. includeapp: Includes application specifics into search. E.g. input : '//hostname/a/hostame/x/y/z' not set => '//hostname/a/hostame' set => '//hostname' matchidx=#idx: Ignore matches '< #idx', return match '== #idx'. default := 0 # first match matchlvl=#num: Increment of match for top node when multiple are in the path. See common options for details. matchlvlupward=#num: Increment of match for top node when multiple are in the path. See common options for details. pattern: Scope and type of match pattern. literal: Literal node by node match regnode: Match regular expression for individual nodes, implies no contained 'os.sep' and no 'os.pathsep' patternlvl: Defines the level of sub nodes of the search path to be resolved. The value 'full' forces a full match of 'spath' within a 'plist' item. raw: Suppress normalization by call of 'os.path.normpath'. reverse: This reverses the resulting search order from bottom-up to top-down. Takes effect on 'redundant' only. split: Returns the split path prefix matched on search list, and the relative sub path outside search list. Returns: When successful returns a path, else None. Returns by default the expanded pathname including the searched. Raises: passed through exceptions: """ if type(plist) == NoneType: plist = sys.path elif type(plist) != list: raise FileSysObjectsException("Requires list argument:" + str(plist)) _rev = False raw = False # ias = False # incap = False _abs= 0 _hook= False _pat = 0 _patlvl = 0 matchlvl = 0 matchlvlupward = -1 matchidx = 0 for k, v in kargs.items(): if k == 'matchidx': if not type(v) is int or v < 0: raise FileSysObjectsException("Requires int>0 matchidx=" + str(v)) matchidx = v elif k == 'matchlvl': if not type(v) is int or v < 0: raise FileSysObjectsException("Requires int>0 matchlvl=" + str(v)) matchlvl = v matchlvlupward = -1 elif k == 'matchlvlupward': if not type(v) is int or v < 0: raise FileSysObjectsException("Requires int>0 matchlvlupward=" + str(v)) matchlvl = -1 matchlvlupward = v elif k == 'ias': ias = True incap = False elif k == 'includeapp': incap = True ias = False elif k == 'raw': raw = True elif k == 'reverse': _rev = True elif k == 'abs': _abs = True elif k == 'hook': _hook = True _split = False elif k == 'split': _hook = False _split = True elif k == 'patternlvl': if not v == 'full' and ( not type(v) is int or v < 0 ): raise FileSysObjectsException("Requires int>0 patternlvl=" + str(v)) _patlvl = v elif k == 'pattern': if v == 'literal': _pat = 0 elif v == 'regnode': _pat = 1 # elif v == 'all': # _pat = 2 else: raise FileSysObjectsException("Unknown option: " + str(k) + ":" + str(v)) def _comp(p, b): # if _pat == 2: # return a == b if _pat == 1: if p == '*': # assume a glob expression, thus terminate re-processing now return pc = re.compile(ur'^' + p + ur'$') px = pc.match(b) if px: return px.string[px.start():px.end()] elif _pat == 0: if p == b: return b # define processed portion, save prefix for later prepend on result if not raw: _rtype, _host, _share, sp = splitAppPrefix(spath, **{'rtype':True}) _app_prefix = _rtype + _host if _share: if _app_prefix: _app_prefix += os.sep + _share else: _app_prefix = _share else: _app_prefix, sp = '', spath # normalize _sp_elems = splitAppPrefix(sp,**kargs) sp= getAppPrefixLocalPath(_sp_elems) sp = sp.split(os.sep) if sp and sp[-1] == '': sp = sp[:-1] # FIXME: if sp and sp[0] == '': # is @root if len(sp) > 1: _contained = False for cx in plist: # initially check anchor #FIXME: for multiple-path-entries _cxe = splitAppPrefix(cx,**kargs) _cxe= getAppPrefixLocalPath(_sp_elems) if _cxe.startswith(os.sep+sp[1]): _contained = True if not _contained : return None sp = sp[1:] if _patlvl == 'full': _patlvl = len(sp)-1 si0 = -1 if _rev: _pl = reversed(plist) else: _pl = plist for sl in _pl: # scan each path for sp si0 += 1 if not sl: continue if not raw: # canonical # manage app paths - current network only _rtype, _host, _share, sl = splitAppPrefix(sl, **{'rtype':True}) _prefix = _rtype + _host if _share: if _prefix: _prefix += os.sep + _share else: _prefix = _share if _app_prefix: if _app_prefix != _prefix: continue # _prefix += _share else: _prefix = '' s = sl.split(os.sep) if s[0] == '': s = s[1:] if s[-1] == '': s = s[:-1] if matchlvlupward > -1: # count reversed within the path nodes, this is not the index _len = len(s) si = len(s) _fin = False _ucnt = 0 for sx in reversed(s): # check each dir separate si -= 1 _match = False _fin = False if si == 0 and sp[0] == '': # root dir _c = sx else: _c = _comp(sp[0], sx) m = -1 if _c: for spx in sp: m += 1 if si + m >= _len: _fin = True break _c = _comp(spx, s[si + m]) if si + m < _len and m < len(sp) and _c: _match = True if m <= len(sp) - 1: _fin = True else: if _match: _fin = True break if _fin: if _ucnt < matchlvlupward: _ucnt += 1 continue else: _ucnt = 0 if _fin and m >= _patlvl: # full match in front of pos m if _hook: _spx = '' else: if _c and m == len(sp) - 1: _spx = [_c] else: _spx = sp[m:] if matchidx == 0: _r = _prefix if os.sep.join(s[:si + m]): if _r or os.path.isabs(sl): _r += os.sep _r += os.sep.join(s[:si + m]) if _spx: if _r: _r += os.sep _r += os.sep.join(_spx) if not os.path.isabs(sl) and _abs: return os.path.abspath(_r) return _r matchidx -= 1 else: # if matchlvl > -1: si = -1 _fin = False _len = len(s) _dcnt = 0 for sx in s: # check each dir separate si += 1 _match = False _fin = False if si == 0 and sp[0] == '': # root dir _c = sx else: _c = _comp(sp[0], sx) if _c: _match = True _fin = len(sp) <= 1 m = 1 for spx in sp[1:]: if si + m >= _len: _fin = True _c = '' break _c = _comp(spx, s[si + m]) if si + m < _len and m < len(sp) and _c: _match = True if m >= len(sp) - 1: _fin = True else: if _match: _fin = True break m += 1 if _fin: if _dcnt < matchlvl: _dcnt += 1 continue else: _dcnt = 0 if _fin and m >= _patlvl: # full match in front of pos m if _hook: _spx = '' else: if _c and m == len(sp) - 1: _spx = [_c] else: _spx = sp[m:] if matchidx == 0: _r = _prefix _j = os.sep.join(s[:si + m]) if _j: if _r or os.path.isabs(sl): _r += os.sep _r += _j if _spx: if _r: _r += os.sep _r += os.sep.join(_spx) if not os.path.isabs(sl) and _abs: return os.path.abspath(_r) return _r matchidx -= 1 return None
[docs]def getTopFromPathStringIter(spath, plist=None, **kargs): """Iterates all matches in plist,see getTopFromPathString. """ if type(plist) == NoneType: plist = sys.path for pl in plist: r = getTopFromPathString(spath, [pl], **kargs) if r: yield r pass
[docs]def getDirUserData(): """Gets data directory with complete local file path name, eventually drive. """ if sys.platform in ('win32','cygwin', ): return os.environ['LOCALAPPDATA'] elif sys.platform in ('linux2',): return os.environ['HOME'] elif sys.platform in ('darwin', ): return os.environ['HOME'] else: # eventually may not yet work if not unix return os.environ['HOME']
[docs]def getDirUserConfigData(): """Gets data directory for configuration. """ if sys.platform in ('win32','cygwin',): return os.environ['LOCALAPPDATA'] elif sys.platform in ('linux2',): return os.environ['HOME']+os.sep+'.config' elif sys.platform in ('darwin', ): return os.environ['HOME'] else: # eventually may not yet work if not unix return os.environ['HOME']
[docs]def getDirUserAppData(): """Gets data directory for applications. """ if sys.platform in ('win32','cygwin',): return os.environ['APPDATA'] elif sys.platform in ('linux2',): return os.environ['HOME']+os.sep+'.local/share' elif sys.platform in ('darwin', ): return os.environ['HOME'] else: # eventually may not yet work if not unix return os.environ['HOME']
[docs]def setUpperTreeSearchPath(start=None, top=None, plist=None, **kargs): """Extends the 'plist' based search by each subdirectory from 'start' on upward to 'top'. Prepends a set of search paths into plist. The set of search paths contains of each directory beginning with provided start position. The inserted path is normalized by default Args: start: Start components of a path string. See common options for details. Valid scope types: * literal : X * re : - * blob : - default := caller-file-position. top: End component of a path string. The node 'top' is included. Valid scope types: * literal : X * re : - * blob : - default := <same-as-start> plist: List to for the storage. See common options for details. default := sys.path **kargs: append: Appends the set of search paths. ias: Treats for local file names any number of subsequent '/' only as one. matchidx=#idx: Ignore matches '< #idx', adds match '== #idx' and returns. default := 0 # all matchcnt=#num: The maximal number of matches returned when multiple occur. matchlvl=#num: Increment of match for top node when multiple are in the path. See common options for details. matchlvlupward=#num: Increment of match for top node when multiple are in the path. See common options for details. noTypeCheck: Suppress required identical types of 'top' and 'start'. As a rule of thumb for current version, the search component has to be less restrictive typed than the searched. The default applicable type matches are:: top ¦ start --------+--------------------- LFSYS ¦ LFSYS, LDSYS, SHARE | SMB, CIFS, IAS LDSYS ¦ LDSYS SHARE ¦ SHARE SMB ¦ SMB CIFS ¦ CIFS IAS ¦ IAS See common options for details. prepend: Prepends the set of search paths. This is default. raw: Suppress normalization by call of 'os.path.normpath'. relonly: The paths are inserted relative to the top node only. This is mainly for test purposes. The intermix of relative and absolute path entries is not verified. reverse: This reverses the resulting search order from bottom-up to top-down. unique: Insert non-present only, else present entries are not checked, thus the search order is changed in general for 'prepend', while for 'append' the present still covers the new entry. Returns: When successful returns 'True', else returns either 'False', or raises an exception. Raises: passed through exceptions: """ if type(plist) == NoneType: plist = sys.path _relo = False _matchcnt = 0 _matchidx = 0 setUpperTreeSearchPath._matchcnt = 0 setUpperTreeSearchPath._matchidx = 0 matchlvl = 0 matchlvlupward = -1 reverse = False unique = False prepend = True _tchk= True _raw = False _ias = False _split = False _sitem = False for k, v in kargs.items(): if k == 'relonly': _relo = True elif k == 'matchcnt': if not type(v) is int: raise FileSysObjectsException("Digits only matchcnt:" + str(v)) _matchcnt = v elif k == 'matchidx': if not type(v) is int: raise FileSysObjectsException("Digits only matchidx:" + str(v)) _matchidx = v elif k == 'matchlvl': if not type(v) is int: raise FileSysObjectsException("Digits only matchlvl:" + str(v)) matchlvl = v elif k == 'matchlvlupward': if not type(v) is int: raise FileSysObjectsException("Digits only matchlvlupward:" + str(v)) matchlvlupward = v elif k == 'reverse': reverse = True elif k == 'unique': unique = True elif k == 'append': prepend = False elif k == 'prepend': prepend = True elif k == 'raw': _raw = True elif k == 'ias': _ias = True elif k == 'splitItems': _split = True elif k == 'singleitem': _sitem = True elif k == 'noTypeCheck': _tchk = False if matchlvl > 0: matchlvlupward = -1 # # Prepare search path list # if decided to normalize, and whether to ignore leading '//' # if _raw: # match basically literally if not _ias: _plst = plist else: _plst = [] for i in plist: # normalize _elems = splitAppPrefix(i,**kargs) _plst.append(getAppPrefixLocalPath(_elems)) else: # normalize for safer match conditions _plst = [] for i in plist: # normalize _elems = splitAppPrefix(i,**kargs) _plst.append(getAppPrefixLocalPath(_elems)) # # 0. prep start dir # if start == '': raise FileSysObjectsException("Empty start:''") elif start == None: start = getCallerModuleFilePathName(2) # caller file # normalize _start_elems = splitAppPrefix(start,**kargs) start = getAppPrefixLocalPath(_start_elems) # try a literal if not os.path.isabs(start): start = getCallerPathName(2)+os.sep+start if os.path.isfile(start): start = os.path.dirname(start) # we need dir if not os.path.exists(start): raise FileSysObjectsException("Missing start:" + str(start)) # 1. prep top dir # normalize if top == '': raise FileSysObjectsException("Empty top:''") elif top == None: top = getCallerModulePathName(2) # caller file # normalize _top_elems = list(splitAppPrefix(top,**kargs)) # ptype if _tchk: if _top_elems and _start_elems: if _top_elems[0] != _start_elems[0]: #TODO: still to enhance.. if _top_elems[0] in ( 'LFSYS', ): if os.path.realpath(_top_elems[3]): pass elif _start_elems[0] in ( 'LDSYS', 'LFSYS', ): pass else: raise FileSysObjectsException("LFSYS combined with "+str(_start_elems[0])+" requires relative pathname for LFSYS, given: "+str(_top_elems[3])) pass else: raise FileSysObjectsException("This version requires compatible types: start("+str(_start_elems[0])+") =! top("+str(_top_elems[0])+")") # share # if _top_elems[2]: # if _top_elems[2] != _start_elems[2]: # raise FileSysObjectsException("Top is not in start:" + str(_top_elems[2]) + " != " + str(_top_elems[2])) # else: # _top_elems[2] = _start_elems[2] # node # if _top_elems[1]: # if _top_elems[1] != _start_elems[1]: # raise FileSysObjectsException("Top is not in start:" + str(_top_elems[1]) + " != " + str(_top_elems[1])) # else: # _top_elems[1] = _start_elems[1] # path # if not os.path.isabs(_top_elems[3]): # kargs['patternlvl'] = 'full' # _top_elems[3] = os.path.splitdrive(getTopFromPathString(_top_elems[3], [start],**kargs))[1] # # _top_elems[3] = os.path.splitdrive(getTopFromPathString(_top_elems[3], [start],**{'patternlvl':'full',}))[1] # if not _top_elems[1] and not _top_elems[2] and not os.path.isabs(_top_elems[3]): # top = _top_elems[3] # else: # top= getAppPrefixLocalPath(_top_elems) top= getAppPrefixLocalPath(_top_elems) # if absolute if os.path.isabs(top): if not os.path.exists(top): raise FileSysObjectsException("Top does not exist:" + str(top)) # # start upward recursion now # def _addsub(x, pl=plist): """...same for all.""" # >3: nonlocal _matchcnt if _matchcnt != 0 and _matchcnt <= setUpperTreeSearchPath._matchcnt: return if _matchidx != 0 and _matchidx != setUpperTreeSearchPath._matchidx: return if unique and x in pl or x in plist: return False if reverse: pl.append(x) else: pl.insert(0, x) setUpperTreeSearchPath._matchcnt += 1 pass # find top if top: # FIXME: if top == '.': top = os.path.abspath(top) top = escapeFilePath(top) start = escapeFilePath(start) _sx = re.sub(_CPREP, r"\1", top) # for now works literally a = start.split(top) if len(a) == 1: raise FileSysObjectsException("Top is not in start:" + str(top)) if matchlvl >= len(a): # check valid range raise FileSysObjectsException("Match count out of range:" + str(matchlvl) + ">" + str(len(a))) elif matchlvlupward > 0 and matchlvlupward >= len(a): # check valid range raise FileSysObjectsException("Match count out of range:" + str(matchlvlupward) + ">" + str(len(a))) else: if matchlvl > 0: raise FileSysObjectsException("Match count out of range:" + str(matchlvl) + "> 0") if matchlvlupward > 0: raise FileSysObjectsException("Match count out of range:" + str(matchlvlupward) + "> 0") _addsub(start) return True # # so we have actually at least one top within valid range and a remaining sub-path - let us start # if a == ['', '']: # top == start if matchlvl > 0: raise FileSysObjectsException("Match count out of range:" + str(matchlvl) + "> 0") if matchlvlupward > 0: raise FileSysObjectsException("Match count out of range:" + str(matchlvl) + "> 0") _addsub(start) return True elif a[0] == '': # top is prefix _tpath = top if matchlvlupward >= 0: mcnt = len(a) - 1 - matchlvlupward else: mcnt = matchlvl _spath = top.join(a[mcnt + 1:]) # sub-path for search recursion else: # get index for requested number of ignored/contained matches if matchlvlupward >= 0: mcnt = len(a) - 1 - matchlvlupward else: mcnt = matchlvl + 1 # set matched prefix and postfix if os.path.isabs(top): _tpath = top _spath = (os.sep + top + os.sep).join(a[mcnt:]) # sub-path for search recursion elif not a[mcnt-1]: # tail _tpath = (os.sep + top + os.sep).join(a[:mcnt]) _spath = '' else: _tpath = (os.sep + top + os.sep).join(a[:mcnt]) + os.sep + top # top path as search hook _spath = (os.sep + top + os.sep).join(a[mcnt:]) # sub-path for search recursion if _relo: # relative paths, mainly for test curp = '' else: curp = unescapeFilePath(_tpath) curp = os.path.normpath(curp) if curp not in plist: # insert top itself _addsub(curp) a = _spath.split(os.sep) if prepend: for p in a: if not p: continue curp = os.path.join(curp, p) _addsub(curp) else: _buf = [] for p in a: if not p: continue curp = os.path.join(curp, p) _addsub(curp, _buf) plist.extend(_buf) return True
[docs]def splitAppPrefix(apstr, **kargs): """Splits application prefix from a single resource-path - IEEE-1003.1/SMB/CIFS. For a search path containing mulltiple single-path entries refer to 'splitPathVar' `[see] <#splitpathvar>`_. The supported application type prefixes are as follows, for detailed information refer to the chapter **Syntax Elements** `[details] <path_syntax.html#syntax-elements>`_ :: PREFIXKEY := ( 'file:///' + 2SEP + SPECIALNODE + varSEP + share-name | 'file://' | 'smb://' + SPECIALNODE + varSEP + share-name | 2SEP SPECIALNODE [varSEP] | nSEP ) SPECIALNODE := ( <networknode> ) share-name := ( 1*80pchar # see [MS-DTYP] ) Args: apstr: A path containing an application part. **kargs: ias: This option is foreseen for test purposes. Ignore application separator, this normalizes the pathname by eliminating the PREFIXKEY property. This results in consequence into:: file://///hostname/share/a/b => /hostname/share/a/b where the pathname is created without any application recognition on the base of:: /////hostname/share/a/b => /hostname/share/a/b raw: Displays the type prefix as provided raw sub string - see 'rtype' - and suppress any normalization of the resulting 'pathname' sub string. rtype: Displays the type prefix as provided raw sub string. tpf: Target platform for the file pathname, for details refer to 'normpathX' `[see normpathX] <#normpathx>`_. Returns: When split successful returns a tuple containing: :: (TYPE, host-name, share-name, pathname) TYPE := (RAW|CIFS|SMB|SHARE|LFSYS|LDSYS) SMB := ('file:///'+2SEP|'smb://') SHARE := 2SEP LFSYS := ('file://'|'') LDSYS := [a-z]':' host-name =: (host-name|'') share-name := (valid-share-name|'') valid-share-name := ( raw | smb-share-name | cifs-share-name | win-drive-share-name | win-drive-os | win-special-share-name ) pathname := "pathname on target" else: :: (LFSYS, '', '', apstr) REMARK: The hostname may contain in current release any suboption, but is not tested with options at all. Raises: passed through exceptions: """ ias = kargs.get('ias', False) raw = kargs.get('raw', False) rtype = kargs.get('rtype', False) if not raw: if ias: _cg = _COMPT.match(apstr) if _cg: return ('IAS', '', _cg.group(_cg.lastindex+2), normpathX(_cg.group(_cg.lastindex+3),**kargs),) for i in _COMPX.finditer(apstr): # #FIXME: i.lastindex # for g in _COMPXg: if i.group(g + 3) or i.group(g): # 3:local file system 0:uri or IEEE/UNC/SMB pass elif i.group(g + 2): # 2:DOS-DRIVE - only if not rtype: return (PGTYPE[(g - 2) / 5], i.group(g + 1), i.group(g + 2), '') else: continue # should not occur, anyhow... if not rtype: return (PGTYPE[(g - 2) / 5], i.group(g + 1), i.group(g + 2), normpathX(i.group(g + 3),**kargs)) else: return (i.group(g), i.group(g + 1), i.group(g + 2), normpathX(i.group(g + 3),**kargs)) return None else: if ias: _cg = _COMPT.match(apstr) if _cg: return ('IAS', '', _cg.group(_cg.lastindex+2), _cg.group(_cg.lastindex+3),) for i in _COMPX.finditer(apstr): for g in _COMPXg: if i.group(g + 3) or i.group(g): # 3:local file system / 0:uri or IEEE/UNC/SMB return (i.group(g), i.group(g + 1), i.group(g + 2), i.group(g + 3)) return None return ('', apstr)
[docs]def splitPathVar(pathvar, **kargs): """Splits PATH variables which may include URI type prefixes into a list of single path entries. The default behavior is to split a search path into a list of contained path entries. E.g:: p = 'file:///a/b/c:/d/e::x/y:smb://host/share/q/w' Is split into: :: px = [ 'file:///a/b/c', '/d/e', '', 'x/y', 'smb://host/share/q/w', ] With 'appsplit' :: px = [ ('LFSYS' ,'', '', '/a/b/c'), ('LFSYS' ,'', '', '/d/e'), ('LFSYS' ,'', '', ''), ('LFSYS' ,'', '', 'x/y'), ('SMB' ,'host', 'share', 'q/w'), ] The supported application type prefixes are the same as by 'splitAppPrefix' `[see] <#splitappprefix>`_. For reserved prefix keywords parts of the name may be escaped in order to avoid filtering when required. Args: pathvar: A search path compatible to 'splitAppPrefix' `[see] <#splitappprefix>`_. **kargs: The provided key-options are also passed through transparently to 'normpathX' `[see] <#normpathx>`_ if not 'raw'. appsplit: Splits into tuples of application entries. For further details refer to 'splitAppPrefix' `[see] <#splitappprefix>`_. appsplit = (True|False) ias: This option is foreseen for test purposes. For further details refer to 'splitAppPrefix' `[see] <#splitappprefix>`_. ias = (True|False) raw: Displays a list of unaltered split path items, superposes 'rpath' and 'rtype'. raw = (True|False) rpath: Displays the path as provided raw sub string. For further details refer to 'splitAppPrefix' `[see] <#splitappprefix>`_. rpath = (True|False) rtype: Displays the type prefix as provided raw sub string. For further details refer to 'splitAppPrefix' `[see] <#splitappprefix>`_. rtype = (True|False) tpf: Target platform for the file pathname, for details refer to 'normpathX' `[see normpathX] <#normpathx>`_. Returns: When split successful returns a list of tuples: :: appsplit == False [ <pathvar-item>, ... ] appsplit == True [ (TYPE, host-name, share-name, pathname), ... ] The tuple contains: :: (TYPE, host-name, share-name, pathname) TYPE := (RAW|CIFS|SMB|SHARE|LFSYS|LDSYS) RAW := (<raw-pathvar-item>) CIFS := ('cifs://') SMB := ('file:///'+2SEP|'smb://') SHARE := 2SEP LFSYS := ('file://'|'') LDSYS := [a-z]':' host-name := (host-name|'') share-name := (valid-share-name|'') valid-share-name := ( smb-share-name | cifs-share-name | win-drive-share-name | win-drive-os | win-special-share-name ) pathname := "pathname on target" specials: ias := ('IAS', '', '', ias-pathvar-item) raw := ('RAW', '', '', raw-pathvar-item) rpath := (TYPE, host-name, share-name, raw-pathname>) rtype := (raw-type, host-name, share-name, pathname) rtype + rpath := (raw-type, host-name, share-name, raw-pathname>) else: :: (LFSYS, '', '', apstr) REMARK: The hostname may contain in current release any suboption, but is not tested with options at all. Refer also to 'splitAppPrefix' `[see] <#splitappprefix>`_. Raises: passed through exceptions: """ appsplit = kargs.get('appsplit', False) #: control application tuples, else ordinary path ias = kargs.get('ias', False) #: control IAS raw = kargs.get('raw', False) #: control RAW rpath = kargs.get('rpath', False) #: control RAW-PATH rtype = kargs.get('rtype', False) #: control RAW-TYPE # # prep appsplit # if not appsplit: if ias: raise FileSysObjectsException("IAS requires 'appsplit'") if not raw: rtype = True # prep format if ias and raw: raise FileSysObjectsException("Options are supported EXOR: IAS exor RAW") ret = [] #: collect result g = 0 #: preserve for final analysis outside the loop def getraw(i,g): """Get raw string. Args: i: iterator g: current group Returns: (s,e): for sub string """ # get start string position without APP-PREFIX s = i.start(g+1) if s == -1: s = i.start(g+2) if s == -1: s = i.start(g+3) # get end string position e = i.end(g+3) if e == -1: e = i.end(g+2) if e == -1: e = i.end(g+1) return (s,e,) if not pathvar: # shortcut for empty input - None and 0/length-string if not appsplit: return [] if ias: return [('IAS', '', '', '')] if raw: return [('RAW', '', '', '')] if rtype or (rpath and rtype): return [('', '', '', '')] else: return [('LFSYS', '', '', '')] for i in _CSPLITPATH.finditer(pathvar): for g in _CSPLITPATHg: # do this for getting the syntax term if i.start(g) == -1: # if there is a match at all continue if g == 92: # shortcut for empty group == empty path entry if ias: ret.append(('IAS', '', '', '')) elif raw : # raw prio higher ret.append(('RAW', '', '', '')) elif rtype or (rpath and rtype): ret.append(('', '', '', '')) elif rpath: ret.append(('LFSYS', '', '', '')) else: ret.append(('LFSYS', '', '', '')) break if ias: # ignore application separator if i.group(g + 3) or i.group(g): # 4:local file system / 0:uri or IEEE/UNC/SMB pass elif i.group(g + 2): # 2:DOS-DRIVE - only pass else: continue # should not occur, anyhow... __r='' if i.group(g+1): __r += os.sep+i.group(g+1) if i.group(g+2): if i.group(g+1) != '': __r += os.sep+i.group(g+2) # share is present see path for drive /<drive>/path else: __r += i.group(g+2) # share may not be present, else in path /<drive>/path if i.group(g+3): if rpath: s,e = getraw(i,g) if -1 in (s,e,): ret.append(('', '', '', '')) # should not occur break # now get the raw string if s > 0 and pathvar[s-1] == os.sep: __r += os.sep+pathvar[s-1:e] else: __r += os.sep+pathvar[s:e] else: if __r: __r += os.sep+i.group(g+3) else: __r += i.group(g+3) else: # here: 0,1,2,3 => empty group ret.append(('IAS', '', '', __r)) # shortcut break - ignore 0 break if rtype and rpath: ret.append((i.group(g), '', '', __r)) elif rtype: ret.append((i.group(g), '', '', normpathX(__r,**kargs))) elif rpath: ret.append(('IAS', '', '', __r)) else: ret.append(('IAS', '', '', normpathX(__r,**kargs))) break # # was not IAS - before - spare the 'else' # if i.group(g + 3) or i.group(g): # 4:local file system / 0:uri or IEEE/UNC/SMB pass elif i.group(g + 2): # 2:DOS-DRIVE - only if raw: ret.append(('RAW', '', '', pathvar[i.start(g):i.start(g+3)])) elif rtype and rpath: ret.append((i.group(g), i.group(g + 1), i.group(g + 2), '')) elif rpath: ret.append((PGSTYPE[(g - 4) / 8], i.group(g + 1), i.group(g + 2), '')) elif rtype: ret.append((i.group(g), i.group(g + 1), i.group(g + 2), '')) else: ret.append((PGSTYPE[(g - 4) / 8], i.group(g + 1), i.group(g + 2), '')) break elif not i.group(g + 1): # here: 0,1,2,3 => empty group ret.append(('LFSYS', '', '', '')) break else: continue # should not occur, anyhow... if raw: ret.append(('RAW', '', '', pathvar[i.start(g):i.end(g+3)])) elif rtype and rpath: ret.append((i.group(g), i.group(g + 1), i.group(g + 2), i.group(g+3))) elif rpath: ret.append((PGSTYPE[(g - 4 ) / 8 ], i.group(g + 1), i.group(g + 2), i.group(g+3))) elif rtype: ret.append((i.group(g), i.group(g + 1), i.group(g + 2), normpathX(i.group(g + 3),**kargs))) else: ret.append((PGSTYPE[(g - 4 ) / 8 ], i.group(g + 1), i.group(g + 2), normpathX(i.group(g + 3),**kargs))) #* #* the case empty group regexpr at the end of the search path else complicates the regexpr at all #* if i.end(g-1) != -1 and i.end(g-1) < len(pathvar): # empty group at the end of string if ias: ret.append(('IAS', '', '', '')) elif raw: ret.append(('RAW', '', '', '')) elif rtype: ret.append(('', '', '', '')) else: ret.append(('LFSYS', '', '', '')) if not appsplit: # ordinary pathsplit _ret = [] if raw: _ret = map(lambda x: x[3],ret) else: # expects 'rtype', and valid entries for r in ret: _r = '' if r[1]: _r += os.path.sep + r[1] if r[2]: if r[0] in ("LDSYS", "") and not r[1]: # r[1] should be empty _r += r[2] else: # r[1] mus not be empty (!?) _r += os.path.sep + r[2] if r[3]: if r[0] in ("LDSYS", ""): # drive only _r += r[3] else: if r[1] and r[2]: #share _r += os.sep+r[3] else: _r += r[3] _r = os.pathsep + _r _ret.append(_r[1:]) return _ret return ret
[docs]def getAppPrefixLocalPath(elems,**kargs): """Joins app elements to a path for local access. Args: elems: Elements as provided by 'splitAppPrefix' **kargs: tpf: Target platform for the file pathname, for details refer to 'normpathX' `[see normpathX] <#normpathx>`_. Returns: When access path when successful, else None. Raises: passed through exceptions: """ _tpf = kargs.get('tpf',False) if _tpf in ('Windows', 'win',): s = '\\' elif _tpf == 'posix': s = '/' else: s = os.sep ret = '' if elems and elems[1]: if not elems[2]: if platform.system() == 'Windows' and elems[0] not in ('SMB','SHARE'): raise FileSysObjectsException("Missing share name for start="+str(elems)) ret += 2*s+elems[1] if elems and elems[2]: if elems[1]: ret += s+elems[2] else: ret += elems[2] if elems and ret: if ( elems[1] or elems[2] ) and elems[3][0] not in ('/','\\',os.sep): ret += s+elems[3] else: ret += elems[3] elif elems: ret += elems[3] return ret
#*** # Static 're' components # # match pattern for path names - could be applied on any OS, # but required mandatory on MS-Windows # alist= [ # escapes path names r'((?<![\\\\])[\\\\]*([\a]))', # controls of 're' and others r'((?<![\\\\])[\\\\]*([\b]))', #4 r'((?<![\\\\])[\\\\]*([\f]))', r'((?<![\\\\])[\\\\]*([\n]))', r'((?<![\\\\])[\\\\]*([\r]))', #10 r'((?<![\\\\])[\\\\]*([\t]))', r'((?<![\\\\])[\\\\]*([\v]))', r'(^(file://///)(?![/\\\\]))', # some URIs, suppress slash reduction r'(^(file:///[\\][\\])(?![/\\\\]))', # some URIs, suppress slash reduction r'(^(file://[\\][\\])(?![/\\\\]))', #20 some URIs, suppress slash reduction r'(^(file://))', r'(^(smb://))', #24 r'(^(cifs://))', r'(^(ias://))', # special, proprietary for test purposes r'(^([\\\\][\\\\])(?![\\\\]))', #30 os.sep on win r'((^[\\\\]+)$)', r'(([\\\\]+[.][\\\\]+))', r'(([\\\\]+[.]$))', r'((^[.][\\\\]+))', r'((?<=[a-zA-Z]:)([\\\\]+)$)', #40 r'((?<![\\\\])([\\\\]+)$)', r'((?<![\\\\])([\\\\][\\\\])(?![\\\\]))', r'((?<![\\\\])([\\\\]+)(?![\\\\]))', r'(^([\\\\])(?![\\\\]))', r'(^(//)(?!/))', #50 os.sep on Linux, UNIX, OS-X r'(^(/)$)', r'(([^/]+/+\.\./*))', r'((?<=[^/])(/+[^/]+/+\.\.))', r'(([^/]+/+\.\./+))', r'((?=[^.]*)(\.\./+))', #60 r'((/+[.])+/*$)', r'((/+[.]/+))', r'((?=[^.]*)(\./+))', r'(^(///+)(?!/))', r'((?<=[a-zA-Z]:)(/+)$)', #70 r'((/+)(?:$))', #72 r'((/+)(?![/]))', #74 ] _rx = re.compile('|'.join(alist)) """Regexpr scanner for 'escapeFilePath', also used in 'normpathX'.""" # # map matches to actual control sequences # DOIT = 11111 # out of range ASCII_CTRL = { 2 : '\a', 4 : '\b', 6 : '\f', 8 : '\n', 10 : '\r', 12 : '\t', 14 : '\v', 16 : r'file://///', 18 : r'file:///\\', 20 : r'file://\\', 22 : 'file://', 24 : 'smb://', 26 : 'cifs://', 28 : 'ias://', 30: '\\\\', 32 : DOIT, 34 : DOIT, 36 : DOIT, 38 : DOIT, 40 : DOIT, 42 : DOIT, 44 : '\\\\', 46 : DOIT, 48 : '\\', 50 : DOIT, 52 : DOIT, 54 : DOIT, 56 : DOIT, 58 : DOIT, 60 : DOIT, 62 : DOIT, 64 : DOIT, 66 : DOIT, 68 : DOIT, 70 : DOIT, 72 : DOIT, 74 : DOIT, } # # map matches to appropriate replacement # #* #* *** cross native posix - slash *** emulates normpath on win for remote posix #* ASCII_REPLACE_CNP = { 2 : r'/a', 4 : r'/b', 6 : r'/f', 8 : r'/n', 10 : r'/r', 12 : r'/t', 14 : r'/v', 16 : 'file://///', 18 : 'file://///', 20 : 'file://///', 22 : 'file://', 24 : 'smb://', 26 : 'cifs://', 28 : 'ias://', 30 : r'//', 32 : r'/', 34 : r'/', 36 : r'/', 38 : r'/', 40 : '\\', 42 : '', 44 : r'/', 46 : r'/', 48 : r'/', 50 : '//', 52 : '/', 54 : '', 56 : '', 58 : '', 60 : '/', 62 : '', 64 : '/', 66 : '', 68 : '/', 70 : '', 72: '', 74 : '/', } #* #* *** cross native win - backslash *** emulates normpath on posix for remote win #* ASCII_REPLACE_CNW = { 2 : r'\\a', 4 : r'\\b', 6 : r'\\f', 8 : r'\\n', 10 : r'\\r', 12 : r'\\t', 14 : r'\\v', 16 : r'file://///', 18 : r'file:///\\\\', 20 : r'file://\\\\', 22 : 'file://', 24 : 'smb://', 26 : 'cifs://', 28 : 'ias://', 30 : r'\\\\', 32 : r'\\', 34 : r'\\', 36 : r'\\', 38 : r'\\', 40 : r'\\', 42 : '', 44 : r'\\', 46 : r'\\', 48 : r'\\', 50 : r'\\\\', 52 : r'\\', 54 : r'', 56 : r'', 58 : r'', 60 : r'\\', 62 : r'', 64 : r'\\', 66 : r'', 68 : r'\\', 70 : '\\', 72 : '', 74 : r'\\', } #* #* *** keep *** #* ASCII_REPLACE_K = { 2 : r'\\a', 4 : r'\\b', 6 : r'\\f', 8 : r'\\n', 10 : r'\\r', 12 : r'\\t', 14 : r'\\v', 16 : r'file://///', 18 : r'file:///\\\\', 20 : r'file://\\\\', 22 : 'file://', 24 : 'smb://', 26 : 'cifs://', 28 : 'ias://', 30 : r'\\\\', 32 : r'\\', 34 : r'\\', 36 : r'\\', 38 : r'\\', 40 : r'\\', 42 : r'\\', 44 : r'\\', 46 : r'\\', 48 : r'\\', 50 : '//', 52 : '/', 54 : '', 56 : '', 58 : '', 60 : '/', 62 : '', 64 : '/', 66 : '', 68 : '/', 70 : '/', 72 : '/', 74 : '/', } #* #* *** replace with backslash *** #* ASCII_REPLACE_B = { 2 : r'\\a', 4 : r'\\b', 6 : r'\\f', 8 : r'\\n', 10 : r'\\r', 12 : r'\\t', 14 : r'\\v', 16 : r'file://///', 18 : r'file:///\\\\', 20 : r'file://\\\\', 22 : 'file://', 24 : 'smb://', 26 : 'cifs://', 28 : 'ias://', 30 : r'\\\\', 32 : r'\\', 34 : r'\\', 36 : r'\\', 38 : r'\\', 40 : r'\\', 42 : '', 44 : r'\\', 46 : r'\\', 48 : r'\\', 50 : r'\\\\', 52 : r'\\', 54 : r'', 56 : r'', 58 : r'', 60 : r'\\', 62 : r'', 64 : r'\\', 66 : r'', 68 : r'\\', 70 : r'\\', 72 : '', 74 : r'\\', } #* #* *** replace with slash *** #* ASCII_REPLACE_S= { 2 : r'/a', 4 : r'/b', 6 : r'/f', 8 : r'/n', 10 : r'/r', 12 : r'/t', 14 : r'/v', 16 : 'file://///', 18 : 'file://///', 20 : 'file://///', 22 : 'file://', 24 : 'smb://', 26 : 'cifs://', 28 : 'ias://', 30 : r'//', 32 : r'/', 34 : r'/', 36 : r'/', 38 : r'/', 40 : r'/', 42 : '', 44 : r'/', 46 : r'/', 48 : r'/', 50 : '//', 52 : '/', 54 : '', 56 : '', 58 : '', 60 : '/', 62 : '', 64 : '/', 66 : '', 68 : '/', 70 : '/', 72: '', 74 : '/', } #* #* *** normalize mixed separators for posix/windows *** #* #FIXME: ffs. MIXED_SEP = re.compile(ur"""(/[\\\\]{2})|([\\\\]{2}/)""") def sub_esc_keep(it): """To be used by re.sub() - keeps mixed """ g = it.lastindex+1 if it.group(g): x = it.group(g) c = ASCII_CTRL.get(g) # evtl. drop these in release if c == DOIT or c == it.group(g): # evtl. drop these in release x = ASCII_REPLACE_K[g] return x def sub_esc_b(it): """To be used by re.sub() - converts to backslash """ g = it.lastindex+1 if it.group(g): x = it.group(g) c = ASCII_CTRL.get(g) # evtl. drop these in release if c == DOIT or c == it.group(g): # evtl. drop these in release x = ASCII_REPLACE_B[g] return x def sub_esc_s(it): """To be used by re.sub() - converts to slash """ g = it.lastindex+1 if it.group(g): x = it.group(g) c = ASCII_CTRL.get(g) # evtl. drop these in release if c == DOIT or c == it.group(g): # evtl. drop these in release x = ASCII_REPLACE_S[g] return x def sub_esc_cnw(it): """To be used by re.sub() - converts to slash """ g = it.lastindex+1 if it.group(g): x = it.group(g) c = ASCII_CTRL.get(g) # evtl. drop these in release if c == DOIT or c == it.group(g): # evtl. drop these in release x = ASCII_REPLACE_CNW[g] return x def sub_esc_cnp(it): """To be used by re.sub() - converts to slash """ g = it.lastindex+1 if it.group(g): x = it.group(g) c = ASCII_CTRL.get(g) # evtl. drop these in release if c == DOIT or c == it.group(g): # evtl. drop these in release x = ASCII_REPLACE_CNP[g] return x
[docs]def escapeFilePath(p,tps=None): """Normalize 'os.sep' by re module. Considers any os.sep seperated item as path part. Manages os.sep including flags of 're' as special characters,e.g. 'a', 'b', and 'x'. Args: p: A path. tps: Target path seperator: s: slash b: backslash k: keep keeps, also intermixed, just escapes backslash cnp: cross native posix Emulates native behavior of 'os.path.normpath' on posix platform(Linux, MacOS, ...), e.g.:: d:/ => d: d:\\ => d:\\ cnw:cross native win Emulates native behavior of 'os.path.normpath' on win platform, e.g.:: d:/ => d:\\ d:\\ => d:\\ else: adapt os.sep, local os native Returns: Path. """ if tps not in ('s','b','k', 'cnw', 'cnp', ): if platform.system() == 'Windows': #FIXME: p = MIXED_SEP.sub("\\\\", p) return _rx.sub(sub_esc_b, p) else: #FIXME: p = MIXED_SEP.sub("//", p) return _rx.sub(sub_esc_s, p) if tps == 's': #FIXME: p = MIXED_SEP.sub("//", p) return _rx.sub(sub_esc_s, p) elif tps == 'b': #FIXME: p = MIXED_SEP.sub("\\\\", p) return _rx.sub(sub_esc_b, p) elif tps == 'k': return _rx.sub(sub_esc_keep, p) elif tps == 'cnw': #FIXME: p = MIXED_SEP.sub("\\\\", p) return _rx.sub(sub_esc_cnw, p) elif tps == 'cnp': #FIXME: p = MIXED_SEP.sub("//", p) return _rx.sub(sub_esc_cnp, p) # just for the paranoid... return _rx.sub(sub_esc_keep, p)
#*** #* Static 're' components for table driven re-map #* # match pattern for path names - could be applied on any OS, # but required mandatory on MS-Windows # blist= [ r'((?<![\\\\])¦(?=^)([\\\\][\\\\][a]))', #2 r'((?<![\\\\])¦(?=^)([\\\\][\\\\][b]))', r'((?<![\\\\])¦(?=^)([\\\\][\\\\][f]))', r'((?<![\\\\])¦(?=^)([\\\\][\\\\][n]))', r'((?<![\\\\])¦(?=^)([\\\\][\\\\][r]))', #10 r'((?<![\\\\])¦(?=^)([\\\\][\\\\][t]))', r'((?<![\\\\])¦(?=^)([\\\\][\\\\][v]))', r'(^(file:///[\\][\\][\\][\\])(?![/\\\\]))', # some URIs, suppress slash reduction r'(^(file://[\\][\\][\\][\\])(?![/\\\\]))', #18 # some URIs, suppress slash reduction r'(^([\\\\][\\\\][\\\\][\\\\])(?![\\\\]))', #20 r'(^([\\\\][\\\\])(?![\\\\]))', r'((?<![\\\\])([\\\\][\\\\][\\\\][\\\\])$)', r'((?<![\\\\])([\\\\][\\\\][\\\\][\\\\])(?![\\\\]))', r'((?<![\\\\])([\\\\][\\\\])$)', r'((?<![\\\\])([\\\\][\\\\])(?![\\\\]))', #30 ] _ry = re.compile('|'.join(blist)) # # map matches to actual control sequences # ASCII_ESC = { 2 : r'\\a', 4 : r'\\b', 6 : r'\\f', 8 : r'\\n', 10 : r'\\r', 12 : r'\\t', 14 : r'\\v', 16 : r'file:///\\\\', 18 : r'file://\\\\', 20 : r'\\\\', 22: r'\\', 24 : r'\\\\', 26 : r'\\\\', 28 : r'\\', 30 : r'\\', } # # map matches to appropriate replacement # ASCII_DEESC = { 2 : '\a', 4 : '\b', 6 : '\f', 8 : '\n', 10 : '\r', 12 : '\t', 14 : '\v', 16 : r'file:///\\', 18 : r'file://\\', 20 : r'\\', 22 : '\\', 24: r'\\', 26 : '\\', 28 : '\\', 30 : '\\', } def sub_unesc(it): """To be used by re.sub() """ g = it.lastindex+1 if it.group(g): x = it.group(g) c = ASCII_ESC.get(g) # evtl. drop these in release if c == it.group(g): # evtl. drop these in release x = ASCII_DEESC[g] return x
[docs]def unescapeFilePath(p): """Unescape 'os.pathsep' by re module. Considers any os.pathsep as path part. Manages os.pathsep only - thus flags of the re module itself are not considered and may omitted in regular expressions for pathnames, e.g. 'a', 'b', and 'x'. Args: p: A path. Returns: Path. """ return _ry.sub(sub_unesc, p)
[docs]def normpathX(p,**kargs): """Normalize path similar to os.path.normpath() function, but with optional extensions. This provides a simple interface for preparing valid pathnames across multiple platforms. The 'normpathX' processes a single path, when multiple are required either split them before or use 'splitPathVar' to do. Considers all os.sep and os.pathsep of supported OS as valid path characters and treats them as reserved characters. In advance of os.path.normpath this supports the following simple URIs for file system paths: smb, cifs, file `[see normpathX] <#splitappprefix>`_. The normpathX is in particular e.g. aware of special characters of the 're' module like '\\\\a' and '\\\\b'. These are masked by the *escapeFilePath* and *unescapeFilePath* functions. Args: p: A single path entry - no valid 'os.pathsep'. In case of required search path including semantic 'os.pathsep' use 'splitPathVar()'. **kargs: tpf: Target platform for the file pathname. Due to some deviations from the expected behavior in case of cross platform development the following options are defined: +-----------+------------+----------------------------------------------------------+ | tpf | compatible | behaviour | +===========+============+==========================================================+ | cnp | to POSIX | as os.path.normpath() on POSIX OS, transforms to '/' | +-----------+------------+----------------------------------------------------------+ | cnw | to Win | as os.path.normpath() on Windows OS, transforms to '\\\\' | +-----------+------------+----------------------------------------------------------+ | keep | n.a. | keeps literal | +-----------+------------+----------------------------------------------------------+ | local | yes | compatible to local os.path.normpath() | +-----------+------------+----------------------------------------------------------+ | posix | no | keeps all separators as '/' | +-----------+------------+----------------------------------------------------------+ | win | no | keeps all separators as '\\\\' | +-----------+------------+----------------------------------------------------------+ | <default> | no | adapts 'win' or 'posix' to local os | +-----------+------------+----------------------------------------------------------+ **cnp**: cross native posix Emulates compatible native behavior of 'os.path.normpath' on POSIX platforms(Linux, MacOS, ...), e.g.:: d:/ => d: d:\\ => d:\\ On POSIX OS:: Xposix = os.path.normpath(x) On POSIX OS:: Xposix == filesysobjects.FileSysObjects(x,**{'tpf':'local'}) Xposix == filesysobjects.FileSysObjects(x,**{'tpf':'cnp'}) On Windows OS:: Xposix == filesysobjects.FileSysObjects(x,**{'tpf':'cnp'}) **cnw**:cross native win Emulates compatible native behavior of 'os.path.normpath' on Windows platforms, e.g.:: d:/ => d:\\ d:\\ => d:\\ On Windows OS:: Xwin = os.path.normpath(x) On Windows OS:: Xwin == filesysobjects.FileSysObjects(x,**{'tpf':'local'}) Xwin == filesysobjects.FileSysObjects(x,**{'tpf':'cnw'}) On POSIX OS:: Xwin == filesysobjects.FileSysObjects(x,**{'tpf':'cnw'}) **keep**: Keeps as provided, also intermixed styles of os.path.sep and os.pathsep, just escapes '\\'. The user is responsible for adaption to the local file system interfaces. **local**: Compatible to local 'os.path.normpath()'. **posix**: POSIX style POSIX based style with os.sep = '/'. E.g. Linux, MacOS, BSD, Solaris, etc.:: d:/ => d:/ d:\\ => d:/ This mode is literally compatible across all supported platforms. **win**: Windows style MS-Windows style with os.sep= '\\\\'.:: d:/ => d:\\ d:\\ => d:\\ This mode is literally compatible across all supported platforms. **else**: Adapt 'os.path.sep' and 'os.pathsep' to local native os, else wise the same behavior as the modes 'posix' or 'win'. This mode is in term of the structure including drives of Windows based file systems compatible across all supported platforms. But the os.path.sep, and the os.pathsep are adapted to the local platform. Returns: Normalized path. """ tpf = kargs.get('tpf',platform.system()) if tpf in ('Windows', 'win',): p = escapeFilePath(p,'b') p = unescapeFilePath(p) elif tpf == 'posix': p = escapeFilePath(p,'s') p = unescapeFilePath(p) elif tpf == 'keep': p = escapeFilePath(p,'k') p = unescapeFilePath(p) elif tpf == 'local': #FIXME: # p = escapeFilePath(p,'cnw') # p = unescapeFilePath(p) p = os.path.normpath(p) elif tpf == 'cnw': p = escapeFilePath(p,'cnw') p = unescapeFilePath(p) elif tpf == 'cnp': p = escapeFilePath(p,'cnp') p = unescapeFilePath(p) else: p = escapeFilePath(p,) p = unescapeFilePath(p) return p