ยปCore Development>Code coverage>Lib/pathlib.py

Python code coverage for Lib/pathlib.py

#countcontent
1n/aimport fnmatch
2n/aimport functools
3n/aimport io
4n/aimport ntpath
5n/aimport os
6n/aimport posixpath
7n/aimport re
8n/aimport sys
9n/afrom collections import Sequence
10n/afrom contextlib import contextmanager
11n/afrom errno import EINVAL, ENOENT, ENOTDIR
12n/afrom operator import attrgetter
13n/afrom stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
14n/afrom urllib.parse import quote_from_bytes as urlquote_from_bytes
15n/a
16n/a
17n/asupports_symlinks = True
18n/aif os.name == 'nt':
19n/a import nt
20n/a if sys.getwindowsversion()[:2] >= (6, 0):
21n/a from nt import _getfinalpathname
22n/a else:
23n/a supports_symlinks = False
24n/a _getfinalpathname = None
25n/aelse:
26n/a nt = None
27n/a
28n/a
29n/a__all__ = [
30n/a "PurePath", "PurePosixPath", "PureWindowsPath",
31n/a "Path", "PosixPath", "WindowsPath",
32n/a ]
33n/a
34n/a#
35n/a# Internals
36n/a#
37n/a
38n/adef _is_wildcard_pattern(pat):
39n/a # Whether this pattern needs actual matching using fnmatch, or can
40n/a # be looked up directly as a file.
41n/a return "*" in pat or "?" in pat or "[" in pat
42n/a
43n/a
44n/aclass _Flavour(object):
45n/a """A flavour implements a particular (platform-specific) set of path
46n/a semantics."""
47n/a
48n/a def __init__(self):
49n/a self.join = self.sep.join
50n/a
51n/a def parse_parts(self, parts):
52n/a parsed = []
53n/a sep = self.sep
54n/a altsep = self.altsep
55n/a drv = root = ''
56n/a it = reversed(parts)
57n/a for part in it:
58n/a if not part:
59n/a continue
60n/a if altsep:
61n/a part = part.replace(altsep, sep)
62n/a drv, root, rel = self.splitroot(part)
63n/a if sep in rel:
64n/a for x in reversed(rel.split(sep)):
65n/a if x and x != '.':
66n/a parsed.append(sys.intern(x))
67n/a else:
68n/a if rel and rel != '.':
69n/a parsed.append(sys.intern(rel))
70n/a if drv or root:
71n/a if not drv:
72n/a # If no drive is present, try to find one in the previous
73n/a # parts. This makes the result of parsing e.g.
74n/a # ("C:", "/", "a") reasonably intuitive.
75n/a for part in it:
76n/a if not part:
77n/a continue
78n/a if altsep:
79n/a part = part.replace(altsep, sep)
80n/a drv = self.splitroot(part)[0]
81n/a if drv:
82n/a break
83n/a break
84n/a if drv or root:
85n/a parsed.append(drv + root)
86n/a parsed.reverse()
87n/a return drv, root, parsed
88n/a
89n/a def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):
90n/a """
91n/a Join the two paths represented by the respective
92n/a (drive, root, parts) tuples. Return a new (drive, root, parts) tuple.
93n/a """
94n/a if root2:
95n/a if not drv2 and drv:
96n/a return drv, root2, [drv + root2] + parts2[1:]
97n/a elif drv2:
98n/a if drv2 == drv or self.casefold(drv2) == self.casefold(drv):
99n/a # Same drive => second path is relative to the first
100n/a return drv, root, parts + parts2[1:]
101n/a else:
102n/a # Second path is non-anchored (common case)
103n/a return drv, root, parts + parts2
104n/a return drv2, root2, parts2
105n/a
106n/a
107n/aclass _WindowsFlavour(_Flavour):
108n/a # Reference for Windows paths can be found at
109n/a # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
110n/a
111n/a sep = '\\'
112n/a altsep = '/'
113n/a has_drv = True
114n/a pathmod = ntpath
115n/a
116n/a is_supported = (os.name == 'nt')
117n/a
118n/a drive_letters = (
119n/a set(chr(x) for x in range(ord('a'), ord('z') + 1)) |
120n/a set(chr(x) for x in range(ord('A'), ord('Z') + 1))
121n/a )
122n/a ext_namespace_prefix = '\\\\?\\'
123n/a
124n/a reserved_names = (
125n/a {'CON', 'PRN', 'AUX', 'NUL'} |
126n/a {'COM%d' % i for i in range(1, 10)} |
127n/a {'LPT%d' % i for i in range(1, 10)}
128n/a )
129n/a
130n/a # Interesting findings about extended paths:
131n/a # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
132n/a # but '\\?\c:/a' is not
133n/a # - extended paths are always absolute; "relative" extended paths will
134n/a # fail.
135n/a
136n/a def splitroot(self, part, sep=sep):
137n/a first = part[0:1]
138n/a second = part[1:2]
139n/a if (second == sep and first == sep):
140n/a # XXX extended paths should also disable the collapsing of "."
141n/a # components (according to MSDN docs).
142n/a prefix, part = self._split_extended_path(part)
143n/a first = part[0:1]
144n/a second = part[1:2]
145n/a else:
146n/a prefix = ''
147n/a third = part[2:3]
148n/a if (second == sep and first == sep and third != sep):
149n/a # is a UNC path:
150n/a # vvvvvvvvvvvvvvvvvvvvv root
151n/a # \\machine\mountpoint\directory\etc\...
152n/a # directory ^^^^^^^^^^^^^^
153n/a index = part.find(sep, 2)
154n/a if index != -1:
155n/a index2 = part.find(sep, index + 1)
156n/a # a UNC path can't have two slashes in a row
157n/a # (after the initial two)
158n/a if index2 != index + 1:
159n/a if index2 == -1:
160n/a index2 = len(part)
161n/a if prefix:
162n/a return prefix + part[1:index2], sep, part[index2+1:]
163n/a else:
164n/a return part[:index2], sep, part[index2+1:]
165n/a drv = root = ''
166n/a if second == ':' and first in self.drive_letters:
167n/a drv = part[:2]
168n/a part = part[2:]
169n/a first = third
170n/a if first == sep:
171n/a root = first
172n/a part = part.lstrip(sep)
173n/a return prefix + drv, root, part
174n/a
175n/a def casefold(self, s):
176n/a return s.lower()
177n/a
178n/a def casefold_parts(self, parts):
179n/a return [p.lower() for p in parts]
180n/a
181n/a def resolve(self, path, strict=False):
182n/a s = str(path)
183n/a if not s:
184n/a return os.getcwd()
185n/a previous_s = None
186n/a if _getfinalpathname is not None:
187n/a if strict:
188n/a return self._ext_to_normal(_getfinalpathname(s))
189n/a else:
190n/a while True:
191n/a try:
192n/a s = self._ext_to_normal(_getfinalpathname(s))
193n/a except FileNotFoundError:
194n/a previous_s = s
195n/a s = os.path.dirname(s)
196n/a if previous_s == s:
197n/a return path
198n/a else:
199n/a if previous_s is None:
200n/a return s
201n/a else:
202n/a return s + os.path.sep + os.path.basename(previous_s)
203n/a # Means fallback on absolute
204n/a return None
205n/a
206n/a def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
207n/a prefix = ''
208n/a if s.startswith(ext_prefix):
209n/a prefix = s[:4]
210n/a s = s[4:]
211n/a if s.startswith('UNC\\'):
212n/a prefix += s[:3]
213n/a s = '\\' + s[3:]
214n/a return prefix, s
215n/a
216n/a def _ext_to_normal(self, s):
217n/a # Turn back an extended path into a normal DOS-like path
218n/a return self._split_extended_path(s)[1]
219n/a
220n/a def is_reserved(self, parts):
221n/a # NOTE: the rules for reserved names seem somewhat complicated
222n/a # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
223n/a # We err on the side of caution and return True for paths which are
224n/a # not considered reserved by Windows.
225n/a if not parts:
226n/a return False
227n/a if parts[0].startswith('\\\\'):
228n/a # UNC paths are never reserved
229n/a return False
230n/a return parts[-1].partition('.')[0].upper() in self.reserved_names
231n/a
232n/a def make_uri(self, path):
233n/a # Under Windows, file URIs use the UTF-8 encoding.
234n/a drive = path.drive
235n/a if len(drive) == 2 and drive[1] == ':':
236n/a # It's a path on a local drive => 'file:///c:/a/b'
237n/a rest = path.as_posix()[2:].lstrip('/')
238n/a return 'file:///%s/%s' % (
239n/a drive, urlquote_from_bytes(rest.encode('utf-8')))
240n/a else:
241n/a # It's a path on a network drive => 'file://host/share/a/b'
242n/a return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
243n/a
244n/a def gethomedir(self, username):
245n/a if 'HOME' in os.environ:
246n/a userhome = os.environ['HOME']
247n/a elif 'USERPROFILE' in os.environ:
248n/a userhome = os.environ['USERPROFILE']
249n/a elif 'HOMEPATH' in os.environ:
250n/a try:
251n/a drv = os.environ['HOMEDRIVE']
252n/a except KeyError:
253n/a drv = ''
254n/a userhome = drv + os.environ['HOMEPATH']
255n/a else:
256n/a raise RuntimeError("Can't determine home directory")
257n/a
258n/a if username:
259n/a # Try to guess user home directory. By default all users
260n/a # directories are located in the same place and are named by
261n/a # corresponding usernames. If current user home directory points
262n/a # to nonstandard place, this guess is likely wrong.
263n/a if os.environ['USERNAME'] != username:
264n/a drv, root, parts = self.parse_parts((userhome,))
265n/a if parts[-1] != os.environ['USERNAME']:
266n/a raise RuntimeError("Can't determine home directory "
267n/a "for %r" % username)
268n/a parts[-1] = username
269n/a if drv or root:
270n/a userhome = drv + root + self.join(parts[1:])
271n/a else:
272n/a userhome = self.join(parts)
273n/a return userhome
274n/a
275n/aclass _PosixFlavour(_Flavour):
276n/a sep = '/'
277n/a altsep = ''
278n/a has_drv = False
279n/a pathmod = posixpath
280n/a
281n/a is_supported = (os.name != 'nt')
282n/a
283n/a def splitroot(self, part, sep=sep):
284n/a if part and part[0] == sep:
285n/a stripped_part = part.lstrip(sep)
286n/a # According to POSIX path resolution:
287n/a # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
288n/a # "A pathname that begins with two successive slashes may be
289n/a # interpreted in an implementation-defined manner, although more
290n/a # than two leading slashes shall be treated as a single slash".
291n/a if len(part) - len(stripped_part) == 2:
292n/a return '', sep * 2, stripped_part
293n/a else:
294n/a return '', sep, stripped_part
295n/a else:
296n/a return '', '', part
297n/a
298n/a def casefold(self, s):
299n/a return s
300n/a
301n/a def casefold_parts(self, parts):
302n/a return parts
303n/a
304n/a def resolve(self, path, strict=False):
305n/a sep = self.sep
306n/a accessor = path._accessor
307n/a seen = {}
308n/a def _resolve(path, rest):
309n/a if rest.startswith(sep):
310n/a path = ''
311n/a
312n/a for name in rest.split(sep):
313n/a if not name or name == '.':
314n/a # current dir
315n/a continue
316n/a if name == '..':
317n/a # parent dir
318n/a path, _, _ = path.rpartition(sep)
319n/a continue
320n/a newpath = path + sep + name
321n/a if newpath in seen:
322n/a # Already seen this path
323n/a path = seen[newpath]
324n/a if path is not None:
325n/a # use cached value
326n/a continue
327n/a # The symlink is not resolved, so we must have a symlink loop.
328n/a raise RuntimeError("Symlink loop from %r" % newpath)
329n/a # Resolve the symbolic link
330n/a try:
331n/a target = accessor.readlink(newpath)
332n/a except OSError as e:
333n/a if e.errno != EINVAL:
334n/a if strict:
335n/a raise
336n/a else:
337n/a return newpath
338n/a # Not a symlink
339n/a path = newpath
340n/a else:
341n/a seen[newpath] = None # not resolved symlink
342n/a path = _resolve(path, target)
343n/a seen[newpath] = path # resolved symlink
344n/a
345n/a return path
346n/a # NOTE: according to POSIX, getcwd() cannot contain path components
347n/a # which are symlinks.
348n/a base = '' if path.is_absolute() else os.getcwd()
349n/a return _resolve(base, str(path)) or sep
350n/a
351n/a def is_reserved(self, parts):
352n/a return False
353n/a
354n/a def make_uri(self, path):
355n/a # We represent the path using the local filesystem encoding,
356n/a # for portability to other applications.
357n/a bpath = bytes(path)
358n/a return 'file://' + urlquote_from_bytes(bpath)
359n/a
360n/a def gethomedir(self, username):
361n/a if not username:
362n/a try:
363n/a return os.environ['HOME']
364n/a except KeyError:
365n/a import pwd
366n/a return pwd.getpwuid(os.getuid()).pw_dir
367n/a else:
368n/a import pwd
369n/a try:
370n/a return pwd.getpwnam(username).pw_dir
371n/a except KeyError:
372n/a raise RuntimeError("Can't determine home directory "
373n/a "for %r" % username)
374n/a
375n/a
376n/a_windows_flavour = _WindowsFlavour()
377n/a_posix_flavour = _PosixFlavour()
378n/a
379n/a
380n/aclass _Accessor:
381n/a """An accessor implements a particular (system-specific or not) way of
382n/a accessing paths on the filesystem."""
383n/a
384n/a
385n/aclass _NormalAccessor(_Accessor):
386n/a
387n/a def _wrap_strfunc(strfunc):
388n/a @functools.wraps(strfunc)
389n/a def wrapped(pathobj, *args):
390n/a return strfunc(str(pathobj), *args)
391n/a return staticmethod(wrapped)
392n/a
393n/a def _wrap_binary_strfunc(strfunc):
394n/a @functools.wraps(strfunc)
395n/a def wrapped(pathobjA, pathobjB, *args):
396n/a return strfunc(str(pathobjA), str(pathobjB), *args)
397n/a return staticmethod(wrapped)
398n/a
399n/a stat = _wrap_strfunc(os.stat)
400n/a
401n/a lstat = _wrap_strfunc(os.lstat)
402n/a
403n/a open = _wrap_strfunc(os.open)
404n/a
405n/a listdir = _wrap_strfunc(os.listdir)
406n/a
407n/a scandir = _wrap_strfunc(os.scandir)
408n/a
409n/a chmod = _wrap_strfunc(os.chmod)
410n/a
411n/a if hasattr(os, "lchmod"):
412n/a lchmod = _wrap_strfunc(os.lchmod)
413n/a else:
414n/a def lchmod(self, pathobj, mode):
415n/a raise NotImplementedError("lchmod() not available on this system")
416n/a
417n/a mkdir = _wrap_strfunc(os.mkdir)
418n/a
419n/a unlink = _wrap_strfunc(os.unlink)
420n/a
421n/a rmdir = _wrap_strfunc(os.rmdir)
422n/a
423n/a rename = _wrap_binary_strfunc(os.rename)
424n/a
425n/a replace = _wrap_binary_strfunc(os.replace)
426n/a
427n/a if nt:
428n/a if supports_symlinks:
429n/a symlink = _wrap_binary_strfunc(os.symlink)
430n/a else:
431n/a def symlink(a, b, target_is_directory):
432n/a raise NotImplementedError("symlink() not available on this system")
433n/a else:
434n/a # Under POSIX, os.symlink() takes two args
435n/a @staticmethod
436n/a def symlink(a, b, target_is_directory):
437n/a return os.symlink(str(a), str(b))
438n/a
439n/a utime = _wrap_strfunc(os.utime)
440n/a
441n/a # Helper for resolve()
442n/a def readlink(self, path):
443n/a return os.readlink(path)
444n/a
445n/a
446n/a_normal_accessor = _NormalAccessor()
447n/a
448n/a
449n/a#
450n/a# Globbing helpers
451n/a#
452n/a
453n/adef _make_selector(pattern_parts):
454n/a pat = pattern_parts[0]
455n/a child_parts = pattern_parts[1:]
456n/a if pat == '**':
457n/a cls = _RecursiveWildcardSelector
458n/a elif '**' in pat:
459n/a raise ValueError("Invalid pattern: '**' can only be an entire path component")
460n/a elif _is_wildcard_pattern(pat):
461n/a cls = _WildcardSelector
462n/a else:
463n/a cls = _PreciseSelector
464n/a return cls(pat, child_parts)
465n/a
466n/aif hasattr(functools, "lru_cache"):
467n/a _make_selector = functools.lru_cache()(_make_selector)
468n/a
469n/a
470n/aclass _Selector:
471n/a """A selector matches a specific glob pattern part against the children
472n/a of a given path."""
473n/a
474n/a def __init__(self, child_parts):
475n/a self.child_parts = child_parts
476n/a if child_parts:
477n/a self.successor = _make_selector(child_parts)
478n/a self.dironly = True
479n/a else:
480n/a self.successor = _TerminatingSelector()
481n/a self.dironly = False
482n/a
483n/a def select_from(self, parent_path):
484n/a """Iterate over all child paths of `parent_path` matched by this
485n/a selector. This can contain parent_path itself."""
486n/a path_cls = type(parent_path)
487n/a is_dir = path_cls.is_dir
488n/a exists = path_cls.exists
489n/a scandir = parent_path._accessor.scandir
490n/a if not is_dir(parent_path):
491n/a return iter([])
492n/a return self._select_from(parent_path, is_dir, exists, scandir)
493n/a
494n/a
495n/aclass _TerminatingSelector:
496n/a
497n/a def _select_from(self, parent_path, is_dir, exists, scandir):
498n/a yield parent_path
499n/a
500n/a
501n/aclass _PreciseSelector(_Selector):
502n/a
503n/a def __init__(self, name, child_parts):
504n/a self.name = name
505n/a _Selector.__init__(self, child_parts)
506n/a
507n/a def _select_from(self, parent_path, is_dir, exists, scandir):
508n/a try:
509n/a path = parent_path._make_child_relpath(self.name)
510n/a if (is_dir if self.dironly else exists)(path):
511n/a for p in self.successor._select_from(path, is_dir, exists, scandir):
512n/a yield p
513n/a except PermissionError:
514n/a return
515n/a
516n/a
517n/aclass _WildcardSelector(_Selector):
518n/a
519n/a def __init__(self, pat, child_parts):
520n/a self.pat = re.compile(fnmatch.translate(pat))
521n/a _Selector.__init__(self, child_parts)
522n/a
523n/a def _select_from(self, parent_path, is_dir, exists, scandir):
524n/a try:
525n/a cf = parent_path._flavour.casefold
526n/a entries = list(scandir(parent_path))
527n/a for entry in entries:
528n/a if not self.dironly or entry.is_dir():
529n/a name = entry.name
530n/a casefolded = cf(name)
531n/a if self.pat.match(casefolded):
532n/a path = parent_path._make_child_relpath(name)
533n/a for p in self.successor._select_from(path, is_dir, exists, scandir):
534n/a yield p
535n/a except PermissionError:
536n/a return
537n/a
538n/a
539n/a
540n/aclass _RecursiveWildcardSelector(_Selector):
541n/a
542n/a def __init__(self, pat, child_parts):
543n/a _Selector.__init__(self, child_parts)
544n/a
545n/a def _iterate_directories(self, parent_path, is_dir, scandir):
546n/a yield parent_path
547n/a try:
548n/a entries = list(scandir(parent_path))
549n/a for entry in entries:
550n/a if entry.is_dir() and not entry.is_symlink():
551n/a path = parent_path._make_child_relpath(entry.name)
552n/a for p in self._iterate_directories(path, is_dir, scandir):
553n/a yield p
554n/a except PermissionError:
555n/a return
556n/a
557n/a def _select_from(self, parent_path, is_dir, exists, scandir):
558n/a try:
559n/a yielded = set()
560n/a try:
561n/a successor_select = self.successor._select_from
562n/a for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
563n/a for p in successor_select(starting_point, is_dir, exists, scandir):
564n/a if p not in yielded:
565n/a yield p
566n/a yielded.add(p)
567n/a finally:
568n/a yielded.clear()
569n/a except PermissionError:
570n/a return
571n/a
572n/a
573n/a#
574n/a# Public API
575n/a#
576n/a
577n/aclass _PathParents(Sequence):
578n/a """This object provides sequence-like access to the logical ancestors
579n/a of a path. Don't try to construct it yourself."""
580n/a __slots__ = ('_pathcls', '_drv', '_root', '_parts')
581n/a
582n/a def __init__(self, path):
583n/a # We don't store the instance to avoid reference cycles
584n/a self._pathcls = type(path)
585n/a self._drv = path._drv
586n/a self._root = path._root
587n/a self._parts = path._parts
588n/a
589n/a def __len__(self):
590n/a if self._drv or self._root:
591n/a return len(self._parts) - 1
592n/a else:
593n/a return len(self._parts)
594n/a
595n/a def __getitem__(self, idx):
596n/a if idx < 0 or idx >= len(self):
597n/a raise IndexError(idx)
598n/a return self._pathcls._from_parsed_parts(self._drv, self._root,
599n/a self._parts[:-idx - 1])
600n/a
601n/a def __repr__(self):
602n/a return "<{}.parents>".format(self._pathcls.__name__)
603n/a
604n/a
605n/aclass PurePath(object):
606n/a """PurePath represents a filesystem path and offers operations which
607n/a don't imply any actual filesystem I/O. Depending on your system,
608n/a instantiating a PurePath will return either a PurePosixPath or a
609n/a PureWindowsPath object. You can also instantiate either of these classes
610n/a directly, regardless of your system.
611n/a """
612n/a __slots__ = (
613n/a '_drv', '_root', '_parts',
614n/a '_str', '_hash', '_pparts', '_cached_cparts',
615n/a )
616n/a
617n/a def __new__(cls, *args):
618n/a """Construct a PurePath from one or several strings and or existing
619n/a PurePath objects. The strings and path objects are combined so as
620n/a to yield a canonicalized path, which is incorporated into the
621n/a new PurePath object.
622n/a """
623n/a if cls is PurePath:
624n/a cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
625n/a return cls._from_parts(args)
626n/a
627n/a def __reduce__(self):
628n/a # Using the parts tuple helps share interned path parts
629n/a # when pickling related paths.
630n/a return (self.__class__, tuple(self._parts))
631n/a
632n/a @classmethod
633n/a def _parse_args(cls, args):
634n/a # This is useful when you don't want to create an instance, just
635n/a # canonicalize some constructor arguments.
636n/a parts = []
637n/a for a in args:
638n/a if isinstance(a, PurePath):
639n/a parts += a._parts
640n/a else:
641n/a a = os.fspath(a)
642n/a if isinstance(a, str):
643n/a # Force-cast str subclasses to str (issue #21127)
644n/a parts.append(str(a))
645n/a else:
646n/a raise TypeError(
647n/a "argument should be a str object or an os.PathLike "
648n/a "object returning str, not %r"
649n/a % type(a))
650n/a return cls._flavour.parse_parts(parts)
651n/a
652n/a @classmethod
653n/a def _from_parts(cls, args, init=True):
654n/a # We need to call _parse_args on the instance, so as to get the
655n/a # right flavour.
656n/a self = object.__new__(cls)
657n/a drv, root, parts = self._parse_args(args)
658n/a self._drv = drv
659n/a self._root = root
660n/a self._parts = parts
661n/a if init:
662n/a self._init()
663n/a return self
664n/a
665n/a @classmethod
666n/a def _from_parsed_parts(cls, drv, root, parts, init=True):
667n/a self = object.__new__(cls)
668n/a self._drv = drv
669n/a self._root = root
670n/a self._parts = parts
671n/a if init:
672n/a self._init()
673n/a return self
674n/a
675n/a @classmethod
676n/a def _format_parsed_parts(cls, drv, root, parts):
677n/a if drv or root:
678n/a return drv + root + cls._flavour.join(parts[1:])
679n/a else:
680n/a return cls._flavour.join(parts)
681n/a
682n/a def _init(self):
683n/a # Overridden in concrete Path
684n/a pass
685n/a
686n/a def _make_child(self, args):
687n/a drv, root, parts = self._parse_args(args)
688n/a drv, root, parts = self._flavour.join_parsed_parts(
689n/a self._drv, self._root, self._parts, drv, root, parts)
690n/a return self._from_parsed_parts(drv, root, parts)
691n/a
692n/a def __str__(self):
693n/a """Return the string representation of the path, suitable for
694n/a passing to system calls."""
695n/a try:
696n/a return self._str
697n/a except AttributeError:
698n/a self._str = self._format_parsed_parts(self._drv, self._root,
699n/a self._parts) or '.'
700n/a return self._str
701n/a
702n/a def __fspath__(self):
703n/a return str(self)
704n/a
705n/a def as_posix(self):
706n/a """Return the string representation of the path with forward (/)
707n/a slashes."""
708n/a f = self._flavour
709n/a return str(self).replace(f.sep, '/')
710n/a
711n/a def __bytes__(self):
712n/a """Return the bytes representation of the path. This is only
713n/a recommended to use under Unix."""
714n/a return os.fsencode(str(self))
715n/a
716n/a def __repr__(self):
717n/a return "{}({!r})".format(self.__class__.__name__, self.as_posix())
718n/a
719n/a def as_uri(self):
720n/a """Return the path as a 'file' URI."""
721n/a if not self.is_absolute():
722n/a raise ValueError("relative path can't be expressed as a file URI")
723n/a return self._flavour.make_uri(self)
724n/a
725n/a @property
726n/a def _cparts(self):
727n/a # Cached casefolded parts, for hashing and comparison
728n/a try:
729n/a return self._cached_cparts
730n/a except AttributeError:
731n/a self._cached_cparts = self._flavour.casefold_parts(self._parts)
732n/a return self._cached_cparts
733n/a
734n/a def __eq__(self, other):
735n/a if not isinstance(other, PurePath):
736n/a return NotImplemented
737n/a return self._cparts == other._cparts and self._flavour is other._flavour
738n/a
739n/a def __hash__(self):
740n/a try:
741n/a return self._hash
742n/a except AttributeError:
743n/a self._hash = hash(tuple(self._cparts))
744n/a return self._hash
745n/a
746n/a def __lt__(self, other):
747n/a if not isinstance(other, PurePath) or self._flavour is not other._flavour:
748n/a return NotImplemented
749n/a return self._cparts < other._cparts
750n/a
751n/a def __le__(self, other):
752n/a if not isinstance(other, PurePath) or self._flavour is not other._flavour:
753n/a return NotImplemented
754n/a return self._cparts <= other._cparts
755n/a
756n/a def __gt__(self, other):
757n/a if not isinstance(other, PurePath) or self._flavour is not other._flavour:
758n/a return NotImplemented
759n/a return self._cparts > other._cparts
760n/a
761n/a def __ge__(self, other):
762n/a if not isinstance(other, PurePath) or self._flavour is not other._flavour:
763n/a return NotImplemented
764n/a return self._cparts >= other._cparts
765n/a
766n/a drive = property(attrgetter('_drv'),
767n/a doc="""The drive prefix (letter or UNC path), if any.""")
768n/a
769n/a root = property(attrgetter('_root'),
770n/a doc="""The root of the path, if any.""")
771n/a
772n/a @property
773n/a def anchor(self):
774n/a """The concatenation of the drive and root, or ''."""
775n/a anchor = self._drv + self._root
776n/a return anchor
777n/a
778n/a @property
779n/a def name(self):
780n/a """The final path component, if any."""
781n/a parts = self._parts
782n/a if len(parts) == (1 if (self._drv or self._root) else 0):
783n/a return ''
784n/a return parts[-1]
785n/a
786n/a @property
787n/a def suffix(self):
788n/a """The final component's last suffix, if any."""
789n/a name = self.name
790n/a i = name.rfind('.')
791n/a if 0 < i < len(name) - 1:
792n/a return name[i:]
793n/a else:
794n/a return ''
795n/a
796n/a @property
797n/a def suffixes(self):
798n/a """A list of the final component's suffixes, if any."""
799n/a name = self.name
800n/a if name.endswith('.'):
801n/a return []
802n/a name = name.lstrip('.')
803n/a return ['.' + suffix for suffix in name.split('.')[1:]]
804n/a
805n/a @property
806n/a def stem(self):
807n/a """The final path component, minus its last suffix."""
808n/a name = self.name
809n/a i = name.rfind('.')
810n/a if 0 < i < len(name) - 1:
811n/a return name[:i]
812n/a else:
813n/a return name
814n/a
815n/a def with_name(self, name):
816n/a """Return a new path with the file name changed."""
817n/a if not self.name:
818n/a raise ValueError("%r has an empty name" % (self,))
819n/a drv, root, parts = self._flavour.parse_parts((name,))
820n/a if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
821n/a or drv or root or len(parts) != 1):
822n/a raise ValueError("Invalid name %r" % (name))
823n/a return self._from_parsed_parts(self._drv, self._root,
824n/a self._parts[:-1] + [name])
825n/a
826n/a def with_suffix(self, suffix):
827n/a """Return a new path with the file suffix changed (or added, if none)."""
828n/a # XXX if suffix is None, should the current suffix be removed?
829n/a f = self._flavour
830n/a if f.sep in suffix or f.altsep and f.altsep in suffix:
831n/a raise ValueError("Invalid suffix %r" % (suffix))
832n/a if suffix and not suffix.startswith('.') or suffix == '.':
833n/a raise ValueError("Invalid suffix %r" % (suffix))
834n/a name = self.name
835n/a if not name:
836n/a raise ValueError("%r has an empty name" % (self,))
837n/a old_suffix = self.suffix
838n/a if not old_suffix:
839n/a name = name + suffix
840n/a else:
841n/a name = name[:-len(old_suffix)] + suffix
842n/a return self._from_parsed_parts(self._drv, self._root,
843n/a self._parts[:-1] + [name])
844n/a
845n/a def relative_to(self, *other):
846n/a """Return the relative path to another path identified by the passed
847n/a arguments. If the operation is not possible (because this is not
848n/a a subpath of the other path), raise ValueError.
849n/a """
850n/a # For the purpose of this method, drive and root are considered
851n/a # separate parts, i.e.:
852n/a # Path('c:/').relative_to('c:') gives Path('/')
853n/a # Path('c:/').relative_to('/') raise ValueError
854n/a if not other:
855n/a raise TypeError("need at least one argument")
856n/a parts = self._parts
857n/a drv = self._drv
858n/a root = self._root
859n/a if root:
860n/a abs_parts = [drv, root] + parts[1:]
861n/a else:
862n/a abs_parts = parts
863n/a to_drv, to_root, to_parts = self._parse_args(other)
864n/a if to_root:
865n/a to_abs_parts = [to_drv, to_root] + to_parts[1:]
866n/a else:
867n/a to_abs_parts = to_parts
868n/a n = len(to_abs_parts)
869n/a cf = self._flavour.casefold_parts
870n/a if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
871n/a formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
872n/a raise ValueError("{!r} does not start with {!r}"
873n/a .format(str(self), str(formatted)))
874n/a return self._from_parsed_parts('', root if n == 1 else '',
875n/a abs_parts[n:])
876n/a
877n/a @property
878n/a def parts(self):
879n/a """An object providing sequence-like access to the
880n/a components in the filesystem path."""
881n/a # We cache the tuple to avoid building a new one each time .parts
882n/a # is accessed. XXX is this necessary?
883n/a try:
884n/a return self._pparts
885n/a except AttributeError:
886n/a self._pparts = tuple(self._parts)
887n/a return self._pparts
888n/a
889n/a def joinpath(self, *args):
890n/a """Combine this path with one or several arguments, and return a
891n/a new path representing either a subpath (if all arguments are relative
892n/a paths) or a totally different path (if one of the arguments is
893n/a anchored).
894n/a """
895n/a return self._make_child(args)
896n/a
897n/a def __truediv__(self, key):
898n/a return self._make_child((key,))
899n/a
900n/a def __rtruediv__(self, key):
901n/a return self._from_parts([key] + self._parts)
902n/a
903n/a @property
904n/a def parent(self):
905n/a """The logical parent of the path."""
906n/a drv = self._drv
907n/a root = self._root
908n/a parts = self._parts
909n/a if len(parts) == 1 and (drv or root):
910n/a return self
911n/a return self._from_parsed_parts(drv, root, parts[:-1])
912n/a
913n/a @property
914n/a def parents(self):
915n/a """A sequence of this path's logical parents."""
916n/a return _PathParents(self)
917n/a
918n/a def is_absolute(self):
919n/a """True if the path is absolute (has both a root and, if applicable,
920n/a a drive)."""
921n/a if not self._root:
922n/a return False
923n/a return not self._flavour.has_drv or bool(self._drv)
924n/a
925n/a def is_reserved(self):
926n/a """Return True if the path contains one of the special names reserved
927n/a by the system, if any."""
928n/a return self._flavour.is_reserved(self._parts)
929n/a
930n/a def match(self, path_pattern):
931n/a """
932n/a Return True if this path matches the given pattern.
933n/a """
934n/a cf = self._flavour.casefold
935n/a path_pattern = cf(path_pattern)
936n/a drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
937n/a if not pat_parts:
938n/a raise ValueError("empty pattern")
939n/a if drv and drv != cf(self._drv):
940n/a return False
941n/a if root and root != cf(self._root):
942n/a return False
943n/a parts = self._cparts
944n/a if drv or root:
945n/a if len(pat_parts) != len(parts):
946n/a return False
947n/a pat_parts = pat_parts[1:]
948n/a elif len(pat_parts) > len(parts):
949n/a return False
950n/a for part, pat in zip(reversed(parts), reversed(pat_parts)):
951n/a if not fnmatch.fnmatchcase(part, pat):
952n/a return False
953n/a return True
954n/a
955n/a# Can't subclass os.PathLike from PurePath and keep the constructor
956n/a# optimizations in PurePath._parse_args().
957n/aos.PathLike.register(PurePath)
958n/a
959n/a
960n/aclass PurePosixPath(PurePath):
961n/a _flavour = _posix_flavour
962n/a __slots__ = ()
963n/a
964n/a
965n/aclass PureWindowsPath(PurePath):
966n/a _flavour = _windows_flavour
967n/a __slots__ = ()
968n/a
969n/a
970n/a# Filesystem-accessing classes
971n/a
972n/a
973n/aclass Path(PurePath):
974n/a __slots__ = (
975n/a '_accessor',
976n/a '_closed',
977n/a )
978n/a
979n/a def __new__(cls, *args, **kwargs):
980n/a if cls is Path:
981n/a cls = WindowsPath if os.name == 'nt' else PosixPath
982n/a self = cls._from_parts(args, init=False)
983n/a if not self._flavour.is_supported:
984n/a raise NotImplementedError("cannot instantiate %r on your system"
985n/a % (cls.__name__,))
986n/a self._init()
987n/a return self
988n/a
989n/a def _init(self,
990n/a # Private non-constructor arguments
991n/a template=None,
992n/a ):
993n/a self._closed = False
994n/a if template is not None:
995n/a self._accessor = template._accessor
996n/a else:
997n/a self._accessor = _normal_accessor
998n/a
999n/a def _make_child_relpath(self, part):
1000n/a # This is an optimization used for dir walking. `part` must be
1001n/a # a single part relative to this path.
1002n/a parts = self._parts + [part]
1003n/a return self._from_parsed_parts(self._drv, self._root, parts)
1004n/a
1005n/a def __enter__(self):
1006n/a if self._closed:
1007n/a self._raise_closed()
1008n/a return self
1009n/a
1010n/a def __exit__(self, t, v, tb):
1011n/a self._closed = True
1012n/a
1013n/a def _raise_closed(self):
1014n/a raise ValueError("I/O operation on closed path")
1015n/a
1016n/a def _opener(self, name, flags, mode=0o666):
1017n/a # A stub for the opener argument to built-in open()
1018n/a return self._accessor.open(self, flags, mode)
1019n/a
1020n/a def _raw_open(self, flags, mode=0o777):
1021n/a """
1022n/a Open the file pointed by this path and return a file descriptor,
1023n/a as os.open() does.
1024n/a """
1025n/a if self._closed:
1026n/a self._raise_closed()
1027n/a return self._accessor.open(self, flags, mode)
1028n/a
1029n/a # Public API
1030n/a
1031n/a @classmethod
1032n/a def cwd(cls):
1033n/a """Return a new path pointing to the current working directory
1034n/a (as returned by os.getcwd()).
1035n/a """
1036n/a return cls(os.getcwd())
1037n/a
1038n/a @classmethod
1039n/a def home(cls):
1040n/a """Return a new path pointing to the user's home directory (as
1041n/a returned by os.path.expanduser('~')).
1042n/a """
1043n/a return cls(cls()._flavour.gethomedir(None))
1044n/a
1045n/a def samefile(self, other_path):
1046n/a """Return whether other_path is the same or not as this file
1047n/a (as returned by os.path.samefile()).
1048n/a """
1049n/a st = self.stat()
1050n/a try:
1051n/a other_st = other_path.stat()
1052n/a except AttributeError:
1053n/a other_st = os.stat(other_path)
1054n/a return os.path.samestat(st, other_st)
1055n/a
1056n/a def iterdir(self):
1057n/a """Iterate over the files in this directory. Does not yield any
1058n/a result for the special paths '.' and '..'.
1059n/a """
1060n/a if self._closed:
1061n/a self._raise_closed()
1062n/a for name in self._accessor.listdir(self):
1063n/a if name in {'.', '..'}:
1064n/a # Yielding a path object for these makes little sense
1065n/a continue
1066n/a yield self._make_child_relpath(name)
1067n/a if self._closed:
1068n/a self._raise_closed()
1069n/a
1070n/a def glob(self, pattern):
1071n/a """Iterate over this subtree and yield all existing files (of any
1072n/a kind, including directories) matching the given pattern.
1073n/a """
1074n/a if not pattern:
1075n/a raise ValueError("Unacceptable pattern: {!r}".format(pattern))
1076n/a pattern = self._flavour.casefold(pattern)
1077n/a drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1078n/a if drv or root:
1079n/a raise NotImplementedError("Non-relative patterns are unsupported")
1080n/a selector = _make_selector(tuple(pattern_parts))
1081n/a for p in selector.select_from(self):
1082n/a yield p
1083n/a
1084n/a def rglob(self, pattern):
1085n/a """Recursively yield all existing files (of any kind, including
1086n/a directories) matching the given pattern, anywhere in this subtree.
1087n/a """
1088n/a pattern = self._flavour.casefold(pattern)
1089n/a drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1090n/a if drv or root:
1091n/a raise NotImplementedError("Non-relative patterns are unsupported")
1092n/a selector = _make_selector(("**",) + tuple(pattern_parts))
1093n/a for p in selector.select_from(self):
1094n/a yield p
1095n/a
1096n/a def absolute(self):
1097n/a """Return an absolute version of this path. This function works
1098n/a even if the path doesn't point to anything.
1099n/a
1100n/a No normalization is done, i.e. all '.' and '..' will be kept along.
1101n/a Use resolve() to get the canonical path to a file.
1102n/a """
1103n/a # XXX untested yet!
1104n/a if self._closed:
1105n/a self._raise_closed()
1106n/a if self.is_absolute():
1107n/a return self
1108n/a # FIXME this must defer to the specific flavour (and, under Windows,
1109n/a # use nt._getfullpathname())
1110n/a obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1111n/a obj._init(template=self)
1112n/a return obj
1113n/a
1114n/a def resolve(self, strict=False):
1115n/a """
1116n/a Make the path absolute, resolving all symlinks on the way and also
1117n/a normalizing it (for example turning slashes into backslashes under
1118n/a Windows).
1119n/a """
1120n/a if self._closed:
1121n/a self._raise_closed()
1122n/a s = self._flavour.resolve(self, strict=strict)
1123n/a if s is None:
1124n/a # No symlink resolution => for consistency, raise an error if
1125n/a # the path doesn't exist or is forbidden
1126n/a self.stat()
1127n/a s = str(self.absolute())
1128n/a # Now we have no symlinks in the path, it's safe to normalize it.
1129n/a normed = self._flavour.pathmod.normpath(s)
1130n/a obj = self._from_parts((normed,), init=False)
1131n/a obj._init(template=self)
1132n/a return obj
1133n/a
1134n/a def stat(self):
1135n/a """
1136n/a Return the result of the stat() system call on this path, like
1137n/a os.stat() does.
1138n/a """
1139n/a return self._accessor.stat(self)
1140n/a
1141n/a def owner(self):
1142n/a """
1143n/a Return the login name of the file owner.
1144n/a """
1145n/a import pwd
1146n/a return pwd.getpwuid(self.stat().st_uid).pw_name
1147n/a
1148n/a def group(self):
1149n/a """
1150n/a Return the group name of the file gid.
1151n/a """
1152n/a import grp
1153n/a return grp.getgrgid(self.stat().st_gid).gr_name
1154n/a
1155n/a def open(self, mode='r', buffering=-1, encoding=None,
1156n/a errors=None, newline=None):
1157n/a """
1158n/a Open the file pointed by this path and return a file object, as
1159n/a the built-in open() function does.
1160n/a """
1161n/a if self._closed:
1162n/a self._raise_closed()
1163n/a return io.open(str(self), mode, buffering, encoding, errors, newline,
1164n/a opener=self._opener)
1165n/a
1166n/a def read_bytes(self):
1167n/a """
1168n/a Open the file in bytes mode, read it, and close the file.
1169n/a """
1170n/a with self.open(mode='rb') as f:
1171n/a return f.read()
1172n/a
1173n/a def read_text(self, encoding=None, errors=None):
1174n/a """
1175n/a Open the file in text mode, read it, and close the file.
1176n/a """
1177n/a with self.open(mode='r', encoding=encoding, errors=errors) as f:
1178n/a return f.read()
1179n/a
1180n/a def write_bytes(self, data):
1181n/a """
1182n/a Open the file in bytes mode, write to it, and close the file.
1183n/a """
1184n/a # type-check for the buffer interface before truncating the file
1185n/a view = memoryview(data)
1186n/a with self.open(mode='wb') as f:
1187n/a return f.write(view)
1188n/a
1189n/a def write_text(self, data, encoding=None, errors=None):
1190n/a """
1191n/a Open the file in text mode, write to it, and close the file.
1192n/a """
1193n/a if not isinstance(data, str):
1194n/a raise TypeError('data must be str, not %s' %
1195n/a data.__class__.__name__)
1196n/a with self.open(mode='w', encoding=encoding, errors=errors) as f:
1197n/a return f.write(data)
1198n/a
1199n/a def touch(self, mode=0o666, exist_ok=True):
1200n/a """
1201n/a Create this file with the given access mode, if it doesn't exist.
1202n/a """
1203n/a if self._closed:
1204n/a self._raise_closed()
1205n/a if exist_ok:
1206n/a # First try to bump modification time
1207n/a # Implementation note: GNU touch uses the UTIME_NOW option of
1208n/a # the utimensat() / futimens() functions.
1209n/a try:
1210n/a self._accessor.utime(self, None)
1211n/a except OSError:
1212n/a # Avoid exception chaining
1213n/a pass
1214n/a else:
1215n/a return
1216n/a flags = os.O_CREAT | os.O_WRONLY
1217n/a if not exist_ok:
1218n/a flags |= os.O_EXCL
1219n/a fd = self._raw_open(flags, mode)
1220n/a os.close(fd)
1221n/a
1222n/a def mkdir(self, mode=0o777, parents=False, exist_ok=False):
1223n/a if self._closed:
1224n/a self._raise_closed()
1225n/a if not parents:
1226n/a try:
1227n/a self._accessor.mkdir(self, mode)
1228n/a except FileExistsError:
1229n/a if not exist_ok or not self.is_dir():
1230n/a raise
1231n/a else:
1232n/a try:
1233n/a self._accessor.mkdir(self, mode)
1234n/a except FileExistsError:
1235n/a if not exist_ok or not self.is_dir():
1236n/a raise
1237n/a except OSError as e:
1238n/a if e.errno != ENOENT or self.parent == self:
1239n/a raise
1240n/a self.parent.mkdir(parents=True)
1241n/a self._accessor.mkdir(self, mode)
1242n/a
1243n/a def chmod(self, mode):
1244n/a """
1245n/a Change the permissions of the path, like os.chmod().
1246n/a """
1247n/a if self._closed:
1248n/a self._raise_closed()
1249n/a self._accessor.chmod(self, mode)
1250n/a
1251n/a def lchmod(self, mode):
1252n/a """
1253n/a Like chmod(), except if the path points to a symlink, the symlink's
1254n/a permissions are changed, rather than its target's.
1255n/a """
1256n/a if self._closed:
1257n/a self._raise_closed()
1258n/a self._accessor.lchmod(self, mode)
1259n/a
1260n/a def unlink(self):
1261n/a """
1262n/a Remove this file or link.
1263n/a If the path is a directory, use rmdir() instead.
1264n/a """
1265n/a if self._closed:
1266n/a self._raise_closed()
1267n/a self._accessor.unlink(self)
1268n/a
1269n/a def rmdir(self):
1270n/a """
1271n/a Remove this directory. The directory must be empty.
1272n/a """
1273n/a if self._closed:
1274n/a self._raise_closed()
1275n/a self._accessor.rmdir(self)
1276n/a
1277n/a def lstat(self):
1278n/a """
1279n/a Like stat(), except if the path points to a symlink, the symlink's
1280n/a status information is returned, rather than its target's.
1281n/a """
1282n/a if self._closed:
1283n/a self._raise_closed()
1284n/a return self._accessor.lstat(self)
1285n/a
1286n/a def rename(self, target):
1287n/a """
1288n/a Rename this path to the given path.
1289n/a """
1290n/a if self._closed:
1291n/a self._raise_closed()
1292n/a self._accessor.rename(self, target)
1293n/a
1294n/a def replace(self, target):
1295n/a """
1296n/a Rename this path to the given path, clobbering the existing
1297n/a destination if it exists.
1298n/a """
1299n/a if self._closed:
1300n/a self._raise_closed()
1301n/a self._accessor.replace(self, target)
1302n/a
1303n/a def symlink_to(self, target, target_is_directory=False):
1304n/a """
1305n/a Make this path a symlink pointing to the given path.
1306n/a Note the order of arguments (self, target) is the reverse of os.symlink's.
1307n/a """
1308n/a if self._closed:
1309n/a self._raise_closed()
1310n/a self._accessor.symlink(target, self, target_is_directory)
1311n/a
1312n/a # Convenience functions for querying the stat results
1313n/a
1314n/a def exists(self):
1315n/a """
1316n/a Whether this path exists.
1317n/a """
1318n/a try:
1319n/a self.stat()
1320n/a except OSError as e:
1321n/a if e.errno not in (ENOENT, ENOTDIR):
1322n/a raise
1323n/a return False
1324n/a return True
1325n/a
1326n/a def is_dir(self):
1327n/a """
1328n/a Whether this path is a directory.
1329n/a """
1330n/a try:
1331n/a return S_ISDIR(self.stat().st_mode)
1332n/a except OSError as e:
1333n/a if e.errno not in (ENOENT, ENOTDIR):
1334n/a raise
1335n/a # Path doesn't exist or is a broken symlink
1336n/a # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1337n/a return False
1338n/a
1339n/a def is_file(self):
1340n/a """
1341n/a Whether this path is a regular file (also True for symlinks pointing
1342n/a to regular files).
1343n/a """
1344n/a try:
1345n/a return S_ISREG(self.stat().st_mode)
1346n/a except OSError as e:
1347n/a if e.errno not in (ENOENT, ENOTDIR):
1348n/a raise
1349n/a # Path doesn't exist or is a broken symlink
1350n/a # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1351n/a return False
1352n/a
1353n/a def is_symlink(self):
1354n/a """
1355n/a Whether this path is a symbolic link.
1356n/a """
1357n/a try:
1358n/a return S_ISLNK(self.lstat().st_mode)
1359n/a except OSError as e:
1360n/a if e.errno not in (ENOENT, ENOTDIR):
1361n/a raise
1362n/a # Path doesn't exist
1363n/a return False
1364n/a
1365n/a def is_block_device(self):
1366n/a """
1367n/a Whether this path is a block device.
1368n/a """
1369n/a try:
1370n/a return S_ISBLK(self.stat().st_mode)
1371n/a except OSError as e:
1372n/a if e.errno not in (ENOENT, ENOTDIR):
1373n/a raise
1374n/a # Path doesn't exist or is a broken symlink
1375n/a # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1376n/a return False
1377n/a
1378n/a def is_char_device(self):
1379n/a """
1380n/a Whether this path is a character device.
1381n/a """
1382n/a try:
1383n/a return S_ISCHR(self.stat().st_mode)
1384n/a except OSError as e:
1385n/a if e.errno not in (ENOENT, ENOTDIR):
1386n/a raise
1387n/a # Path doesn't exist or is a broken symlink
1388n/a # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1389n/a return False
1390n/a
1391n/a def is_fifo(self):
1392n/a """
1393n/a Whether this path is a FIFO.
1394n/a """
1395n/a try:
1396n/a return S_ISFIFO(self.stat().st_mode)
1397n/a except OSError as e:
1398n/a if e.errno not in (ENOENT, ENOTDIR):
1399n/a raise
1400n/a # Path doesn't exist or is a broken symlink
1401n/a # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1402n/a return False
1403n/a
1404n/a def is_socket(self):
1405n/a """
1406n/a Whether this path is a socket.
1407n/a """
1408n/a try:
1409n/a return S_ISSOCK(self.stat().st_mode)
1410n/a except OSError as e:
1411n/a if e.errno not in (ENOENT, ENOTDIR):
1412n/a raise
1413n/a # Path doesn't exist or is a broken symlink
1414n/a # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1415n/a return False
1416n/a
1417n/a def expanduser(self):
1418n/a """ Return a new path with expanded ~ and ~user constructs
1419n/a (as returned by os.path.expanduser)
1420n/a """
1421n/a if (not (self._drv or self._root) and
1422n/a self._parts and self._parts[0][:1] == '~'):
1423n/a homedir = self._flavour.gethomedir(self._parts[0][1:])
1424n/a return self._from_parts([homedir] + self._parts[1:])
1425n/a
1426n/a return self
1427n/a
1428n/a
1429n/aclass PosixPath(Path, PurePosixPath):
1430n/a __slots__ = ()
1431n/a
1432n/aclass WindowsPath(Path, PureWindowsPath):
1433n/a __slots__ = ()
1434n/a
1435n/a def owner(self):
1436n/a raise NotImplementedError("Path.owner() is unsupported on this system")
1437n/a
1438n/a def group(self):
1439n/a raise NotImplementedError("Path.group() is unsupported on this system")