Initial commit: add .gitignore and README
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
"""
|
||||
This module provides common classes for the gitignore patterns.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from pathspec.pattern import (
|
||||
RegexPattern)
|
||||
from pathspec._typing import (
|
||||
AnyStr) # Removed in 3.18.
|
||||
|
||||
_BYTES_ENCODING = 'latin1'
|
||||
"""
|
||||
The encoding to use when parsing a byte string pattern.
|
||||
"""
|
||||
|
||||
|
||||
class _GitIgnoreBasePattern(RegexPattern):
|
||||
"""
|
||||
.. warning:: This class is not part of the public API. It is subject to
|
||||
change.
|
||||
|
||||
The :class:`_GitIgnoreBasePattern` class is the base implementation for a
|
||||
compiled gitignore pattern.
|
||||
"""
|
||||
|
||||
# Keep the dict-less class hierarchy.
|
||||
__slots__ = ()
|
||||
|
||||
@staticmethod
|
||||
def escape(s: AnyStr) -> AnyStr:
|
||||
"""
|
||||
Escape special characters in the given string.
|
||||
|
||||
*s* (:class:`str` or :class:`bytes`) a filename or a string that you want to
|
||||
escape, usually before adding it to a ".gitignore".
|
||||
|
||||
Returns the escaped string (:class:`str` or :class:`bytes`).
|
||||
"""
|
||||
if isinstance(s, str):
|
||||
return_type = str
|
||||
string = s
|
||||
elif isinstance(s, bytes):
|
||||
return_type = bytes
|
||||
string = s.decode(_BYTES_ENCODING)
|
||||
else:
|
||||
raise TypeError(f"s:{s!r} is not a unicode or byte string.")
|
||||
|
||||
# Reference: https://git-scm.com/docs/gitignore#_pattern_format
|
||||
out_string = ''.join((f"\\{x}" if x in '[]!*#?' else x) for x in string)
|
||||
|
||||
if return_type is bytes:
|
||||
return out_string.encode(_BYTES_ENCODING)
|
||||
else:
|
||||
return out_string
|
||||
|
||||
@staticmethod
|
||||
def _translate_segment_glob(pattern: str) -> str:
|
||||
"""
|
||||
Translates the glob pattern to a regular expression. This is used in the
|
||||
constructor to translate a path segment glob pattern to its corresponding
|
||||
regular expression.
|
||||
|
||||
*pattern* (:class:`str`) is the glob pattern.
|
||||
|
||||
Returns the regular expression (:class:`str`).
|
||||
"""
|
||||
# NOTE: This is derived from `fnmatch.translate()` and is similar to the
|
||||
# POSIX function `fnmatch()` with the `FNM_PATHNAME` flag set.
|
||||
|
||||
escape = False
|
||||
regex = ''
|
||||
i, end = 0, len(pattern)
|
||||
while i < end:
|
||||
# Get next character.
|
||||
char = pattern[i]
|
||||
i += 1
|
||||
|
||||
if escape:
|
||||
# Escape the character.
|
||||
escape = False
|
||||
regex += re.escape(char)
|
||||
|
||||
elif char == '\\':
|
||||
# Escape character, escape next character.
|
||||
escape = True
|
||||
|
||||
elif char == '*':
|
||||
# Multi-character wildcard. Match any string (except slashes), including
|
||||
# an empty string.
|
||||
regex += '[^/]*'
|
||||
|
||||
elif char == '?':
|
||||
# Single-character wildcard. Match any single character (except a
|
||||
# slash).
|
||||
regex += '[^/]'
|
||||
|
||||
elif char == '[':
|
||||
# Bracket expression wildcard. Except for the beginning exclamation
|
||||
# mark, the whole bracket expression can be used directly as regex, but
|
||||
# we have to find where the expression ends.
|
||||
# - "[][!]" matches ']', '[' and '!'.
|
||||
# - "[]-]" matches ']' and '-'.
|
||||
# - "[!]a-]" matches any character except ']', 'a' and '-'.
|
||||
j = i
|
||||
|
||||
# Pass bracket expression negation.
|
||||
if j < end and (pattern[j] == '!' or pattern[j] == '^'):
|
||||
j += 1
|
||||
|
||||
# Pass first closing bracket if it is at the beginning of the
|
||||
# expression.
|
||||
if j < end and pattern[j] == ']':
|
||||
j += 1
|
||||
|
||||
# Find closing bracket. Stop once we reach the end or find it.
|
||||
while j < end and pattern[j] != ']':
|
||||
j += 1
|
||||
|
||||
if j < end:
|
||||
# Found end of bracket expression. Increment j to be one past the
|
||||
# closing bracket:
|
||||
#
|
||||
# [...]
|
||||
# ^ ^
|
||||
# i j
|
||||
#
|
||||
j += 1
|
||||
expr = '['
|
||||
|
||||
if pattern[i] == '!':
|
||||
# Bracket expression needs to be negated.
|
||||
expr += '^'
|
||||
i += 1
|
||||
elif pattern[i] == '^':
|
||||
# POSIX declares that the regex bracket expression negation "[^...]"
|
||||
# is undefined in a glob pattern. Python's `fnmatch.translate()`
|
||||
# escapes the caret ('^') as a literal. Git supports the using a
|
||||
# caret for negation. Maintain consistency with Git because that is
|
||||
# the expected behavior.
|
||||
expr += '^'
|
||||
i += 1
|
||||
|
||||
# Build regex bracket expression. Escape slashes so they are treated
|
||||
# as literal slashes by regex as defined by POSIX.
|
||||
expr += pattern[i:j].replace('\\', '\\\\')
|
||||
|
||||
# Add regex bracket expression to regex result.
|
||||
regex += expr
|
||||
|
||||
# Set i to one past the closing bracket.
|
||||
i = j
|
||||
|
||||
else:
|
||||
# Failed to find closing bracket, treat opening bracket as a bracket
|
||||
# literal instead of as an expression.
|
||||
regex += '\\['
|
||||
|
||||
else:
|
||||
# Regular character, escape it for regex.
|
||||
regex += re.escape(char)
|
||||
|
||||
if escape:
|
||||
raise ValueError((
|
||||
f"Escape character found with no next character to escape: {pattern!r}"
|
||||
)) # ValueError
|
||||
|
||||
return regex
|
||||
|
||||
|
||||
class GitIgnorePatternError(ValueError):
|
||||
"""
|
||||
The :class:`GitIgnorePatternError` class indicates an invalid gitignore
|
||||
pattern.
|
||||
"""
|
||||
pass
|
||||
Reference in New Issue
Block a user