Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
util.py270 linesDownload Raw Back to babel
1"""2    babel.util3    ~~~~~~~~~~4 5    Various utility classes and functions.6 7    :copyright: (c) 2013-2024 by the Babel Team.8    :license: BSD, see LICENSE for more details.9"""10from __future__ import annotations11 12import codecs13import collections14import datetime15import os16import re17import textwrap18from collections.abc import Generator, Iterable19from typing import IO, Any, TypeVar20 21from babel import dates, localtime22 23missing = object()24 25_T = TypeVar("_T")26 27 28def distinct(iterable: Iterable[_T]) -> Generator[_T, None, None]:29    """Yield all items in an iterable collection that are distinct.30 31    Unlike when using sets for a similar effect, the original ordering of the32    items in the collection is preserved by this function.33 34    >>> print(list(distinct([1, 2, 1, 3, 4, 4])))35    [1, 2, 3, 4]36    >>> print(list(distinct('foobar')))37    ['f', 'o', 'b', 'a', 'r']38 39    :param iterable: the iterable collection providing the data40    """41    seen = set()42    for item in iter(iterable):43        if item not in seen:44            yield item45            seen.add(item)46 47 48# Regexp to match python magic encoding line49PYTHON_MAGIC_COMMENT_re = re.compile(50    br'[ \t\f]* \# .* coding[=:][ \t]*([-\w.]+)', re.VERBOSE)51 52 53def parse_encoding(fp: IO[bytes]) -> str | None:54    """Deduce the encoding of a source file from magic comment.55 56    It does this in the same way as the `Python interpreter`__57 58    .. __: https://docs.python.org/3.4/reference/lexical_analysis.html#encoding-declarations59 60    The ``fp`` argument should be a seekable file object.61 62    (From Jeff Dairiki)63    """64    pos = fp.tell()65    fp.seek(0)66    try:67        line1 = fp.readline()68        has_bom = line1.startswith(codecs.BOM_UTF8)69        if has_bom:70            line1 = line1[len(codecs.BOM_UTF8):]71 72        m = PYTHON_MAGIC_COMMENT_re.match(line1)73        if not m:74            try:75                import ast76                ast.parse(line1.decode('latin-1'))77            except (ImportError, SyntaxError, UnicodeEncodeError):78                # Either it's a real syntax error, in which case the source is79                # not valid python source, or line2 is a continuation of line1,80                # in which case we don't want to scan line2 for a magic81                # comment.82                pass83            else:84                line2 = fp.readline()85                m = PYTHON_MAGIC_COMMENT_re.match(line2)86 87        if has_bom:88            if m:89                magic_comment_encoding = m.group(1).decode('latin-1')90                if magic_comment_encoding != 'utf-8':91                    raise SyntaxError(f"encoding problem: {magic_comment_encoding} with BOM")92            return 'utf-8'93        elif m:94            return m.group(1).decode('latin-1')95        else:96            return None97    finally:98        fp.seek(pos)99 100 101PYTHON_FUTURE_IMPORT_re = re.compile(102    r'from\s+__future__\s+import\s+\(*(.+)\)*')103 104 105def parse_future_flags(fp: IO[bytes], encoding: str = 'latin-1') -> int:106    """Parse the compiler flags by :mod:`__future__` from the given Python107    code.108    """109    import __future__110    pos = fp.tell()111    fp.seek(0)112    flags = 0113    try:114        body = fp.read().decode(encoding)115 116        # Fix up the source to be (hopefully) parsable by regexpen.117        # This will likely do untoward things if the source code itself is broken.118 119        # (1) Fix `import (\n...` to be `import (...`.120        body = re.sub(r'import\s*\([\r\n]+', 'import (', body)121        # (2) Join line-ending commas with the next line.122        body = re.sub(r',\s*[\r\n]+', ', ', body)123        # (3) Remove backslash line continuations.124        body = re.sub(r'\\\s*[\r\n]+', ' ', body)125 126        for m in PYTHON_FUTURE_IMPORT_re.finditer(body):127            names = [x.strip().strip('()') for x in m.group(1).split(',')]128            for name in names:129                feature = getattr(__future__, name, None)130                if feature:131                    flags |= feature.compiler_flag132    finally:133        fp.seek(pos)134    return flags135 136 137def pathmatch(pattern: str, filename: str) -> bool:138    """Extended pathname pattern matching.139 140    This function is similar to what is provided by the ``fnmatch`` module in141    the Python standard library, but:142 143     * can match complete (relative or absolute) path names, and not just file144       names, and145     * also supports a convenience pattern ("**") to match files at any146       directory level.147 148    Examples:149 150    >>> pathmatch('**.py', 'bar.py')151    True152    >>> pathmatch('**.py', 'foo/bar/baz.py')153    True154    >>> pathmatch('**.py', 'templates/index.html')155    False156 157    >>> pathmatch('./foo/**.py', 'foo/bar/baz.py')158    True159    >>> pathmatch('./foo/**.py', 'bar/baz.py')160    False161 162    >>> pathmatch('^foo/**.py', 'foo/bar/baz.py')163    True164    >>> pathmatch('^foo/**.py', 'bar/baz.py')165    False166 167    >>> pathmatch('**/templates/*.html', 'templates/index.html')168    True169    >>> pathmatch('**/templates/*.html', 'templates/foo/bar.html')170    False171 172    :param pattern: the glob pattern173    :param filename: the path name of the file to match against174    """175    symbols = {176        '?': '[^/]',177        '?/': '[^/]/',178        '*': '[^/]+',179        '*/': '[^/]+/',180        '**/': '(?:.+/)*?',181        '**': '(?:.+/)*?[^/]+',182    }183 184    if pattern.startswith('^'):185        buf = ['^']186        pattern = pattern[1:]187    elif pattern.startswith('./'):188        buf = ['^']189        pattern = pattern[2:]190    else:191        buf = []192 193    for idx, part in enumerate(re.split('([?*]+/?)', pattern)):194        if idx % 2:195            buf.append(symbols[part])196        elif part:197            buf.append(re.escape(part))198    match = re.match(f"{''.join(buf)}$", filename.replace(os.sep, "/"))199    return match is not None200 201 202class TextWrapper(textwrap.TextWrapper):203    wordsep_re = re.compile(204        r'(\s+|'                                  # any whitespace205        r'(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))',   # em-dash206    )207 208 209def wraptext(text: str, width: int = 70, initial_indent: str = '', subsequent_indent: str = '') -> list[str]:210    """Simple wrapper around the ``textwrap.wrap`` function in the standard211    library. This version does not wrap lines on hyphens in words.212 213    :param text: the text to wrap214    :param width: the maximum line width215    :param initial_indent: string that will be prepended to the first line of216                           wrapped output217    :param subsequent_indent: string that will be prepended to all lines save218                              the first of wrapped output219    """220    wrapper = TextWrapper(width=width, initial_indent=initial_indent,221                          subsequent_indent=subsequent_indent,222                          break_long_words=False)223    return wrapper.wrap(text)224 225 226# TODO (Babel 3.x): Remove this re-export227odict = collections.OrderedDict228 229 230class FixedOffsetTimezone(datetime.tzinfo):231    """Fixed offset in minutes east from UTC."""232 233    def __init__(self, offset: float, name: str | None = None) -> None:234 235        self._offset = datetime.timedelta(minutes=offset)236        if name is None:237            name = 'Etc/GMT%+d' % offset238        self.zone = name239 240    def __str__(self) -> str:241        return self.zone242 243    def __repr__(self) -> str:244        return f'<FixedOffset "{self.zone}" {self._offset}>'245 246    def utcoffset(self, dt: datetime.datetime) -> datetime.timedelta:247        return self._offset248 249    def tzname(self, dt: datetime.datetime) -> str:250        return self.zone251 252    def dst(self, dt: datetime.datetime) -> datetime.timedelta:253        return ZERO254 255 256# Export the localtime functionality here because that's257# where it was in the past.258# TODO(3.0): remove these aliases259UTC = dates.UTC260LOCALTZ = dates.LOCALTZ261get_localzone = localtime.get_localzone262STDOFFSET = localtime.STDOFFSET263DSTOFFSET = localtime.DSTOFFSET264DSTDIFF = localtime.DSTDIFF265ZERO = localtime.ZERO266 267 268def _cmp(a: Any, b: Any):269    return (a > b) - (a < b)270 
codekingpro/portable-devtools · Team Ai