add proxy for the first part and try to develop the second part

This commit is contained in:
Pierre Geeraert
2025-12-03 12:08:56 +00:00
parent 6c34d510e5
commit 460f18d85a
1095 changed files with 358799 additions and 5 deletions
@@ -0,0 +1,117 @@
from __future__ import annotations
import re
from collections.abc import Iterable
from functools import cache
from typing import TypeVar
_T = TypeVar('_T')
_undefined = object() # unique object for when None is not a good default
def first(iterable, default=_undefined):
"""Return the first item of *iterable*, or *default* if *iterable* is
empty.
>>> first([0, 1, 2, 3])
0
>>> first([], 'some default')
'some default'
If *default* is not provided and there are no items in the iterable,
raise ``ValueError``.
:func:`first` is useful when you have a generator of expensive-to-retrieve
values and want any arbitrary one. It is marginally shorter than
``next(iter(iterable), default)``.
"""
# NOTE: https://more-itertools.readthedocs.io/en/stable/_modules/more_itertools/more.html#first
try:
return next(iter(iterable))
except StopIteration as e:
# I'm on the edge about raising ValueError instead of StopIteration. At
# the moment, ValueError wins, because the caller could conceivably
# want to do something different with flow control when I raise the
# exception, and it's weird to explicitly catch StopIteration.
if default is _undefined:
raise ValueError(
'first() was called on an empty iterable, and no '
'default value was provided.',
) from e
return default
def match_to_find(m: re.Match):
if m is None:
return None
g = m.groups(default=m.string[0:0])
if len(g) == 1:
return g[0]
else:
return g or m.group()
def findalliter(pattern, string, pos=None, endpos=None, flags=0):
"""
like finditer(), but with return values like findall()
implementation taken from cpython/Modules/_sre.c/findall()
"""
r = (
pattern
if isinstance(pattern, re.Pattern)
else re.compile(pattern, flags=flags)
)
if endpos is not None:
iterator = r.finditer(string, pos=pos, endpos=endpos)
elif pos is not None:
iterator = r.finditer(string, pos=pos)
else:
iterator = r.finditer(string)
for m in iterator:
yield match_to_find(m)
def findfirst(pattern, string, pos=None, endpos=None, flags=0, default=_undefined):
"""
Avoids using the inefficient findall(...)[0], or first(findall(...))
"""
return first(
findalliter(pattern, string, pos=pos, endpos=endpos, flags=flags),
default=default,
)
def topsort(nodes: Iterable[_T], order: Iterable[tuple[_T, _T]]) -> list[_T]:
# https://en.wikipedia.org/wiki/Topological_sorting
order = set(order)
result: list[_T] = [] # Empty list that will contain the sorted elements
def with_incoming() -> set[_T]:
return {m for (_, m) in order}
# Set of all nodes with no incoming edges
pending = list(set(nodes) - with_incoming())
while pending:
n = pending.pop()
result.append(n)
# nodes m with an edge from n to m
outgoing = {m for (x, m) in order if x == n}
order -= {(n, m) for m in outgoing}
pending.extend(outgoing - with_incoming())
if order:
raise ValueError('There are cycles in the topological order')
return list(reversed(result)) # a topologically sorted list
@cache
def cached_re_compile(regex: re.Pattern | str | bytes) -> re.Pattern | None:
if isinstance(regex, re.Pattern):
return regex
return re.compile(regex) if isinstance(regex, (str | bytes)) else None