]>
crepu.dev Git - config.git/blob - djavu-asus/elpy/rpc-venv/lib/python3.11/site-packages/pathspec/util.py
2 This module provides utility methods for dealing with path-specs.
12 from collections
.abc
import (
13 Collection
as CollectionType
,
14 Iterable
as IterableType
)
31 from .pattern
import (
34 if sys
.version_info
>= (3, 9):
35 StrPath
= Union
[str, PathLike
[str]]
37 StrPath
= Union
[str, PathLike
]
39 NORMALIZE_PATH_SEPS
= [
41 for __sep
in [os
.sep
, os
.altsep
]
42 if __sep
and __sep
!= posixpath
.sep
45 *NORMALIZE_PATH_SEPS* (:class:`list` of :class:`str`) contains the path
46 separators that need to be normalized to the POSIX separator for the
47 current operating system. The separators are determined by examining
48 :data:`os.sep` and :data:`os.altsep`.
51 _registered_patterns
= {}
53 *_registered_patterns* (:class:`dict`) maps a name (:class:`str`) to the
54 registered pattern factory (:class:`~collections.abc.Callable`).
58 def append_dir_sep(path
: pathlib
.Path
) -> str:
60 Appends the path separator to the path if the path is a directory.
61 This can be used to aid in distinguishing between directories and
62 files on the file-system by relying on the presence of a trailing path
65 *path* (:class:`pathlib.path`) is the path to use.
67 Returns the path (:class:`str`).
76 def detailed_match_files(
77 patterns
: Iterable
[Pattern
],
79 all_matches
: Optional
[bool] = None,
80 ) -> Dict
[str, 'MatchDetail']:
82 Matches the files to the patterns, and returns which patterns matched
85 *patterns* (:class:`~collections.abc.Iterable` of :class:`~pathspec.pattern.Pattern`)
86 contains the patterns to use.
88 *files* (:class:`~collections.abc.Iterable` of :class:`str`) contains
89 the normalized file paths to be matched against *patterns*.
91 *all_matches* (:class:`boot` or :data:`None`) is whether to return all
92 matches patterns (:data:`True`), or only the last matched pattern
93 (:data:`False`). Default is :data:`None` for :data:`False`.
95 Returns the matched files (:class:`dict`) which maps each matched file
96 (:class:`str`) to the patterns that matched in order (:class:`.MatchDetail`).
98 all_files
= files
if isinstance(files
, CollectionType
) else list(files
)
100 for pattern
in patterns
:
101 if pattern
.include
is not None:
102 result_files
= pattern
.match(all_files
) # TODO: Replace with `.match_file()`.
104 # Add files and record pattern.
105 for result_file
in result_files
:
106 if result_file
in return_files
:
108 return_files
[result_file
].patterns
.append(pattern
)
110 return_files
[result_file
].patterns
[0] = pattern
112 return_files
[result_file
] = MatchDetail([pattern
])
116 for file in result_files
:
117 del return_files
[file]
122 def _filter_patterns(patterns
: Iterable
[Pattern
]) -> List
[Pattern
]:
124 Filters out null-patterns.
126 *patterns* (:class:`Iterable` of :class:`.Pattern`) contains the
129 Returns the patterns (:class:`list` of :class:`.Pattern`).
133 for __pat
in patterns
134 if __pat
.include
is not None
138 def _is_iterable(value
: Any
) -> bool:
140 Check whether the value is an iterable (excludes strings).
142 *value* is the value to check,
144 Returns whether *value* is a iterable (:class:`bool`).
146 return isinstance(value
, IterableType
) and not isinstance(value
, (str, bytes
))
149 def iter_tree_entries(
151 on_error
: Optional
[Callable
] = None,
152 follow_links
: Optional
[bool] = None,
153 ) -> Iterator
['TreeEntry']:
155 Walks the specified directory for all files and directories.
157 *root* (:class:`str` or :class:`os.PathLike[str]`) is the root directory to
160 *on_error* (:class:`~collections.abc.Callable` or :data:`None`)
161 optionally is the error handler for file-system exceptions. It will be
162 called with the exception (:exc:`OSError`). Reraise the exception to
163 abort the walk. Default is :data:`None` to ignore file-system
166 *follow_links* (:class:`bool` or :data:`None`) optionally is whether
167 to walk symbolic links that resolve to directories. Default is
168 :data:`None` for :data:`True`.
170 Raises :exc:`RecursionError` if recursion is detected.
172 Returns an :class:`~collections.abc.Iterator` yielding each file or
173 directory entry (:class:`.TreeEntry`) relative to *root*.
175 if on_error
is not None and not callable(on_error
):
176 raise TypeError(f
"on_error:{on_error!r} is not callable.")
178 if follow_links
is None:
181 yield from _iter_tree_entries_next(os
.path
.abspath(root
), '', {}, on_error
, follow_links
)
184 def _iter_tree_entries_next(
187 memo
: Dict
[str, str],
190 ) -> Iterator
['TreeEntry']:
192 Scan the directory for all descendant files.
194 *root_full* (:class:`str`) the absolute path to the root directory.
196 *dir_rel* (:class:`str`) the path to the directory to scan relative to
199 *memo* (:class:`dict`) keeps track of ancestor directories
200 encountered. Maps each ancestor real path (:class:`str`) to relative
203 *on_error* (:class:`~collections.abc.Callable` or :data:`None`)
204 optionally is the error handler for file-system exceptions.
206 *follow_links* (:class:`bool`) is whether to walk symbolic links that
207 resolve to directories.
209 Yields each entry (:class:`.TreeEntry`).
211 dir_full
= os
.path
.join(root_full
, dir_rel
)
212 dir_real
= os
.path
.realpath(dir_full
)
214 # Remember each encountered ancestor directory and its canonical
215 # (real) path. If a canonical path is encountered more than once,
216 # recursion has occurred.
217 if dir_real
not in memo
:
218 memo
[dir_real
] = dir_rel
220 raise RecursionError(real_path
=dir_real
, first_path
=memo
[dir_real
], second_path
=dir_rel
)
222 with os
.scandir(dir_full
) as scan_iter
:
223 node_ent
: os
.DirEntry
224 for node_ent
in scan_iter
:
225 node_rel
= os
.path
.join(dir_rel
, node_ent
.name
)
227 # Inspect child node.
229 node_lstat
= node_ent
.stat(follow_symlinks
=False)
231 if on_error
is not None:
235 if node_ent
.is_symlink():
236 # Child node is a link, inspect the target node.
238 node_stat
= node_ent
.stat()
240 if on_error
is not None:
244 node_stat
= node_lstat
246 if node_ent
.is_dir(follow_symlinks
=follow_links
):
247 # Child node is a directory, recurse into it and yield its
249 yield TreeEntry(node_ent
.name
, node_rel
, node_lstat
, node_stat
)
251 yield from _iter_tree_entries_next(root_full
, node_rel
, memo
, on_error
, follow_links
)
253 elif node_ent
.is_file() or node_ent
.is_symlink():
254 # Child node is either a file or an unfollowed link, yield it.
255 yield TreeEntry(node_ent
.name
, node_rel
, node_lstat
, node_stat
)
257 # NOTE: Make sure to remove the canonical (real) path of the directory
258 # from the ancestors memo once we are done with it. This allows the
259 # same directory to appear multiple times. If this is not done, the
260 # second occurrence of the directory will be incorrectly interpreted
261 # as a recursion. See <https://github.com/cpburnz/python-path-specification/pull/7>.
267 on_error
: Optional
[Callable
] = None,
268 follow_links
: Optional
[bool] = None,
271 Walks the specified directory for all files.
273 *root* (:class:`str` or :class:`os.PathLike[str]`) is the root directory to
276 *on_error* (:class:`~collections.abc.Callable` or :data:`None`)
277 optionally is the error handler for file-system exceptions. It will be
278 called with the exception (:exc:`OSError`). Reraise the exception to
279 abort the walk. Default is :data:`None` to ignore file-system
282 *follow_links* (:class:`bool` or :data:`None`) optionally is whether
283 to walk symbolic links that resolve to directories. Default is
284 :data:`None` for :data:`True`.
286 Raises :exc:`RecursionError` if recursion is detected.
288 Returns an :class:`~collections.abc.Iterator` yielding the path to
289 each file (:class:`str`) relative to *root*.
291 for entry
in iter_tree_entries(root
, on_error
=on_error
, follow_links
=follow_links
):
292 if not entry
.is_dir(follow_links
):
296 def iter_tree(root
, on_error
=None, follow_links
=None):
298 DEPRECATED: The :func:`.iter_tree` function is an alias for the
299 :func:`.iter_tree_files` function.
302 "util.iter_tree() is deprecated. Use util.iter_tree_files() instead."
303 ), DeprecationWarning, stacklevel
=2)
304 return iter_tree_files(root
, on_error
=on_error
, follow_links
=follow_links
)
307 def lookup_pattern(name
: str) -> Callable
[[AnyStr
], Pattern
]:
309 Lookups a registered pattern factory by name.
311 *name* (:class:`str`) is the name of the pattern factory.
313 Returns the registered pattern factory (:class:`~collections.abc.Callable`).
314 If no pattern factory is registered, raises :exc:`KeyError`.
316 return _registered_patterns
[name
]
319 def match_file(patterns
: Iterable
[Pattern
], file: str) -> bool:
321 Matches the file to the patterns.
323 *patterns* (:class:`~collections.abc.Iterable` of :class:`~pathspec.pattern.Pattern`)
324 contains the patterns to use.
326 *file* (:class:`str`) is the normalized file path to be matched
329 Returns :data:`True` if *file* matched; otherwise, :data:`False`.
332 for pattern
in patterns
:
333 if pattern
.include
is not None:
334 if pattern
.match_file(file) is not None:
335 matched
= pattern
.include
341 patterns
: Iterable
[Pattern
],
342 files
: Iterable
[str],
345 DEPRECATED: This is an old function no longer used. Use the :func:`.match_file`
346 function with a loop for better results.
348 Matches the files to the patterns.
350 *patterns* (:class:`~collections.abc.Iterable` of :class:`~pathspec.pattern.Pattern`)
351 contains the patterns to use.
353 *files* (:class:`~collections.abc.Iterable` of :class:`str`) contains
354 the normalized file paths to be matched against *patterns*.
356 Returns the matched files (:class:`set` of :class:`str`).
359 "util.match_files() is deprecated. Use util.match_file() with a "
360 "loop for better results."
361 ), DeprecationWarning, stacklevel
=2)
363 use_patterns
= _filter_patterns(patterns
)
367 if match_file(use_patterns
, file):
368 return_files
.add(file)
375 separators
: Optional
[Collection
[str]] = None,
378 Normalizes the file path to use the POSIX path separator (i.e.,
379 :data:`'/'`), and make the paths relative (remove leading :data:`'/'`).
381 *file* (:class:`str` or :class:`os.PathLike[str]`) is the file path.
383 *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
384 :data:`None`) optionally contains the path separators to normalize.
385 This does not need to include the POSIX path separator (:data:`'/'`),
386 but including it will not affect the results. Default is :data:`None`
387 for :data:`NORMALIZE_PATH_SEPS`. To prevent normalization, pass an
388 empty container (e.g., an empty tuple :data:`()`).
390 Returns the normalized file path (:class:`str`).
392 # Normalize path separators.
393 if separators
is None:
394 separators
= NORMALIZE_PATH_SEPS
396 # Convert path object to string.
397 norm_file
: str = os
.fspath(file)
399 for sep
in separators
:
400 norm_file
= norm_file
.replace(sep
, posixpath
.sep
)
402 if norm_file
.startswith('/'):
403 # Make path relative.
404 norm_file
= norm_file
[1:]
406 elif norm_file
.startswith('./'):
407 # Remove current directory prefix.
408 norm_file
= norm_file
[2:]
414 files
: Iterable
[StrPath
],
415 separators
: Optional
[Collection
[str]] = None,
416 ) -> Dict
[str, List
[StrPath
]]:
418 DEPRECATED: This function is no longer used. Use the :func:`.normalize_file`
419 function with a loop for better results.
421 Normalizes the file paths to use the POSIX path separator.
423 *files* (:class:`~collections.abc.Iterable` of :class:`str` or
424 :class:`os.PathLike[str]`) contains the file paths to be normalized.
426 *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
427 :data:`None`) optionally contains the path separators to normalize.
428 See :func:`normalize_file` for more information.
430 Returns a :class:`dict` mapping each normalized file path (:class:`str`)
431 to the original file paths (:class:`list` of :class:`str` or
432 :class:`os.PathLike[str]`).
435 "util.normalize_files() is deprecated. Use util.normalize_file() "
436 "with a loop for better results."
437 ), DeprecationWarning, stacklevel
=2)
441 norm_file
= normalize_file(path
, separators
=separators
)
442 if norm_file
in norm_files
:
443 norm_files
[norm_file
].append(path
)
445 norm_files
[norm_file
] = [path
]
450 def register_pattern(
452 pattern_factory
: Callable
[[AnyStr
], Pattern
],
453 override
: Optional
[bool] = None,
456 Registers the specified pattern factory.
458 *name* (:class:`str`) is the name to register the pattern factory
461 *pattern_factory* (:class:`~collections.abc.Callable`) is used to
462 compile patterns. It must accept an uncompiled pattern (:class:`str`)
463 and return the compiled pattern (:class:`.Pattern`).
465 *override* (:class:`bool` or :data:`None`) optionally is whether to
466 allow overriding an already registered pattern under the same name
467 (:data:`True`), instead of raising an :exc:`AlreadyRegisteredError`
468 (:data:`False`). Default is :data:`None` for :data:`False`.
470 if not isinstance(name
, str):
471 raise TypeError(f
"name:{name!r} is not a string.")
473 if not callable(pattern_factory
):
474 raise TypeError(f
"pattern_factory:{pattern_factory!r} is not callable.")
476 if name
in _registered_patterns
and not override
:
477 raise AlreadyRegisteredError(name
, _registered_patterns
[name
])
479 _registered_patterns
[name
] = pattern_factory
482 class AlreadyRegisteredError(Exception):
484 The :exc:`AlreadyRegisteredError` exception is raised when a pattern
485 factory is registered under a name already in use.
491 pattern_factory
: Callable
[[AnyStr
], Pattern
],
494 Initializes the :exc:`AlreadyRegisteredError` instance.
496 *name* (:class:`str`) is the name of the registered pattern.
498 *pattern_factory* (:class:`~collections.abc.Callable`) is the
499 registered pattern factory.
501 super(AlreadyRegisteredError
, self
).__init
__(name
, pattern_factory
)
504 def message(self
) -> str:
506 *message* (:class:`str`) is the error message.
508 return "{name!r} is already registered for pattern factory:{pattern_factory!r}.".format(
510 pattern_factory
=self
.pattern_factory
,
514 def name(self
) -> str:
516 *name* (:class:`str`) is the name of the registered pattern.
521 def pattern_factory(self
) -> Callable
[[AnyStr
], Pattern
]:
523 *pattern_factory* (:class:`~collections.abc.Callable`) is the
524 registered pattern factory.
529 class RecursionError(Exception):
531 The :exc:`RecursionError` exception is raised when recursion is
542 Initializes the :exc:`RecursionError` instance.
544 *real_path* (:class:`str`) is the real path that recursion was
547 *first_path* (:class:`str`) is the first path encountered for
550 *second_path* (:class:`str`) is the second path encountered for
553 super(RecursionError
, self
).__init
__(real_path
, first_path
, second_path
)
556 def first_path(self
) -> str:
558 *first_path* (:class:`str`) is the first path encountered for
559 :attr:`self.real_path <RecursionError.real_path>`.
564 def message(self
) -> str:
566 *message* (:class:`str`) is the error message.
568 return "Real path {real!r} was encountered at {first!r} and then {second!r}.".format(
570 first
=self
.first_path
,
571 second
=self
.second_path
,
575 def real_path(self
) -> str:
577 *real_path* (:class:`str`) is the real path that recursion was
583 def second_path(self
) -> str:
585 *second_path* (:class:`str`) is the second path encountered for
586 :attr:`self.real_path <RecursionError.real_path>`.
591 class MatchDetail(object):
593 The :class:`.MatchDetail` class contains information about
596 # Make the class dict-less.
597 __slots__
= ('patterns',)
599 def __init__(self
, patterns
: Sequence
[Pattern
]) -> None:
601 Initialize the :class:`.MatchDetail` instance.
603 *patterns* (:class:`~collections.abc.Sequence` of :class:`~pathspec.pattern.Pattern`)
604 contains the patterns that matched the file in the order they were
608 self
.patterns
= patterns
610 *patterns* (:class:`~collections.abc.Sequence` of :class:`~pathspec.pattern.Pattern`)
611 contains the patterns that matched the file in the order they were
616 class TreeEntry(object):
618 The :class:`.TreeEntry` class contains information about a file-system
622 # Make the class dict-less.
623 __slots__
= ('_lstat', 'name', 'path', '_stat')
629 lstat
: os
.stat_result
,
630 stat
: os
.stat_result
,
633 Initialize the :class:`.TreeEntry` instance.
635 *name* (:class:`str`) is the base name of the entry.
637 *path* (:class:`str`) is the relative path of the entry.
639 *lstat* (:class:`os.stat_result`) is the stat result of the direct
642 *stat* (:class:`os.stat_result`) is the stat result of the entry,
646 self
._lstat
: os
.stat_result
= lstat
648 *_lstat* (:class:`os.stat_result`) is the stat result of the direct
652 self
.name
: str = name
654 *name* (:class:`str`) is the base name of the entry.
657 self
.path
: str = path
659 *path* (:class:`str`) is the path of the entry.
662 self
._stat
: os
.stat_result
= stat
664 *_stat* (:class:`os.stat_result`) is the stat result of the linked
668 def is_dir(self
, follow_links
: Optional
[bool] = None) -> bool:
670 Get whether the entry is a directory.
672 *follow_links* (:class:`bool` or :data:`None`) is whether to follow
673 symbolic links. If this is :data:`True`, a symlink to a directory
674 will result in :data:`True`. Default is :data:`None` for :data:`True`.
676 Returns whether the entry is a directory (:class:`bool`).
678 if follow_links
is None:
681 node_stat
= self
._stat
if follow_links
else self
._lstat
682 return stat
.S_ISDIR(node_stat
.st_mode
)
684 def is_file(self
, follow_links
: Optional
[bool] = None) -> bool:
686 Get whether the entry is a regular file.
688 *follow_links* (:class:`bool` or :data:`None`) is whether to follow
689 symbolic links. If this is :data:`True`, a symlink to a regular file
690 will result in :data:`True`. Default is :data:`None` for :data:`True`.
692 Returns whether the entry is a regular file (:class:`bool`).
694 if follow_links
is None:
697 node_stat
= self
._stat
if follow_links
else self
._lstat
698 return stat
.S_ISREG(node_stat
.st_mode
)
700 def is_symlink(self
) -> bool:
702 Returns whether the entry is a symbolic link (:class:`bool`).
704 return stat
.S_ISLNK(self
._lstat
.st_mode
)
706 def stat(self
, follow_links
: Optional
[bool] = None) -> os
.stat_result
:
708 Get the cached stat result for the entry.
710 *follow_links* (:class:`bool` or :data:`None`) is whether to follow
711 symbolic links. If this is :data:`True`, the stat result of the
712 linked file will be returned. Default is :data:`None` for :data:`True`.
714 Returns that stat result (:class:`os.stat_result`).
716 if follow_links
is None:
719 return self
._stat
if follow_links
else self
._lstat