1#
2# SOURCE: https://pypi.python.org/pypi/path.py
3# VERSION: 8.2.1
4# -----------------------------------------------------------------------------
5# Copyright (c) 2010 Mikhail Gusarov
6#
7# Permission is hereby granted, free of charge, to any person obtaining a copy
8# of this software and associated documentation files (the "Software"), to deal
9# in the Software without restriction, including without limitation the rights
10# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11# copies of the Software, and to permit persons to whom the Software is
12# furnished to do so, subject to the following conditions:
13#
14# The above copyright notice and this permission notice shall be included in
15# all copies or substantial portions of the Software.
16#
17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
24#
25
26"""
27path.py - An object representing a path to a file or directory.
28
29https://github.com/jaraco/path.py
30
31Example::
32
33    from path import Path
34    d = Path('/home/guido/bin')
35    for f in d.files('*.py'):
36        f.chmod(0o755)
37"""
38
39from __future__ import unicode_literals
40
41import sys
42import warnings
43import os
44import fnmatch
45import glob
46import shutil
47import codecs
48import hashlib
49import errno
50import tempfile
51import functools
52import operator
53import re
54import contextlib
55import io
56from distutils import dir_util
57import importlib
58
59try:
60    import win32security
61except ImportError:
62    pass
63
64try:
65    import pwd
66except ImportError:
67    pass
68
69try:
70    import grp
71except ImportError:
72    pass
73
74##############################################################################
75# Python 2/3 support
76PY3 = sys.version_info >= (3,)
77PY2 = not PY3
78
79string_types = str,
80text_type = str
81getcwdu = os.getcwd
82
83def surrogate_escape(error):
84    """
85    Simulate the Python 3 ``surrogateescape`` handler, but for Python 2 only.
86    """
87    chars = error.object[error.start:error.end]
88    assert len(chars) == 1
89    val = ord(chars)
90    val += 0xdc00
91    return __builtin__.unichr(val), error.end
92
93if PY2:
94    import __builtin__
95    string_types = __builtin__.basestring,
96    text_type = __builtin__.unicode
97    getcwdu = os.getcwdu
98    codecs.register_error('surrogateescape', surrogate_escape)
99
100@contextlib.contextmanager
101def io_error_compat():
102    try:
103        yield
104    except IOError as io_err:
105        # On Python 2, io.open raises IOError; transform to OSError for
106        # future compatibility.
107        os_err = OSError(*io_err.args)
108        os_err.filename = getattr(io_err, 'filename', None)
109        raise os_err
110
111##############################################################################
112
113__all__ = ['Path', 'CaseInsensitivePattern']
114
115
116LINESEPS = ['\r\n', '\r', '\n']
117U_LINESEPS = LINESEPS + ['\u0085', '\u2028', '\u2029']
118NEWLINE = re.compile('|'.join(LINESEPS))
119U_NEWLINE = re.compile('|'.join(U_LINESEPS))
120NL_END = re.compile(r'(?:{0})$'.format(NEWLINE.pattern))
121U_NL_END = re.compile(r'(?:{0})$'.format(U_NEWLINE.pattern))
122
123
124try:
125    import pkg_resources
126    __version__ = pkg_resources.require('path.py')[0].version
127except Exception:
128    __version__ = '8.2.1'   # XXX-MODIFIED-WAS: 'unknown'
129
130
131class TreeWalkWarning(Warning):
132    pass
133
134
135# from jaraco.functools
136def compose(*funcs):
137    compose_two = lambda f1, f2: lambda *args, **kwargs: f1(f2(*args, **kwargs))
138    return functools.reduce(compose_two, funcs)
139
140
141def simple_cache(func):
142    """
143    Save results for the :meth:'path.using_module' classmethod.
144    When Python 3.2 is available, use functools.lru_cache instead.
145    """
146    saved_results = {}
147
148    def wrapper(cls, module):
149        if module in saved_results:
150            return saved_results[module]
151        saved_results[module] = func(cls, module)
152        return saved_results[module]
153    return wrapper
154
155
156class ClassProperty(property):
157    def __get__(self, cls, owner):
158        return self.fget.__get__(None, owner)()
159
160
161class multimethod(object):
162    """
163    Acts like a classmethod when invoked from the class and like an
164    instancemethod when invoked from the instance.
165    """
166    def __init__(self, func):
167        self.func = func
168
169    def __get__(self, instance, owner):
170        return (
171            functools.partial(self.func, owner) if instance is None
172            else functools.partial(self.func, owner, instance)
173        )
174
175
176class Path(text_type):
177    """
178    Represents a filesystem path.
179
180    For documentation on individual methods, consult their
181    counterparts in :mod:`os.path`.
182
183    Some methods are additionally included from :mod:`shutil`.
184    The functions are linked directly into the class namespace
185    such that they will be bound to the Path instance. For example,
186    ``Path(src).copy(target)`` is equivalent to
187    ``shutil.copy(src, target)``. Therefore, when referencing
188    the docs for these methods, assume `src` references `self`,
189    the Path instance.
190    """
191
192    module = os.path
193    """ The path module to use for path operations.
194
195    .. seealso:: :mod:`os.path`
196    """
197
198    def __init__(self, other=''):
199        if other is None:
200            raise TypeError("Invalid initial value for path: None")
201
202    @classmethod
203    @simple_cache
204    def using_module(cls, module):
205        subclass_name = cls.__name__ + '_' + module.__name__
206        if PY2:
207            subclass_name = str(subclass_name)
208        bases = (cls,)
209        ns = {'module': module}
210        return type(subclass_name, bases, ns)
211
212    @ClassProperty
213    @classmethod
214    def _next_class(cls):
215        """
216        What class should be used to construct new instances from this class
217        """
218        return cls
219
220    @classmethod
221    def _always_unicode(cls, path):
222        """
223        Ensure the path as retrieved from a Python API, such as :func:`os.listdir`,
224        is a proper Unicode string.
225        """
226        if PY3 or isinstance(path, text_type):
227            return path
228        return path.decode(sys.getfilesystemencoding(), 'surrogateescape')
229
230    # --- Special Python methods.
231
232    def __repr__(self):
233        return '%s(%s)' % (type(self).__name__, super(Path, self).__repr__())
234
235    # Adding a Path and a string yields a Path.
236    def __add__(self, more):
237        try:
238            return self._next_class(super(Path, self).__add__(more))
239        except TypeError:  # Python bug
240            return NotImplemented
241
242    def __radd__(self, other):
243        if not isinstance(other, string_types):
244            return NotImplemented
245        return self._next_class(other.__add__(self))
246
247    # The / operator joins Paths.
248    def __div__(self, rel):
249        """ fp.__div__(rel) == fp / rel == fp.joinpath(rel)
250
251        Join two path components, adding a separator character if
252        needed.
253
254        .. seealso:: :func:`os.path.join`
255        """
256        return self._next_class(self.module.join(self, rel))
257
258    # Make the / operator work even when true division is enabled.
259    __truediv__ = __div__
260
261    # The / operator joins Paths the other way around
262    def __rdiv__(self, rel):
263        """ fp.__rdiv__(rel) == rel / fp
264
265        Join two path components, adding a separator character if
266        needed.
267
268        .. seealso:: :func:`os.path.join`
269        """
270        return self._next_class(self.module.join(rel, self))
271
272    # Make the / operator work even when true division is enabled.
273    __rtruediv__ = __rdiv__
274
275    def __enter__(self):
276        self._old_dir = self.getcwd()
277        os.chdir(self)
278        return self
279
280    def __exit__(self, *_):
281        os.chdir(self._old_dir)
282
283    @classmethod
284    def getcwd(cls):
285        """ Return the current working directory as a path object.
286
287        .. seealso:: :func:`os.getcwdu`
288        """
289        return cls(getcwdu())
290
291    #
292    # --- Operations on Path strings.
293
294    def abspath(self):
295        """ .. seealso:: :func:`os.path.abspath` """
296        return self._next_class(self.module.abspath(self))
297
298    def normcase(self):
299        """ .. seealso:: :func:`os.path.normcase` """
300        return self._next_class(self.module.normcase(self))
301
302    def normpath(self):
303        """ .. seealso:: :func:`os.path.normpath` """
304        return self._next_class(self.module.normpath(self))
305
306    def realpath(self):
307        """ .. seealso:: :func:`os.path.realpath` """
308        return self._next_class(self.module.realpath(self))
309
310    def expanduser(self):
311        """ .. seealso:: :func:`os.path.expanduser` """
312        return self._next_class(self.module.expanduser(self))
313
314    def expandvars(self):
315        """ .. seealso:: :func:`os.path.expandvars` """
316        return self._next_class(self.module.expandvars(self))
317
318    def dirname(self):
319        """ .. seealso:: :attr:`parent`, :func:`os.path.dirname` """
320        return self._next_class(self.module.dirname(self))
321
322    def basename(self):
323        """ .. seealso:: :attr:`name`, :func:`os.path.basename` """
324        return self._next_class(self.module.basename(self))
325
326    def expand(self):
327        """ Clean up a filename by calling :meth:`expandvars()`,
328        :meth:`expanduser()`, and :meth:`normpath()` on it.
329
330        This is commonly everything needed to clean up a filename
331        read from a configuration file, for example.
332        """
333        return self.expandvars().expanduser().normpath()
334
335    @property
336    def namebase(self):
337        """ The same as :meth:`name`, but with one file extension stripped off.
338
339        For example,
340        ``Path('/home/guido/python.tar.gz').name == 'python.tar.gz'``,
341        but
342        ``Path('/home/guido/python.tar.gz').namebase == 'python.tar'``.
343        """
344        base, ext = self.module.splitext(self.name)
345        return base
346
347    @property
348    def ext(self):
349        """ The file extension, for example ``'.py'``. """
350        f, ext = self.module.splitext(self)
351        return ext
352
353    @property
354    def drive(self):
355        """ The drive specifier, for example ``'C:'``.
356
357        This is always empty on systems that don't use drive specifiers.
358        """
359        drive, r = self.module.splitdrive(self)
360        return self._next_class(drive)
361
362    parent = property(
363        dirname, None, None,
364        """ This path's parent directory, as a new Path object.
365
366        For example,
367        ``Path('/usr/local/lib/libpython.so').parent ==
368        Path('/usr/local/lib')``
369
370        .. seealso:: :meth:`dirname`, :func:`os.path.dirname`
371        """)
372
373    name = property(
374        basename, None, None,
375        """ The name of this file or directory without the full path.
376
377        For example,
378        ``Path('/usr/local/lib/libpython.so').name == 'libpython.so'``
379
380        .. seealso:: :meth:`basename`, :func:`os.path.basename`
381        """)
382
383    def splitpath(self):
384        """ p.splitpath() -> Return ``(p.parent, p.name)``.
385
386        .. seealso:: :attr:`parent`, :attr:`name`, :func:`os.path.split`
387        """
388        parent, child = self.module.split(self)
389        return self._next_class(parent), child
390
391    def splitdrive(self):
392        """ p.splitdrive() -> Return ``(p.drive, <the rest of p>)``.
393
394        Split the drive specifier from this path.  If there is
395        no drive specifier, :samp:`{p.drive}` is empty, so the return value
396        is simply ``(Path(''), p)``.  This is always the case on Unix.
397
398        .. seealso:: :func:`os.path.splitdrive`
399        """
400        drive, rel = self.module.splitdrive(self)
401        return self._next_class(drive), rel
402
403    def splitext(self):
404        """ p.splitext() -> Return ``(p.stripext(), p.ext)``.
405
406        Split the filename extension from this path and return
407        the two parts.  Either part may be empty.
408
409        The extension is everything from ``'.'`` to the end of the
410        last path segment.  This has the property that if
411        ``(a, b) == p.splitext()``, then ``a + b == p``.
412
413        .. seealso:: :func:`os.path.splitext`
414        """
415        filename, ext = self.module.splitext(self)
416        return self._next_class(filename), ext
417
418    def stripext(self):
419        """ p.stripext() -> Remove one file extension from the path.
420
421        For example, ``Path('/home/guido/python.tar.gz').stripext()``
422        returns ``Path('/home/guido/python.tar')``.
423        """
424        return self.splitext()[0]
425
426    def splitunc(self):
427        """ .. seealso:: :func:`os.path.splitunc` """
428        unc, rest = self.module.splitunc(self)
429        return self._next_class(unc), rest
430
431    @property
432    def uncshare(self):
433        """
434        The UNC mount point for this path.
435        This is empty for paths on local drives.
436        """
437        unc, r = self.module.splitunc(self)
438        return self._next_class(unc)
439
440    @multimethod
441    def joinpath(cls, first, *others):
442        """
443        Join first to zero or more :class:`Path` components, adding a separator
444        character (:samp:`{first}.module.sep`) if needed.  Returns a new instance of
445        :samp:`{first}._next_class`.
446
447        .. seealso:: :func:`os.path.join`
448        """
449        if not isinstance(first, cls):
450            first = cls(first)
451        return first._next_class(first.module.join(first, *others))
452
453    def splitall(self):
454        r""" Return a list of the path components in this path.
455
456        The first item in the list will be a Path.  Its value will be
457        either :data:`os.curdir`, :data:`os.pardir`, empty, or the root
458        directory of this path (for example, ``'/'`` or ``'C:\\'``).  The
459        other items in the list will be strings.
460
461        ``path.Path.joinpath(*result)`` will yield the original path.
462        """
463        parts = []
464        loc = self
465        while loc != os.curdir and loc != os.pardir:
466            prev = loc
467            loc, child = prev.splitpath()
468            if loc == prev:
469                break
470            parts.append(child)
471        parts.append(loc)
472        parts.reverse()
473        return parts
474
475    def relpath(self, start='.'):
476        """ Return this path as a relative path,
477        based from `start`, which defaults to the current working directory.
478        """
479        cwd = self._next_class(start)
480        return cwd.relpathto(self)
481
482    def relpathto(self, dest):
483        """ Return a relative path from `self` to `dest`.
484
485        If there is no relative path from `self` to `dest`, for example if
486        they reside on different drives in Windows, then this returns
487        ``dest.abspath()``.
488        """
489        origin = self.abspath()
490        dest = self._next_class(dest).abspath()
491
492        orig_list = origin.normcase().splitall()
493        # Don't normcase dest!  We want to preserve the case.
494        dest_list = dest.splitall()
495
496        if orig_list[0] != self.module.normcase(dest_list[0]):
497            # Can't get here from there.
498            return dest
499
500        # Find the location where the two paths start to differ.
501        i = 0
502        for start_seg, dest_seg in zip(orig_list, dest_list):
503            if start_seg != self.module.normcase(dest_seg):
504                break
505            i += 1
506
507        # Now i is the point where the two paths diverge.
508        # Need a certain number of "os.pardir"s to work up
509        # from the origin to the point of divergence.
510        segments = [os.pardir] * (len(orig_list) - i)
511        # Need to add the diverging part of dest_list.
512        segments += dest_list[i:]
513        if len(segments) == 0:
514            # If they happen to be identical, use os.curdir.
515            relpath = os.curdir
516        else:
517            relpath = self.module.join(*segments)
518        return self._next_class(relpath)
519
520    # --- Listing, searching, walking, and matching
521
522    def listdir(self, pattern=None):
523        """ D.listdir() -> List of items in this directory.
524
525        Use :meth:`files` or :meth:`dirs` instead if you want a listing
526        of just files or just subdirectories.
527
528        The elements of the list are Path objects.
529
530        With the optional `pattern` argument, this only lists
531        items whose names match the given pattern.
532
533        .. seealso:: :meth:`files`, :meth:`dirs`
534        """
535        if pattern is None:
536            pattern = '*'
537        return [
538            self / child
539            for child in map(self._always_unicode, os.listdir(self))
540            if self._next_class(child).fnmatch(pattern)
541        ]
542
543    def dirs(self, pattern=None):
544        """ D.dirs() -> List of this directory's subdirectories.
545
546        The elements of the list are Path objects.
547        This does not walk recursively into subdirectories
548        (but see :meth:`walkdirs`).
549
550        With the optional `pattern` argument, this only lists
551        directories whose names match the given pattern.  For
552        example, ``d.dirs('build-*')``.
553        """
554        return [p for p in self.listdir(pattern) if p.isdir()]
555
556    def files(self, pattern=None):
557        """ D.files() -> List of the files in this directory.
558
559        The elements of the list are Path objects.
560        This does not walk into subdirectories (see :meth:`walkfiles`).
561
562        With the optional `pattern` argument, this only lists files
563        whose names match the given pattern.  For example,
564        ``d.files('*.pyc')``.
565        """
566
567        return [p for p in self.listdir(pattern) if p.isfile()]
568
569    def walk(self, pattern=None, errors='strict'):
570        """ D.walk() -> iterator over files and subdirs, recursively.
571
572        The iterator yields Path objects naming each child item of
573        this directory and its descendants.  This requires that
574        ``D.isdir()``.
575
576        This performs a depth-first traversal of the directory tree.
577        Each directory is returned just before all its children.
578
579        The `errors=` keyword argument controls behavior when an
580        error occurs.  The default is ``'strict'``, which causes an
581        exception.  Other allowed values are ``'warn'`` (which
582        reports the error via :func:`warnings.warn()`), and ``'ignore'``.
583        `errors` may also be an arbitrary callable taking a msg parameter.
584        """
585        class Handlers:
586            def strict(msg):
587                raise
588
589            def warn(msg):
590                warnings.warn(msg, TreeWalkWarning)
591
592            def ignore(msg):
593                pass
594
595        if not callable(errors) and errors not in vars(Handlers):
596            raise ValueError("invalid errors parameter")
597        errors = vars(Handlers).get(errors, errors)
598
599        try:
600            childList = self.listdir()
601        except Exception:
602            exc = sys.exc_info()[1]
603            tmpl = "Unable to list directory '%(self)s': %(exc)s"
604            msg = tmpl % locals()
605            errors(msg)
606            return
607
608        for child in childList:
609            if pattern is None or child.fnmatch(pattern):
610                yield child
611            try:
612                isdir = child.isdir()
613            except Exception:
614                exc = sys.exc_info()[1]
615                tmpl = "Unable to access '%(child)s': %(exc)s"
616                msg = tmpl % locals()
617                errors(msg)
618                isdir = False
619
620            if isdir:
621                for item in child.walk(pattern, errors):
622                    yield item
623
624    def walkdirs(self, pattern=None, errors='strict'):
625        """ D.walkdirs() -> iterator over subdirs, recursively.
626
627        With the optional `pattern` argument, this yields only
628        directories whose names match the given pattern.  For
629        example, ``mydir.walkdirs('*test')`` yields only directories
630        with names ending in ``'test'``.
631
632        The `errors=` keyword argument controls behavior when an
633        error occurs.  The default is ``'strict'``, which causes an
634        exception.  The other allowed values are ``'warn'`` (which
635        reports the error via :func:`warnings.warn()`), and ``'ignore'``.
636        """
637        if errors not in ('strict', 'warn', 'ignore'):
638            raise ValueError("invalid errors parameter")
639
640        try:
641            dirs = self.dirs()
642        except Exception:
643            if errors == 'ignore':
644                return
645            elif errors == 'warn':
646                warnings.warn(
647                    "Unable to list directory '%s': %s"
648                    % (self, sys.exc_info()[1]),
649                    TreeWalkWarning)
650                return
651            else:
652                raise
653
654        for child in dirs:
655            if pattern is None or child.fnmatch(pattern):
656                yield child
657            for subsubdir in child.walkdirs(pattern, errors):
658                yield subsubdir
659
660    def walkfiles(self, pattern=None, errors='strict'):
661        """ D.walkfiles() -> iterator over files in D, recursively.
662
663        The optional argument `pattern` limits the results to files
664        with names that match the pattern.  For example,
665        ``mydir.walkfiles('*.tmp')`` yields only files with the ``.tmp``
666        extension.
667        """
668        if errors not in ('strict', 'warn', 'ignore'):
669            raise ValueError("invalid errors parameter")
670
671        try:
672            childList = self.listdir()
673        except Exception:
674            if errors == 'ignore':
675                return
676            elif errors == 'warn':
677                warnings.warn(
678                    "Unable to list directory '%s': %s"
679                    % (self, sys.exc_info()[1]),
680                    TreeWalkWarning)
681                return
682            else:
683                raise
684
685        for child in childList:
686            try:
687                isfile = child.isfile()
688                isdir = not isfile and child.isdir()
689            except:
690                if errors == 'ignore':
691                    continue
692                elif errors == 'warn':
693                    warnings.warn(
694                        "Unable to access '%s': %s"
695                        % (self, sys.exc_info()[1]),
696                        TreeWalkWarning)
697                    continue
698                else:
699                    raise
700
701            if isfile:
702                if pattern is None or child.fnmatch(pattern):
703                    yield child
704            elif isdir:
705                for f in child.walkfiles(pattern, errors):
706                    yield f
707
708    def fnmatch(self, pattern, normcase=None):
709        """ Return ``True`` if `self.name` matches the given `pattern`.
710
711        `pattern` - A filename pattern with wildcards,
712            for example ``'*.py'``. If the pattern contains a `normcase`
713            attribute, it is applied to the name and path prior to comparison.
714
715        `normcase` - (optional) A function used to normalize the pattern and
716            filename before matching. Defaults to :meth:`self.module`, which defaults
717            to :meth:`os.path.normcase`.
718
719        .. seealso:: :func:`fnmatch.fnmatch`
720        """
721        default_normcase = getattr(pattern, 'normcase', self.module.normcase)
722        normcase = normcase or default_normcase
723        name = normcase(self.name)
724        pattern = normcase(pattern)
725        return fnmatch.fnmatchcase(name, pattern)
726
727    def glob(self, pattern):
728        """ Return a list of Path objects that match the pattern.
729
730        `pattern` - a path relative to this directory, with wildcards.
731
732        For example, ``Path('/users').glob('*/bin/*')`` returns a list
733        of all the files users have in their :file:`bin` directories.
734
735        .. seealso:: :func:`glob.glob`
736        """
737        cls = self._next_class
738        return [cls(s) for s in glob.glob(self / pattern)]
739
740    #
741    # --- Reading or writing an entire file at once.
742
743    def open(self, *args, **kwargs):
744        """ Open this file and return a corresponding :class:`file` object.
745
746        Keyword arguments work as in :func:`io.open`.  If the file cannot be
747        opened, an :class:`~exceptions.OSError` is raised.
748        """
749        with io_error_compat():
750            return io.open(self, *args, **kwargs)
751
752    def bytes(self):
753        """ Open this file, read all bytes, return them as a string. """
754        with self.open('rb') as f:
755            return f.read()
756
757    def chunks(self, size, *args, **kwargs):
758        """ Returns a generator yielding chunks of the file, so it can
759            be read piece by piece with a simple for loop.
760
761           Any argument you pass after `size` will be passed to :meth:`open`.
762
763           :example:
764
765               >>> hash = hashlib.md5()
766               >>> for chunk in Path("path.py").chunks(8192, mode='rb'):
767               ...     hash.update(chunk)
768
769            This will read the file by chunks of 8192 bytes.
770        """
771        with self.open(*args, **kwargs) as f:
772            for chunk in iter(lambda: f.read(size) or None, None):
773                yield chunk
774
775    def write_bytes(self, bytes, append=False):
776        """ Open this file and write the given bytes to it.
777
778        Default behavior is to overwrite any existing file.
779        Call ``p.write_bytes(bytes, append=True)`` to append instead.
780        """
781        if append:
782            mode = 'ab'
783        else:
784            mode = 'wb'
785        with self.open(mode) as f:
786            f.write(bytes)
787
788    def text(self, encoding=None, errors='strict'):
789        r""" Open this file, read it in, return the content as a string.
790
791        All newline sequences are converted to ``'\n'``.  Keyword arguments
792        will be passed to :meth:`open`.
793
794        .. seealso:: :meth:`lines`
795        """
796        with self.open(mode='r', encoding=encoding, errors=errors) as f:
797            return U_NEWLINE.sub('\n', f.read())
798
799    def write_text(self, text, encoding=None, errors='strict',
800                   linesep=os.linesep, append=False):
801        r""" Write the given text to this file.
802
803        The default behavior is to overwrite any existing file;
804        to append instead, use the `append=True` keyword argument.
805
806        There are two differences between :meth:`write_text` and
807        :meth:`write_bytes`: newline handling and Unicode handling.
808        See below.
809
810        Parameters:
811
812          `text` - str/unicode - The text to be written.
813
814          `encoding` - str - The Unicode encoding that will be used.
815              This is ignored if `text` isn't a Unicode string.
816
817          `errors` - str - How to handle Unicode encoding errors.
818              Default is ``'strict'``.  See ``help(unicode.encode)`` for the
819              options.  This is ignored if `text` isn't a Unicode
820              string.
821
822          `linesep` - keyword argument - str/unicode - The sequence of
823              characters to be used to mark end-of-line.  The default is
824              :data:`os.linesep`.  You can also specify ``None`` to
825              leave all newlines as they are in `text`.
826
827          `append` - keyword argument - bool - Specifies what to do if
828              the file already exists (``True``: append to the end of it;
829              ``False``: overwrite it.)  The default is ``False``.
830
831
832        --- Newline handling.
833
834        ``write_text()`` converts all standard end-of-line sequences
835        (``'\n'``, ``'\r'``, and ``'\r\n'``) to your platform's default
836        end-of-line sequence (see :data:`os.linesep`; on Windows, for example,
837        the end-of-line marker is ``'\r\n'``).
838
839        If you don't like your platform's default, you can override it
840        using the `linesep=` keyword argument.  If you specifically want
841        ``write_text()`` to preserve the newlines as-is, use ``linesep=None``.
842
843        This applies to Unicode text the same as to 8-bit text, except
844        there are three additional standard Unicode end-of-line sequences:
845        ``u'\x85'``, ``u'\r\x85'``, and ``u'\u2028'``.
846
847        (This is slightly different from when you open a file for
848        writing with ``fopen(filename, "w")`` in C or ``open(filename, 'w')``
849        in Python.)
850
851
852        --- Unicode
853
854        If `text` isn't Unicode, then apart from newline handling, the
855        bytes are written verbatim to the file.  The `encoding` and
856        `errors` arguments are not used and must be omitted.
857
858        If `text` is Unicode, it is first converted to :func:`bytes` using the
859        specified `encoding` (or the default encoding if `encoding`
860        isn't specified).  The `errors` argument applies only to this
861        conversion.
862
863        """
864        if isinstance(text, text_type):
865            if linesep is not None:
866                text = U_NEWLINE.sub(linesep, text)
867            text = text.encode(encoding or sys.getdefaultencoding(), errors)
868        else:
869            assert encoding is None
870            text = NEWLINE.sub(linesep, text)
871        self.write_bytes(text, append=append)
872
873    def lines(self, encoding=None, errors='strict', retain=True):
874        r""" Open this file, read all lines, return them in a list.
875
876        Optional arguments:
877            `encoding` - The Unicode encoding (or character set) of
878                the file.  The default is ``None``, meaning the content
879                of the file is read as 8-bit characters and returned
880                as a list of (non-Unicode) str objects.
881            `errors` - How to handle Unicode errors; see help(str.decode)
882                for the options.  Default is ``'strict'``.
883            `retain` - If ``True``, retain newline characters; but all newline
884                character combinations (``'\r'``, ``'\n'``, ``'\r\n'``) are
885                translated to ``'\n'``.  If ``False``, newline characters are
886                stripped off.  Default is ``True``.
887
888        This uses ``'U'`` mode.
889
890        .. seealso:: :meth:`text`
891        """
892        if encoding is None and retain:
893            with self.open('U') as f:
894                return f.readlines()
895        else:
896            return self.text(encoding, errors).splitlines(retain)
897
898    def write_lines(self, lines, encoding=None, errors='strict',
899                    linesep=os.linesep, append=False):
900        r""" Write the given lines of text to this file.
901
902        By default this overwrites any existing file at this path.
903
904        This puts a platform-specific newline sequence on every line.
905        See `linesep` below.
906
907            `lines` - A list of strings.
908
909            `encoding` - A Unicode encoding to use.  This applies only if
910                `lines` contains any Unicode strings.
911
912            `errors` - How to handle errors in Unicode encoding.  This
913                also applies only to Unicode strings.
914
915            linesep - The desired line-ending.  This line-ending is
916                applied to every line.  If a line already has any
917                standard line ending (``'\r'``, ``'\n'``, ``'\r\n'``,
918                ``u'\x85'``, ``u'\r\x85'``, ``u'\u2028'``), that will
919                be stripped off and this will be used instead.  The
920                default is os.linesep, which is platform-dependent
921                (``'\r\n'`` on Windows, ``'\n'`` on Unix, etc.).
922                Specify ``None`` to write the lines as-is, like
923                :meth:`file.writelines`.
924
925        Use the keyword argument ``append=True`` to append lines to the
926        file.  The default is to overwrite the file.
927
928        .. warning ::
929
930            When you use this with Unicode data, if the encoding of the
931            existing data in the file is different from the encoding
932            you specify with the `encoding=` parameter, the result is
933            mixed-encoding data, which can really confuse someone trying
934            to read the file later.
935        """
936        with self.open('ab' if append else 'wb') as f:
937            for l in lines:
938                isUnicode = isinstance(l, text_type)
939                if linesep is not None:
940                    pattern = U_NL_END if isUnicode else NL_END
941                    l = pattern.sub('', l) + linesep
942                if isUnicode:
943                    l = l.encode(encoding or sys.getdefaultencoding(), errors)
944                f.write(l)
945
946    def read_md5(self):
947        """ Calculate the md5 hash for this file.
948
949        This reads through the entire file.
950
951        .. seealso:: :meth:`read_hash`
952        """
953        return self.read_hash('md5')
954
955    def _hash(self, hash_name):
956        """ Returns a hash object for the file at the current path.
957
958            `hash_name` should be a hash algo name (such as ``'md5'`` or ``'sha1'``)
959            that's available in the :mod:`hashlib` module.
960        """
961        m = hashlib.new(hash_name)
962        for chunk in self.chunks(8192, mode="rb"):
963            m.update(chunk)
964        return m
965
966    def read_hash(self, hash_name):
967        """ Calculate given hash for this file.
968
969        List of supported hashes can be obtained from :mod:`hashlib` package.
970        This reads the entire file.
971
972        .. seealso:: :meth:`hashlib.hash.digest`
973        """
974        return self._hash(hash_name).digest()
975
976    def read_hexhash(self, hash_name):
977        """ Calculate given hash for this file, returning hexdigest.
978
979        List of supported hashes can be obtained from :mod:`hashlib` package.
980        This reads the entire file.
981
982        .. seealso:: :meth:`hashlib.hash.hexdigest`
983        """
984        return self._hash(hash_name).hexdigest()
985
986    # --- Methods for querying the filesystem.
987    # N.B. On some platforms, the os.path functions may be implemented in C
988    # (e.g. isdir on Windows, Python 3.2.2), and compiled functions don't get
989    # bound. Playing it safe and wrapping them all in method calls.
990
991    def isabs(self):
992        """ .. seealso:: :func:`os.path.isabs` """
993        return self.module.isabs(self)
994
995    def exists(self):
996        """ .. seealso:: :func:`os.path.exists` """
997        return self.module.exists(self)
998
999    def isdir(self):
1000        """ .. seealso:: :func:`os.path.isdir` """
1001        return self.module.isdir(self)
1002
1003    def isfile(self):
1004        """ .. seealso:: :func:`os.path.isfile` """
1005        return self.module.isfile(self)
1006
1007    def islink(self):
1008        """ .. seealso:: :func:`os.path.islink` """
1009        return self.module.islink(self)
1010
1011    def ismount(self):
1012        """ .. seealso:: :func:`os.path.ismount` """
1013        return self.module.ismount(self)
1014
1015    def samefile(self, other):
1016        """ .. seealso:: :func:`os.path.samefile` """
1017        if not hasattr(self.module, 'samefile'):
1018            other = Path(other).realpath().normpath().normcase()
1019            return self.realpath().normpath().normcase() == other
1020        return self.module.samefile(self, other)
1021
1022    def getatime(self):
1023        """ .. seealso:: :attr:`atime`, :func:`os.path.getatime` """
1024        return self.module.getatime(self)
1025
1026    atime = property(
1027        getatime, None, None,
1028        """ Last access time of the file.
1029
1030        .. seealso:: :meth:`getatime`, :func:`os.path.getatime`
1031        """)
1032
1033    def getmtime(self):
1034        """ .. seealso:: :attr:`mtime`, :func:`os.path.getmtime` """
1035        return self.module.getmtime(self)
1036
1037    mtime = property(
1038        getmtime, None, None,
1039        """ Last-modified time of the file.
1040
1041        .. seealso:: :meth:`getmtime`, :func:`os.path.getmtime`
1042        """)
1043
1044    def getctime(self):
1045        """ .. seealso:: :attr:`ctime`, :func:`os.path.getctime` """
1046        return self.module.getctime(self)
1047
1048    ctime = property(
1049        getctime, None, None,
1050        """ Creation time of the file.
1051
1052        .. seealso:: :meth:`getctime`, :func:`os.path.getctime`
1053        """)
1054
1055    def getsize(self):
1056        """ .. seealso:: :attr:`size`, :func:`os.path.getsize` """
1057        return self.module.getsize(self)
1058
1059    size = property(
1060        getsize, None, None,
1061        """ Size of the file, in bytes.
1062
1063        .. seealso:: :meth:`getsize`, :func:`os.path.getsize`
1064        """)
1065
1066    if hasattr(os, 'access'):
1067        def access(self, mode):
1068            """ Return ``True`` if current user has access to this path.
1069
1070            mode - One of the constants :data:`os.F_OK`, :data:`os.R_OK`,
1071            :data:`os.W_OK`, :data:`os.X_OK`
1072
1073            .. seealso:: :func:`os.access`
1074            """
1075            return os.access(self, mode)
1076
1077    def stat(self):
1078        """ Perform a ``stat()`` system call on this path.
1079
1080        .. seealso:: :meth:`lstat`, :func:`os.stat`
1081        """
1082        return os.stat(self)
1083
1084    def lstat(self):
1085        """ Like :meth:`stat`, but do not follow symbolic links.
1086
1087        .. seealso:: :meth:`stat`, :func:`os.lstat`
1088        """
1089        return os.lstat(self)
1090
1091    def __get_owner_windows(self):
1092        """
1093        Return the name of the owner of this file or directory. Follow
1094        symbolic links.
1095
1096        Return a name of the form ``r'DOMAIN\\User Name'``; may be a group.
1097
1098        .. seealso:: :attr:`owner`
1099        """
1100        desc = win32security.GetFileSecurity(
1101            self, win32security.OWNER_SECURITY_INFORMATION)
1102        sid = desc.GetSecurityDescriptorOwner()
1103        account, domain, typecode = win32security.LookupAccountSid(None, sid)
1104        return domain + '\\' + account
1105
1106    def __get_owner_unix(self):
1107        """
1108        Return the name of the owner of this file or directory. Follow
1109        symbolic links.
1110
1111        .. seealso:: :attr:`owner`
1112        """
1113        st = self.stat()
1114        return pwd.getpwuid(st.st_uid).pw_name
1115
1116    def __get_owner_not_implemented(self):
1117        raise NotImplementedError("Ownership not available on this platform.")
1118
1119    if 'win32security' in globals():
1120        get_owner = __get_owner_windows
1121    elif 'pwd' in globals():
1122        get_owner = __get_owner_unix
1123    else:
1124        get_owner = __get_owner_not_implemented
1125
1126    owner = property(
1127        get_owner, None, None,
1128        """ Name of the owner of this file or directory.
1129
1130        .. seealso:: :meth:`get_owner`""")
1131
1132    if hasattr(os, 'statvfs'):
1133        def statvfs(self):
1134            """ Perform a ``statvfs()`` system call on this path.
1135
1136            .. seealso:: :func:`os.statvfs`
1137            """
1138            return os.statvfs(self)
1139
1140    if hasattr(os, 'pathconf'):
1141        def pathconf(self, name):
1142            """ .. seealso:: :func:`os.pathconf` """
1143            return os.pathconf(self, name)
1144
1145    #
1146    # --- Modifying operations on files and directories
1147
1148    def utime(self, times):
1149        """ Set the access and modified times of this file.
1150
1151        .. seealso:: :func:`os.utime`
1152        """
1153        os.utime(self, times)
1154        return self
1155
1156    def chmod(self, mode):
1157        """
1158        Set the mode. May be the new mode (os.chmod behavior) or a `symbolic
1159        mode <http://en.wikipedia.org/wiki/Chmod#Symbolic_modes>`_.
1160
1161        .. seealso:: :func:`os.chmod`
1162        """
1163        if isinstance(mode, string_types):
1164            mask = _multi_permission_mask(mode)
1165            mode = mask(self.stat().st_mode)
1166        os.chmod(self, mode)
1167        return self
1168
1169    def chown(self, uid=-1, gid=-1):
1170        """
1171        Change the owner and group by names rather than the uid or gid numbers.
1172
1173        .. seealso:: :func:`os.chown`
1174        """
1175        if hasattr(os, 'chown'):
1176            if 'pwd' in globals() and isinstance(uid, string_types):
1177                uid = pwd.getpwnam(uid).pw_uid
1178            if 'grp' in globals() and isinstance(gid, string_types):
1179                gid = grp.getgrnam(gid).gr_gid
1180            os.chown(self, uid, gid)
1181        else:
1182            raise NotImplementedError("Ownership not available on this platform.")
1183        return self
1184
1185    def rename(self, new):
1186        """ .. seealso:: :func:`os.rename` """
1187        os.rename(self, new)
1188        return self._next_class(new)
1189
1190    def renames(self, new):
1191        """ .. seealso:: :func:`os.renames` """
1192        os.renames(self, new)
1193        return self._next_class(new)
1194
1195    #
1196    # --- Create/delete operations on directories
1197
1198    def mkdir(self, mode=0o777):
1199        """ .. seealso:: :func:`os.mkdir` """
1200        os.mkdir(self, mode)
1201        return self
1202
1203    def mkdir_p(self, mode=0o777):
1204        """ Like :meth:`mkdir`, but does not raise an exception if the
1205        directory already exists. """
1206        try:
1207            self.mkdir(mode)
1208        except OSError:
1209            _, e, _ = sys.exc_info()
1210            if e.errno != errno.EEXIST:
1211                raise
1212        return self
1213
1214    def makedirs(self, mode=0o777):
1215        """ .. seealso:: :func:`os.makedirs` """
1216        os.makedirs(self, mode)
1217        return self
1218
1219    def makedirs_p(self, mode=0o777):
1220        """ Like :meth:`makedirs`, but does not raise an exception if the
1221        directory already exists. """
1222        try:
1223            self.makedirs(mode)
1224        except OSError:
1225            _, e, _ = sys.exc_info()
1226            if e.errno != errno.EEXIST:
1227                raise
1228        return self
1229
1230    def rmdir(self):
1231        """ .. seealso:: :func:`os.rmdir` """
1232        os.rmdir(self)
1233        return self
1234
1235    def rmdir_p(self):
1236        """ Like :meth:`rmdir`, but does not raise an exception if the
1237        directory is not empty or does not exist. """
1238        try:
1239            self.rmdir()
1240        except OSError:
1241            _, e, _ = sys.exc_info()
1242            if e.errno != errno.ENOTEMPTY and e.errno != errno.EEXIST:
1243                raise
1244        return self
1245
1246    def removedirs(self):
1247        """ .. seealso:: :func:`os.removedirs` """
1248        os.removedirs(self)
1249        return self
1250
1251    def removedirs_p(self):
1252        """ Like :meth:`removedirs`, but does not raise an exception if the
1253        directory is not empty or does not exist. """
1254        try:
1255            self.removedirs()
1256        except OSError:
1257            _, e, _ = sys.exc_info()
1258            if e.errno != errno.ENOTEMPTY and e.errno != errno.EEXIST:
1259                raise
1260        return self
1261
1262    # --- Modifying operations on files
1263
1264    def touch(self):
1265        """ Set the access/modified times of this file to the current time.
1266        Create the file if it does not exist.
1267        """
1268        fd = os.open(self, os.O_WRONLY | os.O_CREAT, 0o666)
1269        os.close(fd)
1270        os.utime(self, None)
1271        return self
1272
1273    def remove(self):
1274        """ .. seealso:: :func:`os.remove` """
1275        os.remove(self)
1276        return self
1277
1278    def remove_p(self):
1279        """ Like :meth:`remove`, but does not raise an exception if the
1280        file does not exist. """
1281        try:
1282            self.unlink()
1283        except OSError:
1284            _, e, _ = sys.exc_info()
1285            if e.errno != errno.ENOENT:
1286                raise
1287        return self
1288
1289    def unlink(self):
1290        """ .. seealso:: :func:`os.unlink` """
1291        os.unlink(self)
1292        return self
1293
1294    def unlink_p(self):
1295        """ Like :meth:`unlink`, but does not raise an exception if the
1296        file does not exist. """
1297        self.remove_p()
1298        return self
1299
1300    # --- Links
1301
1302    if hasattr(os, 'link'):
1303        def link(self, newpath):
1304            """ Create a hard link at `newpath`, pointing to this file.
1305
1306            .. seealso:: :func:`os.link`
1307            """
1308            os.link(self, newpath)
1309            return self._next_class(newpath)
1310
1311    if hasattr(os, 'symlink'):
1312        def symlink(self, newlink):
1313            """ Create a symbolic link at `newlink`, pointing here.
1314
1315            .. seealso:: :func:`os.symlink`
1316            """
1317            os.symlink(self, newlink)
1318            return self._next_class(newlink)
1319
1320    if hasattr(os, 'readlink'):
1321        def readlink(self):
1322            """ Return the path to which this symbolic link points.
1323
1324            The result may be an absolute or a relative path.
1325
1326            .. seealso:: :meth:`readlinkabs`, :func:`os.readlink`
1327            """
1328            return self._next_class(os.readlink(self))
1329
1330        def readlinkabs(self):
1331            """ Return the path to which this symbolic link points.
1332
1333            The result is always an absolute path.
1334
1335            .. seealso:: :meth:`readlink`, :func:`os.readlink`
1336            """
1337            p = self.readlink()
1338            if p.isabs():
1339                return p
1340            else:
1341                return (self.parent / p).abspath()
1342
1343    # High-level functions from shutil
1344    # These functions will be bound to the instance such that
1345    # Path(name).copy(target) will invoke shutil.copy(name, target)
1346
1347    copyfile = shutil.copyfile
1348    copymode = shutil.copymode
1349    copystat = shutil.copystat
1350    copy = shutil.copy
1351    copy2 = shutil.copy2
1352    copytree = shutil.copytree
1353    if hasattr(shutil, 'move'):
1354        move = shutil.move
1355    rmtree = shutil.rmtree
1356
1357    def rmtree_p(self):
1358        """ Like :meth:`rmtree`, but does not raise an exception if the
1359        directory does not exist. """
1360        try:
1361            self.rmtree()
1362        except OSError:
1363            _, e, _ = sys.exc_info()
1364            if e.errno != errno.ENOENT:
1365                raise
1366        return self
1367
1368    def chdir(self):
1369        """ .. seealso:: :func:`os.chdir` """
1370        os.chdir(self)
1371
1372    cd = chdir
1373
1374    def merge_tree(self, dst, symlinks=False, *args, **kwargs):
1375        """
1376        Copy entire contents of self to dst, overwriting existing
1377        contents in dst with those in self.
1378
1379        If the additional keyword `update` is True, each
1380        `src` will only be copied if `dst` does not exist,
1381        or `src` is newer than `dst`.
1382
1383        Note that the technique employed stages the files in a temporary
1384        directory first, so this function is not suitable for merging
1385        trees with large files, especially if the temporary directory
1386        is not capable of storing a copy of the entire source tree.
1387        """
1388        update = kwargs.pop('update', False)
1389        with tempdir() as _temp_dir:
1390            # first copy the tree to a stage directory to support
1391            #  the parameters and behavior of copytree.
1392            stage = _temp_dir / str(hash(self))
1393            self.copytree(stage, symlinks, *args, **kwargs)
1394            # now copy everything from the stage directory using
1395            #  the semantics of dir_util.copy_tree
1396            dir_util.copy_tree(stage, dst, preserve_symlinks=symlinks,
1397                update=update)
1398
1399    #
1400    # --- Special stuff from os
1401
1402    if hasattr(os, 'chroot'):
1403        def chroot(self):
1404            """ .. seealso:: :func:`os.chroot` """
1405            os.chroot(self)
1406
1407    if hasattr(os, 'startfile'):
1408        def startfile(self):
1409            """ .. seealso:: :func:`os.startfile` """
1410            os.startfile(self)
1411            return self
1412
1413    # in-place re-writing, courtesy of Martijn Pieters
1414    # http://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/
1415    @contextlib.contextmanager
1416    def in_place(self, mode='r', buffering=-1, encoding=None, errors=None,
1417            newline=None, backup_extension=None):
1418        """
1419        A context in which a file may be re-written in-place with new content.
1420
1421        Yields a tuple of :samp:`({readable}, {writable})` file objects, where `writable`
1422        replaces `readable`.
1423
1424        If an exception occurs, the old file is restored, removing the
1425        written data.
1426
1427        Mode *must not* use ``'w'``, ``'a'``, or ``'+'``; only read-only-modes are
1428        allowed. A :exc:`ValueError` is raised on invalid modes.
1429
1430        For example, to add line numbers to a file::
1431
1432            p = Path(filename)
1433            assert p.isfile()
1434            with p.in_place() as (reader, writer):
1435                for number, line in enumerate(reader, 1):
1436                    writer.write('{0:3}: '.format(number)))
1437                    writer.write(line)
1438
1439        Thereafter, the file at `filename` will have line numbers in it.
1440        """
1441        import io
1442
1443        if set(mode).intersection('wa+'):
1444            raise ValueError('Only read-only file modes can be used')
1445
1446        # move existing file to backup, create new file with same permissions
1447        # borrowed extensively from the fileinput module
1448        backup_fn = self + (backup_extension or os.extsep + 'bak')
1449        try:
1450            os.unlink(backup_fn)
1451        except os.error:
1452            pass
1453        os.rename(self, backup_fn)
1454        readable = io.open(backup_fn, mode, buffering=buffering,
1455            encoding=encoding, errors=errors, newline=newline)
1456        try:
1457            perm = os.fstat(readable.fileno()).st_mode
1458        except OSError:
1459            writable = open(self, 'w' + mode.replace('r', ''),
1460                buffering=buffering, encoding=encoding, errors=errors,
1461                newline=newline)
1462        else:
1463            os_mode = os.O_CREAT | os.O_WRONLY | os.O_TRUNC
1464            if hasattr(os, 'O_BINARY'):
1465                os_mode |= os.O_BINARY
1466            fd = os.open(self, os_mode, perm)
1467            writable = io.open(fd, "w" + mode.replace('r', ''),
1468                buffering=buffering, encoding=encoding, errors=errors,
1469                newline=newline)
1470            try:
1471                if hasattr(os, 'chmod'):
1472                    os.chmod(self, perm)
1473            except OSError:
1474                pass
1475        try:
1476            yield readable, writable
1477        except Exception:
1478            # move backup back
1479            readable.close()
1480            writable.close()
1481            try:
1482                os.unlink(self)
1483            except os.error:
1484                pass
1485            os.rename(backup_fn, self)
1486            raise
1487        else:
1488            readable.close()
1489            writable.close()
1490        finally:
1491            try:
1492                os.unlink(backup_fn)
1493            except os.error:
1494                pass
1495
1496    @ClassProperty
1497    @classmethod
1498    def special(cls):
1499        """
1500        Return a SpecialResolver object suitable referencing a suitable
1501        directory for the relevant platform for the given
1502        type of content.
1503
1504        For example, to get a user config directory, invoke:
1505
1506            dir = Path.special().user.config
1507
1508        Uses the `appdirs
1509        <https://pypi.python.org/pypi/appdirs/1.4.0>`_ to resolve
1510        the paths in a platform-friendly way.
1511
1512        To create a config directory for 'My App', consider:
1513
1514            dir = Path.special("My App").user.config.makedirs_p()
1515
1516        If the ``appdirs`` module is not installed, invocation
1517        of special will raise an ImportError.
1518        """
1519        return functools.partial(SpecialResolver, cls)
1520
1521
1522class SpecialResolver(object):
1523    class ResolverScope:
1524        def __init__(self, paths, scope):
1525            self.paths = paths
1526            self.scope = scope
1527
1528        def __getattr__(self, class_):
1529            return self.paths.get_dir(self.scope, class_)
1530
1531    def __init__(self, path_class, *args, **kwargs):
1532        appdirs = importlib.import_module('appdirs')
1533
1534        # let appname default to None until
1535        # https://github.com/ActiveState/appdirs/issues/55 is solved.
1536        not args and kwargs.setdefault('appname', None)
1537
1538        vars(self).update(
1539            path_class=path_class,
1540            wrapper=appdirs.AppDirs(*args, **kwargs),
1541        )
1542
1543    def __getattr__(self, scope):
1544        return self.ResolverScope(self, scope)
1545
1546    def get_dir(self, scope, class_):
1547        """
1548        Return the callable function from appdirs, but with the
1549        result wrapped in self.path_class
1550        """
1551        prop_name = '{scope}_{class_}_dir'.format(**locals())
1552        value = getattr(self.wrapper, prop_name)
1553        MultiPath = Multi.for_class(self.path_class)
1554        return MultiPath.detect(value)
1555
1556
1557class Multi:
1558    """
1559    A mix-in for a Path which may contain multiple Path separated by pathsep.
1560    """
1561    @classmethod
1562    def for_class(cls, path_cls):
1563        name = 'Multi' + path_cls.__name__
1564        if PY2:
1565            name = str(name)
1566        return type(name, (cls, path_cls), {})
1567
1568    @classmethod
1569    def detect(cls, input):
1570        if os.pathsep not in input:
1571            cls = cls._next_class
1572        return cls(input)
1573
1574    def __iter__(self):
1575        return iter(map(self._next_class, self.split(os.pathsep)))
1576
1577    @ClassProperty
1578    @classmethod
1579    def _next_class(cls):
1580        """
1581        Multi-subclasses should use the parent class
1582        """
1583        return next(
1584            class_
1585            for class_ in cls.__mro__
1586            if not issubclass(class_, Multi)
1587        )
1588
1589
1590class tempdir(Path):
1591    """
1592    A temporary directory via :func:`tempfile.mkdtemp`, and constructed with the
1593    same parameters that you can use as a context manager.
1594
1595    Example:
1596
1597        with tempdir() as d:
1598            # do stuff with the Path object "d"
1599
1600        # here the directory is deleted automatically
1601
1602    .. seealso:: :func:`tempfile.mkdtemp`
1603    """
1604
1605    @ClassProperty
1606    @classmethod
1607    def _next_class(cls):
1608        return Path
1609
1610    def __new__(cls, *args, **kwargs):
1611        dirname = tempfile.mkdtemp(*args, **kwargs)
1612        return super(tempdir, cls).__new__(cls, dirname)
1613
1614    def __init__(self, *args, **kwargs):
1615        pass
1616
1617    def __enter__(self):
1618        return self
1619
1620    def __exit__(self, exc_type, exc_value, traceback):
1621        if not exc_value:
1622            self.rmtree()
1623
1624
1625def _multi_permission_mask(mode):
1626    """
1627    Support multiple, comma-separated Unix chmod symbolic modes.
1628
1629    >>> _multi_permission_mask('a=r,u+w')(0) == 0o644
1630    True
1631    """
1632    compose = lambda f, g: lambda *args, **kwargs: g(f(*args, **kwargs))
1633    return functools.reduce(compose, map(_permission_mask, mode.split(',')))
1634
1635
1636def _permission_mask(mode):
1637    """
1638    Convert a Unix chmod symbolic mode like ``'ugo+rwx'`` to a function
1639    suitable for applying to a mask to affect that change.
1640
1641    >>> mask = _permission_mask('ugo+rwx')
1642    >>> mask(0o554) == 0o777
1643    True
1644
1645    >>> _permission_mask('go-x')(0o777) == 0o766
1646    True
1647
1648    >>> _permission_mask('o-x')(0o445) == 0o444
1649    True
1650
1651    >>> _permission_mask('a+x')(0) == 0o111
1652    True
1653
1654    >>> _permission_mask('a=rw')(0o057) == 0o666
1655    True
1656
1657    >>> _permission_mask('u=x')(0o666) == 0o166
1658    True
1659
1660    >>> _permission_mask('g=')(0o157) == 0o107
1661    True
1662    """
1663    # parse the symbolic mode
1664    parsed = re.match('(?P<who>[ugoa]+)(?P<op>[-+=])(?P<what>[rwx]*)$', mode)
1665    if not parsed:
1666        raise ValueError("Unrecognized symbolic mode", mode)
1667
1668    # generate a mask representing the specified permission
1669    spec_map = dict(r=4, w=2, x=1)
1670    specs = (spec_map[perm] for perm in parsed.group('what'))
1671    spec = functools.reduce(operator.or_, specs, 0)
1672
1673    # now apply spec to each subject in who
1674    shift_map = dict(u=6, g=3, o=0)
1675    who = parsed.group('who').replace('a', 'ugo')
1676    masks = (spec << shift_map[subj] for subj in who)
1677    mask = functools.reduce(operator.or_, masks)
1678
1679    op = parsed.group('op')
1680
1681    # if op is -, invert the mask
1682    if op == '-':
1683        mask ^= 0o777
1684
1685    # if op is =, retain extant values for unreferenced subjects
1686    if op == '=':
1687        masks = (0o7 << shift_map[subj] for subj in who)
1688        retain = functools.reduce(operator.or_, masks) ^ 0o777
1689
1690    op_map = {
1691        '+': operator.or_,
1692        '-': operator.and_,
1693        '=': lambda mask, target: target & retain ^ mask,
1694    }
1695    return functools.partial(op_map[op], mask)
1696
1697
1698class CaseInsensitivePattern(text_type):
1699    """
1700    A string with a ``'normcase'`` property, suitable for passing to
1701    :meth:`listdir`, :meth:`dirs`, :meth:`files`, :meth:`walk`,
1702    :meth:`walkdirs`, or :meth:`walkfiles` to match case-insensitive.
1703
1704    For example, to get all files ending in .py, .Py, .pY, or .PY in the
1705    current directory::
1706
1707        from path import Path, CaseInsensitivePattern as ci
1708        Path('.').files(ci('*.py'))
1709    """
1710
1711    @property
1712    def normcase(self):
1713        return __import__('ntpath').normcase
1714
1715########################
1716# Backward-compatibility
1717class path(Path):
1718    def __new__(cls, *args, **kwargs):
1719        msg = "path is deprecated. Use Path instead."
1720        warnings.warn(msg, DeprecationWarning)
1721        return Path.__new__(cls, *args, **kwargs)
1722
1723
1724__all__ += ['path']
1725########################
1726