import weakref
from typing import Callable, Generic, Iterable, Iterator, List, Optional, Sequence, TypeVar, overload

__all__ = [
    "chunk",
    "take",
    "drop",
    "drop_until",
    "split_by",
    "scanl",
    "scanr",
    "LazyList",
    "Range",
    "MapList",
]
T = TypeVar('T')
A = TypeVar('A')
B = TypeVar('B')
R = TypeVar('R')

def take(n: int, iterable: Iterable[T]) -> Iterator[T]:
    r"""Take the first :attr:`n` elements from an iterable.

    .. code:: python

        >>> list(take(5, range(1000000)))
        [0, 1, 2, 3, 4]

    :param n: The number of elements to take.
    :param iterable: The iterable.
    :return: An iterator returning the first :attr:`n` elements from the iterable.
    """
    if n < 0:
        raise ValueError("`n` should be non-negative")
    try:
        it = iter(iterable)
        for _ in range(n):
            yield next(it)
    except StopIteration:
        pass


import weakref
from typing import Callable, Generic, Iterable, Iterator, List, Optional, Sequence, TypeVar, overload

__all__ = [
    "chunk",
    "take",
    "drop",
    "drop_until",
    "split_by",
    "scanl",
    "scanr",
    "LazyList",
    "Range",
    "MapList",
]
T = TypeVar('T')
A = TypeVar('A')
B = TypeVar('B')
R = TypeVar('R')

def drop(n: int, iterable: Iterable[T]) -> Iterator[T]:
    r"""Drop the first :attr:`n` elements from an iterable, and return the rest as an iterator.

    .. code:: python

        >>> next(drop(5, range(1000000)))
        5

    :param n: The number of elements to drop.
    :param iterable: The iterable.
    :return: An iterator returning the remaining part of the iterable after the first :attr:`n` elements.
    """
    if n < 0:
        raise ValueError("`n` should be non-negative")
    try:
        it = iter(iterable)
        for _ in range(n):
            next(it)
        yield from it
    except StopIteration:
        pass


import weakref
from typing import Callable, Generic, Iterable, Iterator, List, Optional, Sequence, TypeVar, overload

__all__ = [
    "chunk",
    "take",
    "drop",
    "drop_until",
    "split_by",
    "scanl",
    "scanr",
    "LazyList",
    "Range",
    "MapList",
]
T = TypeVar('T')
A = TypeVar('A')
B = TypeVar('B')
R = TypeVar('R')

def drop_until(pred_fn: Callable[[T], bool], iterable: Iterable[T]) -> Iterator[T]:
    r"""Drop elements from the iterable until an element that satisfies the predicate is encountered. Similar to the
    built-in :py:func:`filter` function, but only applied to a prefix of the iterable.

    .. code:: python

        >>> list(drop_until(lambda x: x > 5, range(10)))
        [6, 7, 8, 9]

    :param pred_fn: The predicate that returned elements should satisfy.
    :param iterable: The iterable.
    :return: The iterator after dropping elements.
    """
    iterator = iter(iterable)
    for item in iterator:
        if not pred_fn(item):
            continue
        yield item
        break
    yield from iterator


import weakref
from typing import Callable, Generic, Iterable, Iterator, List, Optional, Sequence, TypeVar, overload

__all__ = [
    "chunk",
    "take",
    "drop",
    "drop_until",
    "split_by",
    "scanl",
    "scanr",
    "LazyList",
    "Range",
    "MapList",
]
T = TypeVar('T')
A = TypeVar('A')
B = TypeVar('B')
R = TypeVar('R')

def split_by(iterable: Iterable[A], empty_segments: bool = False, *, criterion=None, separator=None) \
        -> Iterator[List[A]]:
    r"""Split a list into sub-lists by dropping certain elements. Exactly one of ``criterion`` and ``separator`` must be
    specified. For example:

    .. code:: python

        >>> list(split_by(range(10), criterion=lambda x: x % 3 == 0))
        [[1, 2], [4, 5], [7, 8]]

        >>> list(split_by(" Split by: ", empty_segments=True, separator='.'))
        [[], ['S', 'p', 'l', 'i', 't'], ['b', 'y', ':'], []]

    :param iterable: The list to split.
    :param empty_segments: If ``True``, include an empty list in cases where two adjacent elements satisfy
        the criterion.
    :param criterion: The criterion to decide whether to drop an element.
    :param separator: The separator for sub-lists. An element is dropped if it is equal to ``parameter``.
    :return: List of sub-lists.
    """
    if not ((criterion is None) ^ (separator is None)):
        raise ValueError("Exactly one of `criterion` and `separator` should be specified")
    if criterion is None:
        criterion = lambda x: x == separator
    group = []
    for x in iterable:
        if not criterion(x):
            group.append(x)
        else:
            if len(group) > 0 or empty_segments:
                yield group
            group = []
    if len(group) > 0 or empty_segments:
        yield group


import weakref
from typing import Callable, Generic, Iterable, Iterator, List, Optional, Sequence, TypeVar, overload

__all__ = [
    "chunk",
    "take",
    "drop",
    "drop_until",
    "split_by",
    "scanl",
    "scanr",
    "LazyList",
    "Range",
    "MapList",
]
T = TypeVar('T')
A = TypeVar('A')
B = TypeVar('B')
R = TypeVar('R')

def scanl(func, iterable, *args):
    r"""Computes the intermediate results of :py:func:`~functools.reduce`. Equivalent to Haskell's ``scanl``. For
    example:

    .. code:: python

        >>> list(scanl(operator.add, [1, 2, 3, 4], 0))
        [0, 1, 3, 6, 10]
        >>> list(scanl(lambda s, x: x + s, ['a', 'b', 'c', 'd']))
        ['a', 'ba', 'cba', 'dcba']

    Learn more at `Learn You a Haskell: Higher Order Functions <http://learnyouahaskell.com/higher-order-functions>`_.

    :param func: The function to apply. This should be a binary function where the arguments are: the accumulator,
        and the current element.
    :param iterable: The list of elements to iteratively apply the function to.
    :param initial: The initial value for the accumulator. If not supplied, the first element in the list is used.
    :return: The intermediate results at each step.
    """
    iterable = iter(iterable)
    if len(args) == 1:
        acc = args[0]
    elif len(args) == 0:
        acc = next(iterable)
    else:
        raise ValueError("Too many arguments")
    yield acc
    for x in iterable:
        acc = func(acc, x)
        yield acc


import weakref
from typing import Callable, Generic, Iterable, Iterator, List, Optional, Sequence, TypeVar, overload

__all__ = [
    "chunk",
    "take",
    "drop",
    "drop_until",
    "split_by",
    "scanl",
    "scanr",
    "LazyList",
    "Range",
    "MapList",
]
T = TypeVar('T')
A = TypeVar('A')
B = TypeVar('B')
R = TypeVar('R')

class LazyList(Generic[T], Sequence[T]):

    def __init__(self, iterable: Iterable[T]):
        self.iter = iter(iterable)
        self.exhausted = False
        self.list: List[T] = []

    def __iter__(self):
        if self.exhausted:
            return iter(self.list)
        return self.LazyListIterator(self)


import weakref
from typing import Callable, Generic, Iterable, Iterator, List, Optional, Sequence, TypeVar, overload

__all__ = [
    "chunk",
    "take",
    "drop",
    "drop_until",
    "split_by",
    "scanl",
    "scanr",
    "LazyList",
    "Range",
    "MapList",
]
T = TypeVar('T')
A = TypeVar('A')
B = TypeVar('B')
R = TypeVar('R')

class LazyList(Generic[T], Sequence[T]):

    def __init__(self, iterable: Iterable[T]):
        self.iter = iter(iterable)
        self.exhausted = False
        self.list: List[T] = []

    def __getitem__(self, idx):
        if isinstance(idx, slice):
            self._fetch_until(idx.stop)
        else:
            self._fetch_until(idx)
        return self.list[idx]


import weakref
from typing import Callable, Generic, Iterable, Iterator, List, Optional, Sequence, TypeVar, overload

__all__ = [
    "chunk",
    "take",
    "drop",
    "drop_until",
    "split_by",
    "scanl",
    "scanr",
    "LazyList",
    "Range",
    "MapList",
]
T = TypeVar('T')
A = TypeVar('A')
B = TypeVar('B')
R = TypeVar('R')

class LazyList(Generic[T], Sequence[T]):

    def __init__(self, iterable: Iterable[T]):
        self.iter = iter(iterable)
        self.exhausted = False
        self.list: List[T] = []

    def __len__(self):
        if self.exhausted:
            return len(self.list)
        else:
            raise TypeError("__len__ is not available before the iterable is depleted")


import weakref
from typing import Callable, Generic, Iterable, Iterator, List, Optional, Sequence, TypeVar, overload

__all__ = [
    "chunk",
    "take",
    "drop",
    "drop_until",
    "split_by",
    "scanl",
    "scanr",
    "LazyList",
    "Range",
    "MapList",
]
T = TypeVar('T')
A = TypeVar('A')
B = TypeVar('B')
R = TypeVar('R')

class Range(Sequence[int]):

    def __init__(self, *args):
        if len(args) == 0 or len(args) > 3:
            raise ValueError("Range should be called the same way as the builtin `range`")
        if len(args) == 1:
            self.l = 0
            self.r = args[0]
            self.step = 1
        else:
            self.l = args[0]
            self.r = args[1]
            self.step = 1 if len(args) == 2 else args[2]
        self.val = self.l
        self.length = (self.r - self.l) // self.step

    def __next__(self) -> int:
        if self.val >= self.r:
            raise StopIteration
        result = self.val
        self.val += self.step
        return result


import weakref
from typing import Callable, Generic, Iterable, Iterator, List, Optional, Sequence, TypeVar, overload

__all__ = [
    "chunk",
    "take",
    "drop",
    "drop_until",
    "split_by",
    "scanl",
    "scanr",
    "LazyList",
    "Range",
    "MapList",
]
T = TypeVar('T')
A = TypeVar('A')
B = TypeVar('B')
R = TypeVar('R')

class Range(Sequence[int]):

    def __init__(self, *args):
        if len(args) == 0 or len(args) > 3:
            raise ValueError("Range should be called the same way as the builtin `range`")
        if len(args) == 1:
            self.l = 0
            self.r = args[0]
            self.step = 1
        else:
            self.l = args[0]
            self.r = args[1]
            self.step = 1 if len(args) == 2 else args[2]
        self.val = self.l
        self.length = (self.r - self.l) // self.step

    def __getitem__(self, item):
        if isinstance(item, slice):
            return [self._get_idx(idx) for idx in range(*item.indices(self.length))]
        if item < 0:
            item = self.length + item
        return self._get_idx(item)


import weakref
from typing import Callable, Generic, Iterable, Iterator, List, Optional, Sequence, TypeVar, overload

__all__ = [
    "chunk",
    "take",
    "drop",
    "drop_until",
    "split_by",
    "scanl",
    "scanr",
    "LazyList",
    "Range",
    "MapList",
]
T = TypeVar('T')
A = TypeVar('A')
B = TypeVar('B')
R = TypeVar('R')

class MapList(Generic[R], Sequence[R]):

    def __init__(self, func: Callable[[T], R], lst: Sequence[T]):
        self.func = func
        self.list = lst

    def __getitem__(self, item):
        if isinstance(item, int):
            return self.func(self.list[item])
        return [self.func(x) for x in self.list[item]]


import base64
import codecs
from binascii import Error
from collections import UserString
from typing import ByteString as _ByteString
from typing import (
    Optional,
    Tuple,
    Union,
)

_STR = Union[str, UserString]
NAME = __name__.split('.')[-1]

def encode(
        text: _STR,
        errors: _STR = 'strict'
) -> Tuple[bytes, int]:
    """Convert the given ``text`` of base64 characters into the base64
    decoded bytes.

    Args:
        text (str): The string input.  The given string input can span
            across many lines and be indented any number of spaces.
        errors (str): Not used.  This argument exists to meet the
            interface requirements.  Any value given to this argument
            is ignored.

    Returns:
        bytes: The given ``text`` converted into base64 bytes.
        int: The length of the returned bytes.
    """
    # Convert the given 'text', that are of type UserString into a str.
    text_input = str(text)

    # Cleanup whitespace.
    text_str = text_input.strip()
    text_str = '\n'.join(
        filter(
            lambda x: len(x) > 0,
            map(lambda x: x.strip(), text_str.strip().splitlines())
        )
    )

    # Convert the cleaned text into utf8 bytes
    text_bytes = text_str.encode('utf-8')
    try:
        out = base64.decodebytes(text_bytes)
    except Error as e:
        raise UnicodeEncodeError(
            'b64',
            text_input,
            0,
            len(text),
            (
                f'{text_str!r} is not a proper bas64 character string: '
                f'{e}'
            )
        )
    return out, len(text)


import base64
import codecs
from binascii import Error
from collections import UserString
from typing import ByteString as _ByteString
from typing import (
    Optional,
    Tuple,
    Union,
)

_STR = Union[str, UserString]
NAME = __name__.split('.')[-1]

def register() -> None:
    """Register the ``b64`` codec with Python."""
    try:
        codecs.getdecoder(NAME)
    except LookupError:
        codecs.register(_get_codec_info)


import codecs
from collections import UserString
from functools import reduce
from typing import ByteString as _ByteString
from typing import (
    Generator,
    Optional,
    Tuple,
    Union,
    cast,
)

_Str = Union[str, UserString]
NAME = __name__.split('.')[-1]

def decode(
        data: _ByteString,
        errors: _Str = 'strict'
) -> Tuple[str, int]:
    """Convert a bytes type of escaped utf8 hexadecimal to a string.

    Args:
        data (bytes or bytearray or memoryview): The escaped utf8
            hexadecimal bytes.
        errors (str or :obj:`~UserString`): The error checking level.

    Returns:
        str: The given ``data`` (of escaped utf8 hexadecimal bytes)
            converted into a :obj:`str`.
        int: The number of the given ``data`` bytes consumed.

    Raises:
         UnicodeDecodeError: if the given ``data`` contains escaped
            utf8 hexadecimal that references invalid utf8 bytes.

    """
    # Convert memoryview and bytearray objects to bytes.
    data_bytes = bytes(data)

    # Convert the given 'errors', that are of type UserString into a str.
    errors_input = str(errors)

    # Convert the utf8 bytes into a string of latin-1 characters.
    # This basically maps the exact utf8 bytes to the string. Also,
    # this converts any escaped hexadecimal sequences \\xHH into
    # \xHH bytes.
    text_str_latin1 = data_bytes.decode('unicode_escape')

    # Convert the string of latin-1 characters (which are actually
    # utf8 characters) into bytes.
    text_bytes_utf8 = text_str_latin1.encode('latin1')

    # Convert the utf8 bytes into a string.
    try:
        out = text_bytes_utf8.decode('utf-8', errors=errors_input)
    except UnicodeDecodeError as e:
        raise UnicodeDecodeError(
            'eutf8h',
            data_bytes,
            e.start,
            e.end,
            e.reason
        )
    return out, len(data)


import codecs
from collections import UserString
from functools import reduce
from typing import ByteString as _ByteString
from typing import (
    Generator,
    Optional,
    Tuple,
    Union,
    cast,
)

_Str = Union[str, UserString]
NAME = __name__.split('.')[-1]

def register() -> None:
    try:
        codecs.getdecoder(NAME)
    except LookupError:
        codecs.register(_get_codec_info)


import asyncio
from typing import Any

__all__ = ['cached_property']

class cached_property:

    def __init__(self, func):
        self.__doc__ = getattr(func, "__doc__")
        self.func = func

    def __get__(self, obj: Any, cls):
        if obj is None:
            return self

        if asyncio.iscoroutinefunction(self.func):
            return self._wrap_in_coroutine(obj)

        value = obj.__dict__[self.func.__name__] = self.func(obj)
        return value


from collections import (
    OrderedDict,
    namedtuple,
)
from collections.abc import (
    Mapping,
    Sequence,
)
from functools import singledispatch
from types import SimpleNamespace
from typing import (
    Any,
    List,
    NamedTuple,
    Tuple,
    Union,
    cast,
)
from flutils.validators import validate_identifier

__all__ = ['to_namedtuple']
_AllowedTypes = Union[
    List,
    Mapping,
    NamedTuple,
    SimpleNamespace,
    Tuple,
]

def to_namedtuple(obj: _AllowedTypes) -> Union[NamedTuple, Tuple, List]:
    """Convert particular objects into a namedtuple.

    Args:
        obj: The object to be converted (or have it's contents converted) to
            a :obj:`NamedTuple <collections.namedtuple>`.

    If the given type is of :obj:`list` or :obj:`tuple`, each item will be
    recursively converted to a :obj:`NamedTuple <collections.namedtuple>`
    provided the items can be converted. Items that cannot be converted
    will still exist in the returned object.

    If the given type is of :obj:`list` the return value will be a new
    :obj:`list`.  This means the items are not changed in the given
    ``obj``.

    If the given type is of :obj:`Mapping <collections.abc.Mapping>`
    (:obj:`dict`), keys that can be proper identifiers will become attributes
    on the returned :obj:`NamedTuple <collections.namedtuple>`.  Additionally,
    the attributes of the returned :obj:`NamedTuple <collections.namedtuple>`
    are sorted alphabetically.

    If the given type is of :obj:`OrderedDict <collections.OrderedDict>`,
    the attributes of the returned :obj:`NamedTuple <collections.namedtuple>`
    keep the same order as the given
    :obj:`OrderedDict <collections.OrderedDict>` keys.

    If the given type is of :obj:`SimpleNamespace <types.SimpleNamespace>`,
    The attributes are sorted alphabetically in the returned
    :obj:`NamedTuple <collections.NamedTuple>`.

    Any identifier (key or attribute name) that starts with an underscore
    cannot be used as a :obj:`NamedTuple <collections.namedtuple>` attribute.

    All values are recursively converted.  This means a dictionary that
    contains another dictionary, as one of it's values, will be converted
    to a :obj:`NamedTuple <collections.namedtuple>` with the attribute's
    value also converted to a :obj:`NamedTuple <collections.namedtuple>`.

    :rtype:
        :obj:`list`

            A list with any of it's values converted to a
            :obj:`NamedTuple <collections.namedtuple>`.

        :obj:`tuple`

            A tuple with any of it's values converted to a
            :obj:`NamedTuple <collections.namedtuple>`.

        :obj:`NamedTuple <collections.namedtuple>`.

    Example:
        >>> from flutils.namedtupleutils import to_namedtuple
        >>> dic = {'a': 1, 'b': 2}
        >>> to_namedtuple(dic)
        NamedTuple(a=1, b=2)
    """
    return _to_namedtuple(obj)


from collections import (
    UserList,
    deque,
)
from collections.abc import (
    Iterator,
    KeysView,
    ValuesView,
)
from typing import Any as _Any

__all__ = [
    'has_any_attrs',
    'has_any_callables',
    'has_attrs',
    'has_callables',
    'is_list_like',
    'is_subclass_of_any',
]
_LIST_LIKE = (
    list,
    set,
    frozenset,
    tuple,
    deque,
    Iterator,
    ValuesView,
    KeysView,
    UserList
)

def has_any_attrs(obj: _Any, *attrs: str) -> bool:
    """Check if the given ``obj`` has **ANY** of the given ``*attrs``.

    Args:
        obj (:obj:`Any <typing.Any>`): The object to check.
        *attrs (:obj:`str`): The names of the attributes to check.

    :rtype:
        :obj:`bool`

        * :obj:`True` if any of the given ``*attrs`` exist on the given
          ``obj``;
        * :obj:`False` otherwise.

    Example:
        >>> from flutils.objutils import has_any_attrs
        >>> has_any_attrs(dict(),'get','keys','items','values','something')
        True
    """
    for attr in attrs:
        if hasattr(obj, attr) is True:
            return True
    return False


from collections import (
    UserList,
    deque,
)
from collections.abc import (
    Iterator,
    KeysView,
    ValuesView,
)
from typing import Any as _Any

__all__ = [
    'has_any_attrs',
    'has_any_callables',
    'has_attrs',
    'has_callables',
    'is_list_like',
    'is_subclass_of_any',
]
_LIST_LIKE = (
    list,
    set,
    frozenset,
    tuple,
    deque,
    Iterator,
    ValuesView,
    KeysView,
    UserList
)

def has_any_callables(obj: _Any, *attrs: str) -> bool:
    """Check if the given ``obj`` has **ANY** of the given ``attrs`` and are
    callable.

    Args:
        obj (:obj:`Any <typing.Any>`): The object to check.
        *attrs (:obj:`str`): The names of the attributes to check.

    :rtype:
        :obj:`bool`

        * :obj:`True` if ANY of the given ``*attrs`` exist on the given ``obj``
          and ANY are callable;
        * :obj:`False` otherwise.

    Example:
        >>> from flutils.objutils import has_any_callables
        >>> has_any_callables(dict(),'get','keys','items','values','foo')
        True
    """
    if has_any_attrs(obj, *attrs) is True:
        for attr in attrs:
            if callable(getattr(obj, attr)) is True:
                return True
    return False


from collections import (
    UserList,
    deque,
)
from collections.abc import (
    Iterator,
    KeysView,
    ValuesView,
)
from typing import Any as _Any

__all__ = [
    'has_any_attrs',
    'has_any_callables',
    'has_attrs',
    'has_callables',
    'is_list_like',
    'is_subclass_of_any',
]
_LIST_LIKE = (
    list,
    set,
    frozenset,
    tuple,
    deque,
    Iterator,
    ValuesView,
    KeysView,
    UserList
)

def has_attrs(
        obj: _Any,
        *attrs: str
) -> bool:
    """Check if given ``obj`` has all the given ``*attrs``.

    Args:
        obj (:obj:`Any <typing.Any>`): The object to check.
        *attrs (:obj:`str`): The names of the attributes to check.

    :rtype:
        :obj:`bool`

        * :obj:`True` if all the given ``*attrs`` exist on the given ``obj``;
        * :obj:`False` otherwise.

    Example:
        >>> from flutils.objutils import has_attrs
        >>> has_attrs(dict(),'get','keys','items','values')
        True
    """
    for attr in attrs:
        if hasattr(obj, attr) is False:
            return False
    return True


from collections import (
    UserList,
    deque,
)
from collections.abc import (
    Iterator,
    KeysView,
    ValuesView,
)
from typing import Any as _Any

__all__ = [
    'has_any_attrs',
    'has_any_callables',
    'has_attrs',
    'has_callables',
    'is_list_like',
    'is_subclass_of_any',
]
_LIST_LIKE = (
    list,
    set,
    frozenset,
    tuple,
    deque,
    Iterator,
    ValuesView,
    KeysView,
    UserList
)

def has_callables(
        obj: _Any,
        *attrs: str
) -> bool:
    """Check if given ``obj`` has all the given ``attrs`` and are callable.

    Args:
        obj (:obj:`Any <typing.Any>`): The object to check.
        *attrs (:obj:`str`): The names of the attributes to check.

    :rtype:
        :obj:`bool`

        * :obj:`True` if all the given ``*attrs`` exist on the given ``obj``
          and all are callable;
        * :obj:`False` otherwise.

    Example:
        >>> from flutils.objutils import has_callables
        >>> has_callables(dict(),'get','keys','items','values')
        True
    """
    if has_attrs(obj, *attrs) is True:
        for attr in attrs:
            if callable(getattr(obj, attr)) is False:
                return False
        return True
    return False


from typing import (
    Any,
    Dict,
    Generator,
    List,
    NamedTuple,
    Optional,
    Tuple,
    Union,
    cast,
)
from distutils.version import StrictVersion

__all__ = ['bump_version']
_BUMP_VERSION_MAJOR: int = 0
_BUMP_VERSION_MINOR: int = 1
_BUMP_VERSION_PATCH: int = 2
_BUMP_VERSION_MINOR_ALPHA: int = 3
_BUMP_VERSION_MINOR_BETA: int = 4
_BUMP_VERSION_MINORS: Tuple[int, ...] = (
    _BUMP_VERSION_MINOR,
    _BUMP_VERSION_MINOR_ALPHA,
    _BUMP_VERSION_MINOR_BETA,
)
_BUMP_VERSION_PATCH_ALPHA: int = 5
_BUMP_VERSION_PATCH_BETA: int = 6
_BUMP_VERSION_PATCHES: Tuple[int, ...] = (
    _BUMP_VERSION_PATCH,
    _BUMP_VERSION_PATCH_ALPHA,
    _BUMP_VERSION_PATCH_BETA,
)
_BUMP_VERSION_POSITION_NAMES: Dict[int, str] = {
    _BUMP_VERSION_MAJOR: 'major',
    _BUMP_VERSION_MINOR: 'minor',
    _BUMP_VERSION_PATCH: 'patch',
}

def bump_version(
        version: str,
        position: int = 2,
        pre_release: Optional[str] = None
) -> str:
    """Increase the version number from a version number string.

    *New in version 0.3*

    Args:
        version (str): The version number to be bumped.
        position (int, optional): The position (starting with zero) of the
            version number component to be increased.  Defaults to: ``2``
        pre_release (str, Optional): A value of ``a`` or ``alpha`` will
            create or increase an alpha version number.  A value of ``b`` or
            ``beta`` will create or increase a beta version number.

    Raises:
        ValueError: if the given ``version`` is an invalid version number.
        ValueError: if the given ``position`` does not exist.
        ValueError: if the given ``prerelease`` is not in:
            ``a, alpha, b, beta``
        ValueError: if trying to 'major' part, of a version number, to
            a pre-release version.

    :rtype:
        :obj:`str`

        * The increased version number.

    Examples:
        >>> from flutils.packages import bump_version
        >>> bump_version('1.2.2')
        '1.2.3'
        >>> bump_version('1.2.3', position=1)
        '1.3'
        >>> bump_version('1.3.4', position=0)
        '2.0'
        >>> bump_version('1.2.3', prerelease='a')
        '1.2.4a0'
        >>> bump_version('1.2.4a0', pre_release='a')
        '1.2.4a1'
        >>> bump_version('1.2.4a1', pre_release='b')
        '1.2.4b0'
        >>> bump_version('1.2.4a1')
        '1.2.4'
        >>> bump_version('1.2.4b0')
        '1.2.4'
        >>> bump_version('2.1.3', position=1, pre_release='a')
        '2.2a0'
        >>> bump_version('1.2b0', position=2)
        '1.2.1'

    """
    ver_info = _build_version_info(version)
    position = _build_version_bump_position(position)
    bump_type = _build_version_bump_type(position, pre_release)
    # noinspection PyUnusedLocal
    hold: List[Union[int, str]] = []
    if bump_type == _BUMP_VERSION_MAJOR:
        hold = [ver_info.major.num + 1, 0]
    elif bump_type in _BUMP_VERSION_MINORS:
        if bump_type == _BUMP_VERSION_MINOR:
            if ver_info.minor.pre_txt:
                hold = [ver_info.major.num, ver_info.minor.num]
            else:
                hold = [ver_info.major.num, ver_info.minor.num + 1]
        else:
            if bump_type == _BUMP_VERSION_MINOR_ALPHA:
                if ver_info.minor.pre_txt == 'a':
                    part = '%sa%s' % (
                        ver_info.minor.num,
                        ver_info.minor.pre_num + 1
                    )
                else:
                    part = '{}a0'.format(ver_info.minor.num + 1)
            else:
                if ver_info.minor.pre_txt == 'a':
                    part = '{}b0'.format(ver_info.minor.num)
                elif ver_info.minor.pre_txt == 'b':
                    part = '%sb%s' % (
                        ver_info.minor.num,
                        ver_info.minor.pre_num + 1
                    )
                else:
                    part = '{}b0'.format(ver_info.minor.num + 1)
            hold = [ver_info.major.num, part]
    else:
        if bump_type == _BUMP_VERSION_PATCH:
            if ver_info.patch.pre_txt:
                hold = [
                    ver_info.major.num,
                    ver_info.minor.num,
                    ver_info.patch.num
                ]
            else:
                hold = [
                    ver_info.major.num,
                    ver_info.minor.num,
                    ver_info.patch.num + 1
                ]
        else:
            if bump_type == _BUMP_VERSION_PATCH_ALPHA:
                if ver_info.patch.pre_txt == 'a':
                    part = '%sa%s' % (
                        ver_info.patch.num,
                        ver_info.patch.pre_num + 1
                    )
                else:
                    part = '{}a0'.format(ver_info.patch.num + 1)
            else:
                if ver_info.patch.pre_txt == 'a':
                    part = '{}b0'.format(ver_info.patch.num)

                elif ver_info.patch.pre_txt == 'b':
                    part = '%sb%s' % (
                        ver_info.patch.num,
                        ver_info.patch.pre_num + 1
                    )
                else:
                    part = '{}b0'.format(ver_info.patch.num + 1)
            hold = [ver_info.major.num, ver_info.minor.num, part]
    out = '.'.join(map(str, hold))
    return out


import functools
import getpass
import grp
import os
import pwd
import sys
from collections import deque
from os import PathLike
from pathlib import (
    Path,
    PosixPath,
    WindowsPath,
)
from typing import (
    Deque,
    Generator,
    Optional,
    Union,
    cast,
)

__all__ = [
    'chmod',
    'chown',
    'directory_present',
    'exists_as',
    'find_paths',
    'get_os_group',
    'get_os_user',
    'normalize_path',
    'path_absent',
]
_PATH = Union[
    PathLike,
    PosixPath,
    WindowsPath,
    bytes,
    str,
]
_STR_OR_INT_OR_NONE = Union[
    str,
    int,
    None
]

def chmod(
        path: _PATH,
        mode_file: Optional[int] = None,
        mode_dir: Optional[int] = None,
        include_parent: bool = False
) -> None:
    """Change the mode of a path.

    This function processes the given ``path`` with
    :obj:`~flutils.normalize_path`.

    If the given ``path`` does NOT exist, nothing will be done.

    This function will **NOT** change the mode of:

    - symlinks (symlink targets that are files or directories will be changed)
    - sockets
    - fifo
    - block devices
    - char devices

    Args:
        path (:obj:`str`, :obj:`bytes` or :obj:`Path <pathlib.Path>`):
            The path of the file or directory to have it's mode changed.  This
            value can be a :term:`glob pattern`.
        mode_file (:obj:`int`, optional): The mode applied to the given
            ``path`` that is a file or a symlink target that is a file.
            Defaults to ``0o600``.
        mode_dir (:obj:`int`, optional): The mode applied to the given
            ``path`` that is a directory or a symlink target that is a
            directory. Defaults to ``0o700``.
        include_parent (:obj:`bool`, optional): A value of :obj:`True`` will
            chmod the parent directory of the given ``path`` that contains a
            a :term:`glob pattern`.  Defaults to :obj:`False`.

    :rtype: :obj:`None`

    Examples:
        >>> from flutils.pathutils import chmod
        >>> chmod('~/tmp/flutils.tests.osutils.txt', 0o660)

        Supports a :term:`glob pattern`.  So to recursively change the mode
        of a directory just do:

        >>> chmod('~/tmp/**', mode_file=0o644, mode_dir=0o770)

        To change the mode of a directory's immediate contents:

        >>> chmod('~/tmp/*')

    """

    path = normalize_path(path)

    if mode_file is None:
        mode_file = 0o600

    if mode_dir is None:
        mode_dir = 0o700

    if '*' in path.as_posix():
        try:
            for sub_path in Path().glob(path.as_posix()):
                if sub_path.is_dir() is True:
                    sub_path.chmod(mode_dir)
                elif sub_path.is_file():
                    sub_path.chmod(mode_file)

        # Path().glob() returns an iterator that will
        # raise NotImplementedError if there
        # are no results from the glob pattern.
        except NotImplementedError:
            pass

        else:
            if include_parent is True:
                parent = path.parent
                if parent.is_dir():
                    parent.chmod(mode_dir)
    else:
        if path.exists() is True:
            if path.is_dir():
                path.chmod(mode_dir)
            elif path.is_file():
                path.chmod(mode_file)


import functools
import getpass
import grp
import os
import pwd
import sys
from collections import deque
from os import PathLike
from pathlib import (
    Path,
    PosixPath,
    WindowsPath,
)
from typing import (
    Deque,
    Generator,
    Optional,
    Union,
    cast,
)

__all__ = [
    'chmod',
    'chown',
    'directory_present',
    'exists_as',
    'find_paths',
    'get_os_group',
    'get_os_user',
    'normalize_path',
    'path_absent',
]
_PATH = Union[
    PathLike,
    PosixPath,
    WindowsPath,
    bytes,
    str,
]
_STR_OR_INT_OR_NONE = Union[
    str,
    int,
    None
]

def chown(
        path: _PATH,
        user: Optional[str] = None,
        group: Optional[str] = None,
        include_parent: bool = False
) -> None:
    """Change ownership of a path.

    This function processes the given ``path`` with
    :obj:`~flutils.normalize_path`.

    If the given ``path`` does NOT exist, nothing will be done.

    Args:
        path (:obj:`str`, :obj:`bytes` or :obj:`Path <pathlib.Path>`):
            The path of the file or directory that will have it's ownership
            changed.  This value can be a :term:`glob pattern`.
        user (:obj:`str` or :obj:`int`, optional): The "login name" used to set
            the owner of ``path``.  A value of ``'-1'`` will leave the
            owner unchanged.  Defaults to the "login name" of the current user.
        group (:obj:`str` or :obj:`int`, optional): The group name used to set
            the group of ``path``.  A value of ``'-1'`` will leave the
            group unchanged.  Defaults to the current user's group.
        include_parent (:obj:`bool`, optional): A value of :obj:`True` will
            chown the parent directory of the given ``path`` that contains
            a :term:`glob pattern`.  Defaults to :obj:`False`.

    Raises:
        OSError: If the given :obj:`user` does not exist as a "login
            name" for this operating system.
        OSError: If the given :obj:`group` does not exist as a "group
            name" for this operating system.

    :rtype: :obj:`None`

    Examples:
        >>> from flutils.pathutils import chown
        >>> chown('~/tmp/flutils.tests.osutils.txt')

        Supports a :term:`glob pattern`.  So to recursively change the
        ownership of a directory just do:

        >>> chown('~/tmp/**')

        To change ownership of all the directory's immediate contents:

        >>> chown('~/tmp/*', user='foo', group='bar')

    """
    path = normalize_path(path)
    if isinstance(user, str) and user == '-1':
        uid = -1
    else:
        uid = get_os_user(user).pw_uid

    if isinstance(user, str) and group == '-1':
        gid = -1
    else:
        gid = get_os_group(group).gr_gid

    if '*' in path.as_posix():
        try:
            for sub_path in Path().glob(path.as_posix()):
                if sub_path.is_dir() or sub_path.is_file():
                    os.chown(sub_path.as_posix(), uid, gid)
        except NotImplementedError:
            # Path().glob() returns an iterator that will
            # raise NotImplementedError if there
            # are no results from the glob pattern.
            pass
        else:
            if include_parent is True:
                path = path.parent
                if path.is_dir() is True:
                    os.chown(path.as_posix(), uid, gid)
    else:
        if path.exists() is True:
            os.chown(path.as_posix(), uid, gid)


import functools
import getpass
import grp
import os
import pwd
import sys
from collections import deque
from os import PathLike
from pathlib import (
    Path,
    PosixPath,
    WindowsPath,
)
from typing import (
    Deque,
    Generator,
    Optional,
    Union,
    cast,
)

__all__ = [
    'chmod',
    'chown',
    'directory_present',
    'exists_as',
    'find_paths',
    'get_os_group',
    'get_os_user',
    'normalize_path',
    'path_absent',
]
_PATH = Union[
    PathLike,
    PosixPath,
    WindowsPath,
    bytes,
    str,
]
_STR_OR_INT_OR_NONE = Union[
    str,
    int,
    None
]

def directory_present(
        path: _PATH,
        mode: Optional[int] = None,
        user: Optional[str] = None,
        group: Optional[str] = None,
) -> Path:
    """Ensure the state of the given :obj:`path` is present and a directory.

    This function processes the given ``path`` with
    :obj:`~flutils.normalize_path`.

    If the given ``path`` does **NOT** exist, it will be created as a
    directory.

    If the parent paths of the given ``path`` do not exist, they will also be
    created with the ``mode``, ``user`` and ``group``.

    If the given ``path`` does exist as a directory, the ``mode``, ``user``,
    and :``group`` will be applied.

    Args:
        path (:obj:`str`, :obj:`bytes` or :obj:`Path <pathlib.Path>`):
            The path of the directory.
        mode (:obj:`int`, optional): The mode applied to the ``path``.
            Defaults to ``0o700``.
        user (:obj:`str` or :obj:`int`, optional): The "login name" used to
            set the owner of the given ``path``.  A value of ``'-1'`` will
            leave the owner unchanged.  Defaults to the "login name" of the
            current user.
        group (:obj:`str` or :obj:`int`, optional): The group name used to set
            the group of the given ``path``.  A value of ``'-1'`` will leave
            the group unchanged.  Defaults to the current user's group.

    Raises:
        ValueError: if the given ``path`` contains a glob pattern.
        ValueError: if the given ``path`` is not an absolute path.
        FileExistsError: if the given ``path`` exists and is not a directory.
        FileExistsError: if a parent of the given ``path`` exists and is
            not a directory.

    :rtype: :obj:`Path <pathlib.Path>`

        * :obj:`PosixPath <pathlib.PosixPath>` or
          :obj:`WindowsPath <pathlib.WindowsPath>` depending on the system.

        .. Note:: :obj:`Path <pathlib.Path>` objects are immutable. Therefore,
           any given ``path`` of type :obj:`Path <pathlib.Path>` will not be
           the same object returned.

    Example:
        >>> from flutils.pathutils import directory_present
        >>> directory_present('~/tmp/test_path')
        PosixPath('/Users/len/tmp/test_path')

    """
    path = normalize_path(path)

    if '*' in path.as_posix():
        raise ValueError(
            'The path: %r must NOT contain any glob patterns.'
            % path.as_posix()
        )
    if path.is_absolute() is False:
        raise ValueError(
            'The path: %r must be an absolute path.  A path is considered '
            'absolute if it has both a root and (if the flavour allows) a '
            'drive.'
            % path.as_posix()
        )

    # Create a queue of paths to be created as directories.
    paths: Deque = deque()

    path_exists_as = exists_as(path)
    if path_exists_as == '':
        paths.append(path)
    elif path_exists_as != 'directory':
        raise FileExistsError(
            'The path: %r can NOT be created as a directory because it '
            'already exists as a %s.' % (path.as_posix(), path_exists_as)
        )

    parent = path.parent
    child = path

    # Traverse the path backwards and add any directories that
    # do no exist to the path queue.
    while child.as_posix() != parent.as_posix():
        parent_exists_as = exists_as(parent)
        if parent_exists_as == '':
            paths.appendleft(parent)
            child = parent
            parent = parent.parent
        elif parent_exists_as == 'directory':
            break
        else:
            raise FileExistsError(
                'Unable to create the directory: %r because the'
                'parent path: %r exists as a %s.'
                % (path.as_posix, parent.as_posix(), parent_exists_as)
            )

    if mode is None:
        mode = 0o700

    if paths:
        for build_path in paths:
            build_path.mkdir(mode=mode)
            chown(build_path, user=user, group=group)
    else:
        # The given path already existed only need to do a chown.
        chmod(path, mode_dir=mode)
        chown(path, user=user, group=group)

    return path


import functools
import getpass
import grp
import os
import pwd
import sys
from collections import deque
from os import PathLike
from pathlib import (
    Path,
    PosixPath,
    WindowsPath,
)
from typing import (
    Deque,
    Generator,
    Optional,
    Union,
    cast,
)

__all__ = [
    'chmod',
    'chown',
    'directory_present',
    'exists_as',
    'find_paths',
    'get_os_group',
    'get_os_user',
    'normalize_path',
    'path_absent',
]
_PATH = Union[
    PathLike,
    PosixPath,
    WindowsPath,
    bytes,
    str,
]
_STR_OR_INT_OR_NONE = Union[
    str,
    int,
    None
]

def exists_as(path: _PATH) -> str:
    """Return a string describing the file type if it exists.

    This function processes the given ``path`` with
    :obj:`~flutils.normalize_path`.

    Args:
        path (:obj:`str`, :obj:`bytes` or :obj:`Path <pathlib.Path>`):
            The path to check for existence.

    :rtype:
        :obj:`str`

        * ``''`` (empty string): if the given ``path`` does NOT exist; or,
          is a broken symbolic link; or, other errors (such as permission
          errors) are propagated.
        * ``'directory'``: if the given ``path`` points to a directory or
          is a symbolic link pointing to a directory.
        * ``'file'``: if the given ``path`` points to a regular file or is a
          symbolic link pointing to a regular file.
        * ``'block device'``: if the given ``path`` points to a block device or
          is a symbolic link pointing to a block device.
        * ``'char device'``: if the given ``path`` points to a character device
          or is a symbolic link pointing to a character device.
        * ``'FIFO'``: if the given ``path`` points to a FIFO or is a symbolic
          link pointing to a FIFO.
        * ``'socket'``: if the given ``path`` points to a Unix socket or is a
          symbolic link pointing to a Unix socket.

    Example:
        >>> from flutils.pathutils import exists_as
        >>> exists_as('~/tmp')
        'directory'
    """
    path = normalize_path(path)

    if path.is_dir():
        return 'directory'
    if path.is_file():
        return 'file'
    if path.is_block_device():
        return 'block device'
    if path.is_char_device():
        return 'char device'
    if path.is_fifo():
        return 'FIFO'
    if path.is_socket():
        return 'socket'
    return ''


import functools
import getpass
import grp
import os
import pwd
import sys
from collections import deque
from os import PathLike
from pathlib import (
    Path,
    PosixPath,
    WindowsPath,
)
from typing import (
    Deque,
    Generator,
    Optional,
    Union,
    cast,
)

__all__ = [
    'chmod',
    'chown',
    'directory_present',
    'exists_as',
    'find_paths',
    'get_os_group',
    'get_os_user',
    'normalize_path',
    'path_absent',
]
_PATH = Union[
    PathLike,
    PosixPath,
    WindowsPath,
    bytes,
    str,
]
_STR_OR_INT_OR_NONE = Union[
    str,
    int,
    None
]

def find_paths(
        pattern: _PATH
) -> Generator[Path, None, None]:
    """Find all paths that match the given :term:`glob pattern`.

    This function pre-processes the given ``pattern`` with
    :obj:`~flutils.normalize_path`.

    Args:
        pattern (:obj:`str`, :obj:`bytes` or :obj:`Path <pathlib.Path>`):
            The path to find; which may contain a :term:`glob pattern`.

    :rtype:
        :obj:`Generator <typing.Generator>`

    Yields:
        :obj:`pathlib.PosixPath` or :obj:`pathlib.WindowsPath`

    Example:
        >>> from flutils.pathutils import find_paths
        >>> list(find_paths('~/tmp/*'))
        [PosixPath('/home/test_user/tmp/file_one'),
        PosixPath('/home/test_user/tmp/dir_one')]

    """
    pattern = normalize_path(pattern)
    search = pattern.as_posix()[len(pattern.anchor):]
    yield from Path(pattern.anchor).glob(search)


import functools
import getpass
import grp
import os
import pwd
import sys
from collections import deque
from os import PathLike
from pathlib import (
    Path,
    PosixPath,
    WindowsPath,
)
from typing import (
    Deque,
    Generator,
    Optional,
    Union,
    cast,
)

__all__ = [
    'chmod',
    'chown',
    'directory_present',
    'exists_as',
    'find_paths',
    'get_os_group',
    'get_os_user',
    'normalize_path',
    'path_absent',
]
_PATH = Union[
    PathLike,
    PosixPath,
    WindowsPath,
    bytes,
    str,
]
_STR_OR_INT_OR_NONE = Union[
    str,
    int,
    None
]

def get_os_group(name: _STR_OR_INT_OR_NONE = None) -> grp.struct_group:
    """Get an operating system group object.

    Args:
        name (:obj:`str` or :obj:`int`, optional): The "group name" or ``gid``.
            Defaults to the current users's group.

    Raises:
        OSError: If the given ``name`` does not exist as a "group
            name" for this operating system.
        OSError: If the given ``name`` is a ``gid`` and it does not
            exist.

    :rtype:
        :obj:`struct_group <grp>`

        * A tuple like object.

    Example:
        >>> from flutils.pathutils import get_os_group
        >>> get_os_group('bar')
        grp.struct_group(gr_name='bar', gr_passwd='*', gr_gid=2001,
        gr_mem=['foo'])
    """
    if name is None:
        name = get_os_user().pw_gid
        name = cast(int, name)
    if isinstance(name, int):
        try:
            return grp.getgrgid(name)
        except KeyError:
            raise OSError(
                'The given gid: %r, is not a valid gid for this operating '
                'system.' % name
            )
    try:
        return grp.getgrnam(name)
    except KeyError:
        raise OSError(
            'The given name: %r, is not a valid "group name" '
            'for this operating system.' % name
        )


import functools
import getpass
import grp
import os
import pwd
import sys
from collections import deque
from os import PathLike
from pathlib import (
    Path,
    PosixPath,
    WindowsPath,
)
from typing import (
    Deque,
    Generator,
    Optional,
    Union,
    cast,
)

__all__ = [
    'chmod',
    'chown',
    'directory_present',
    'exists_as',
    'find_paths',
    'get_os_group',
    'get_os_user',
    'normalize_path',
    'path_absent',
]
_PATH = Union[
    PathLike,
    PosixPath,
    WindowsPath,
    bytes,
    str,
]
_STR_OR_INT_OR_NONE = Union[
    str,
    int,
    None
]

def get_os_user(name: _STR_OR_INT_OR_NONE = None) -> pwd.struct_passwd:
    """Return an user object representing an operating system user.

    Args:
        name (:obj:`str` or :obj:`int`, optional): The "login name" or
            ``uid``.  Defaults to the current user's "login name".
    Raises:
        OSError: If the given ``name`` does not exist as a "login
            name" for this operating system.
        OSError: If the given ``name`` is an ``uid`` and it does not
            exist.

    :rtype:
        :obj:`struct_passwd <pwd>`

        * A tuple like object.

    Example:
        >>> from flutils.pathutils import get_os_user
        >>> get_os_user('foo')
        pwd.struct_passwd(pw_name='foo', pw_passwd='********', pw_uid=1001,
        pw_gid=2001, pw_gecos='Foo Bar', pw_dir='/home/foo',
        pw_shell='/usr/local/bin/bash')
    """
    if isinstance(name, int):
        try:
            return pwd.getpwuid(name)
        except KeyError:
            raise OSError(
                'The given uid: %r, is not a valid uid for this operating '
                'system.' % name
            )
    if name is None:
        name = getpass.getuser()
    try:
        return pwd.getpwnam(name)
    except KeyError:
        raise OSError(
            'The given name: %r, is not a valid "login name" '
            'for this operating system.' % name
        )


import functools
import getpass
import grp
import os
import pwd
import sys
from collections import deque
from os import PathLike
from pathlib import (
    Path,
    PosixPath,
    WindowsPath,
)
from typing import (
    Deque,
    Generator,
    Optional,
    Union,
    cast,
)

__all__ = [
    'chmod',
    'chown',
    'directory_present',
    'exists_as',
    'find_paths',
    'get_os_group',
    'get_os_user',
    'normalize_path',
    'path_absent',
]
_PATH = Union[
    PathLike,
    PosixPath,
    WindowsPath,
    bytes,
    str,
]
_STR_OR_INT_OR_NONE = Union[
    str,
    int,
    None
]

def path_absent(
        path: _PATH,
) -> None:
    """Ensure the given ``path`` does **NOT** exist.

    *New in version 0.4.*

    If the given ``path`` does exist, it will be deleted.

    If the given ``path`` is a directory, this function will
    recursively delete all of the directory's contents.

    This function processes the given ``path`` with
    :obj:`~flutils.normalize_path`.

    Args:
        path (:obj:`str`, :obj:`bytes` or :obj:`Path <pathlib.Path>`):
            The path to remove.

    :rtype: :obj:`None`

    Example:
        >>> from flutils.pathutils import path_absent
        >>> path_absent('~/tmp/test_path')

    """
    path = normalize_path(path)
    path = path.as_posix()
    path = cast(str, path)
    if os.path.exists(path):
        if os.path.islink(path):
            os.unlink(path)
        elif os.path.isdir(path):
            for root, dirs, files in os.walk(path, topdown=False):
                for name in files:
                    p = os.path.join(root, name)
                    if os.path.isfile(p) or os.path.islink(p):
                        os.unlink(p)
                for name in dirs:
                    p = os.path.join(root, name)
                    if os.path.islink(p):
                        os.unlink(p)
                    else:
                        os.rmdir(p)
            if os.path.isdir(path):
                os.rmdir(path)
        else:
            os.unlink(path)


import os
from configparser import (
    ConfigParser,
    NoOptionError,
    NoSectionError,
)
from traceback import (
    FrameSummary,
    extract_stack,
)
from typing import (
    Dict,
    Generator,
    List,
    NamedTuple,
    Optional,
    Tuple,
    Union,
    cast,
)
from flutils.strutils import underscore_to_camel

def each_sub_command_config(
        setup_dir: Optional[Union[os.PathLike, str]] = None
) -> Generator[SetupCfgCommandConfig, None, None]:
    format_kwargs: Dict[str, str] = {
        'setup_dir': _prep_setup_dir(setup_dir),
        'home': os.path.expanduser('~')
    }
    setup_cfg_path = os.path.join(format_kwargs['setup_dir'], 'setup.cfg')
    parser = ConfigParser()
    parser.read(setup_cfg_path)
    format_kwargs['name'] = _get_name(parser, setup_cfg_path)
    path = os.path.join(format_kwargs['setup_dir'], 'setup_commands.cfg')
    if os.path.isfile(path):
        parser = ConfigParser()
        parser.read(path)
    yield from _each_setup_cfg_command(parser, format_kwargs)


import re
from itertools import chain
from sys import hexversion
from textwrap import TextWrapper
from typing import (
    List,
    Optional,
    Sequence,
    cast,
)

__all__ = ['len_without_ansi', 'AnsiTextWrapper']
_ANSI_RE = re.compile('(\x1b\\[[0-9;:]+[ABCDEFGHJKSTfhilmns])')

def len_without_ansi(seq: Sequence) -> int:
    """Return the character length of the given
    :obj:`Sequence <typing.Sequence>` without counting any ANSI codes.

    *New in version 0.6*

    Args:
         seq (:obj:`Sequence <typing.Sequence>`): A string or a list/tuple
             of strings.

    :rtype:
        :obj:`int`

    Example:
        >>> from flutils.txtutils import len_without_ansi
        >>> text = '\\x1b[38;5;209mfoobar\\x1b[0m'
        >>> len_without_ansi(text)
        6
    """
    if hasattr(seq, 'capitalize'):
        _text: str = cast(str, seq)
        seq = [c for c in _ANSI_RE.split(_text) if c]
    seq = [c for c in chain(*map(_ANSI_RE.split, seq)) if c]
    seq = cast(Sequence[str], seq)
    out = 0
    for text in seq:
        if hasattr(text, 'capitalize'):
            if text.startswith('\x1b[') and text.endswith('m'):
                continue
            else:
                out += len(text)
    return out


import argparse
import errno
import os
import re
import sys
from argparse import RawDescriptionHelpFormatter
from textwrap import dedent
from urllib.parse import urlsplit
from requests.utils import get_netrc_auth
from httpie.cli.argtypes import (
    AuthCredentials, KeyValueArgType, PARSED_DEFAULT_FORMAT_OPTIONS,
    parse_auth,
    parse_format_options,
)
from httpie.cli.constants import (
    HTTP_GET, HTTP_POST, OUTPUT_OPTIONS, OUTPUT_OPTIONS_DEFAULT,
    OUTPUT_OPTIONS_DEFAULT_OFFLINE, OUTPUT_OPTIONS_DEFAULT_STDOUT_REDIRECTED,
    OUT_RESP_BODY, PRETTY_MAP, PRETTY_STDOUT_TTY_ONLY, RequestType,
    SEPARATOR_CREDENTIALS,
    SEPARATOR_GROUP_ALL_ITEMS, SEPARATOR_GROUP_DATA_ITEMS, URL_SCHEME_RE,
)
from httpie.cli.exceptions import ParseError
from httpie.cli.requestitems import RequestItems
from httpie.context import Environment
from httpie.plugins.registry import plugin_manager
from httpie.utils import ExplicitNullAuth, get_content_type

class HTTPieArgumentParser(argparse.ArgumentParser):

    def __init__(self, *args, formatter_class=HTTPieHelpFormatter, **kwargs):
        kwargs['add_help'] = False
        super().__init__(*args, formatter_class=formatter_class, **kwargs)
        self.env = None
        self.args = None
        self.has_stdin_data = False

    def parse_args(
        self,
        env: Environment,
        args=None,
        namespace=None
    ) -> argparse.Namespace:
        self.env = env
        self.args, no_options = super().parse_known_args(args, namespace)
        if self.args.debug:
            self.args.traceback = True
        self.has_stdin_data = (
            self.env.stdin
            and not self.args.ignore_stdin
            and not self.env.stdin_isatty
        )
        # Arguments processing and environment setup.
        self._apply_no_options(no_options)
        self._process_request_type()
        self._process_download_options()
        self._setup_standard_streams()
        self._process_output_options()
        self._process_pretty_options()
        self._process_format_options()
        self._guess_method()
        self._parse_items()
        if self.has_stdin_data:
            self._body_from_file(self.env.stdin)
        self._process_url()
        self._process_auth()

        if self.args.compress:
            # TODO: allow --compress with --chunked / --multipart
            if self.args.chunked:
                self.error('cannot combine --compress and --chunked')
            if self.args.multipart:
                self.error('cannot combine --compress and --multipart')

        return self.args


from argparse import (FileType, OPTIONAL, SUPPRESS, ZERO_OR_MORE)
from textwrap import dedent, wrap
from httpie import __doc__, __version__
from httpie.cli.argparser import HTTPieArgumentParser
from httpie.cli.argtypes import (
    KeyValueArgType, SessionNameValidator,
    readable_file_arg,
)
from httpie.cli.constants import (
    DEFAULT_FORMAT_OPTIONS, OUTPUT_OPTIONS,
    OUTPUT_OPTIONS_DEFAULT, OUT_REQ_BODY, OUT_REQ_HEAD,
    OUT_RESP_BODY, OUT_RESP_HEAD, PRETTY_MAP, PRETTY_STDOUT_TTY_ONLY,
    RequestType, SEPARATOR_GROUP_ALL_ITEMS, SEPARATOR_PROXY,
    SORTED_FORMAT_OPTIONS_STRING,
    UNSORTED_FORMAT_OPTIONS_STRING,
)
from httpie.output.formatters.colors import (
    AUTO_STYLE, AVAILABLE_STYLES, DEFAULT_STYLE,
)
from httpie.plugins.builtin import BuiltinAuthPlugin
from httpie.plugins.registry import plugin_manager
from httpie.sessions import DEFAULT_SESSIONS_DIR
from httpie.ssl import AVAILABLE_SSL_VERSION_ARG_MAPPING, DEFAULT_SSL_CIPHERS

parser = HTTPieArgumentParser(
    prog='http',
    description='%s <https://httpie.org>' % __doc__.strip(),
    epilog=dedent('''
    For every --OPTION there is also a --no-OPTION that reverts OPTION
    to its default value.

    Suggestions and bug reports are greatly appreciated:

        https://github.com/httpie/httpie/issues

    '''),
)
positional = parser.add_argument_group(
    title='Positional Arguments',
    description=dedent('''
    These arguments come after any flags and in the order they are listed here.
    Only URL is required.

    ''')
)
content_type = parser.add_argument_group(
    title='Predefined Content Types',
    description=None
)
content_processing = parser.add_argument_group(
    title='Content Processing Options',
    description=None
)
output_processing = parser.add_argument_group(title='Output Processing')
_sorted_kwargs = {
    'action': 'append_const',
    'const': SORTED_FORMAT_OPTIONS_STRING,
    'dest': 'format_options'
}
_unsorted_kwargs = {
    'action': 'append_const',
    'const': UNSORTED_FORMAT_OPTIONS_STRING,
    'dest': 'format_options'
}
output_options = parser.add_argument_group(title='Output Options')
sessions = parser.add_argument_group(title='Sessions') \
    .add_mutually_exclusive_group(required=False)
session_name_validator = SessionNameValidator(
    'Session name contains invalid characters.'
)
auth = parser.add_argument_group(title='Authentication')
_auth_plugins = plugin_manager.get_auth_plugins()
network = parser.add_argument_group(title='Network')
ssl = parser.add_argument_group(title='SSL')
troubleshooting = parser.add_argument_group(title='Troubleshooting')

class _AuthTypeLazyChoices:
    # Needed for plugin testing

    def __contains__(self, item):
        return item in plugin_manager.get_auth_plugin_mapping()


from argparse import (FileType, OPTIONAL, SUPPRESS, ZERO_OR_MORE)
from textwrap import dedent, wrap
from httpie import __doc__, __version__
from httpie.cli.argparser import HTTPieArgumentParser
from httpie.cli.argtypes import (
    KeyValueArgType, SessionNameValidator,
    readable_file_arg,
)
from httpie.cli.constants import (
    DEFAULT_FORMAT_OPTIONS, OUTPUT_OPTIONS,
    OUTPUT_OPTIONS_DEFAULT, OUT_REQ_BODY, OUT_REQ_HEAD,
    OUT_RESP_BODY, OUT_RESP_HEAD, PRETTY_MAP, PRETTY_STDOUT_TTY_ONLY,
    RequestType, SEPARATOR_GROUP_ALL_ITEMS, SEPARATOR_PROXY,
    SORTED_FORMAT_OPTIONS_STRING,
    UNSORTED_FORMAT_OPTIONS_STRING,
)
from httpie.output.formatters.colors import (
    AUTO_STYLE, AVAILABLE_STYLES, DEFAULT_STYLE,
)
from httpie.plugins.builtin import BuiltinAuthPlugin
from httpie.plugins.registry import plugin_manager
from httpie.sessions import DEFAULT_SESSIONS_DIR
from httpie.ssl import AVAILABLE_SSL_VERSION_ARG_MAPPING, DEFAULT_SSL_CIPHERS

parser = HTTPieArgumentParser(
    prog='http',
    description='%s <https://httpie.org>' % __doc__.strip(),
    epilog=dedent('''
    For every --OPTION there is also a --no-OPTION that reverts OPTION
    to its default value.

    Suggestions and bug reports are greatly appreciated:

        https://github.com/httpie/httpie/issues

    '''),
)
positional = parser.add_argument_group(
    title='Positional Arguments',
    description=dedent('''
    These arguments come after any flags and in the order they are listed here.
    Only URL is required.

    ''')
)
content_type = parser.add_argument_group(
    title='Predefined Content Types',
    description=None
)
content_processing = parser.add_argument_group(
    title='Content Processing Options',
    description=None
)
output_processing = parser.add_argument_group(title='Output Processing')
_sorted_kwargs = {
    'action': 'append_const',
    'const': SORTED_FORMAT_OPTIONS_STRING,
    'dest': 'format_options'
}
_unsorted_kwargs = {
    'action': 'append_const',
    'const': UNSORTED_FORMAT_OPTIONS_STRING,
    'dest': 'format_options'
}
output_options = parser.add_argument_group(title='Output Options')
sessions = parser.add_argument_group(title='Sessions') \
    .add_mutually_exclusive_group(required=False)
session_name_validator = SessionNameValidator(
    'Session name contains invalid characters.'
)
auth = parser.add_argument_group(title='Authentication')
_auth_plugins = plugin_manager.get_auth_plugins()
network = parser.add_argument_group(title='Network')
ssl = parser.add_argument_group(title='SSL')
troubleshooting = parser.add_argument_group(title='Troubleshooting')

class _AuthTypeLazyChoices:
    # Needed for plugin testing

    def __iter__(self):
        return iter(sorted(plugin_manager.get_auth_plugin_mapping().keys()))


import os
from typing import Callable, Dict, IO, List, Optional, Tuple, Union
from httpie.cli.argtypes import KeyValueArg
from httpie.cli.constants import (
    SEPARATORS_GROUP_MULTIPART, SEPARATOR_DATA_EMBED_FILE_CONTENTS,
    SEPARATOR_DATA_EMBED_RAW_JSON_FILE,
    SEPARATOR_DATA_RAW_JSON, SEPARATOR_DATA_STRING, SEPARATOR_FILE_UPLOAD,
    SEPARATOR_FILE_UPLOAD_TYPE, SEPARATOR_HEADER, SEPARATOR_HEADER_EMPTY,
    SEPARATOR_QUERY_PARAM,
)
from httpie.cli.dicts import (
    MultipartRequestDataDict, RequestDataDict, RequestFilesDict,
    RequestHeadersDict, RequestJSONDataDict,
    RequestQueryParamsDict,
)
from httpie.cli.exceptions import ParseError
from httpie.utils import (get_content_type, load_json_preserve_order)

JSONType = Union[str, bool, int, list, dict]

def process_file_upload_arg(arg: KeyValueArg) -> Tuple[str, IO, str]:
    parts = arg.value.split(SEPARATOR_FILE_UPLOAD_TYPE)
    filename = parts[0]
    mime_type = parts[1] if len(parts) > 1 else None
    try:
        f = open(os.path.expanduser(filename), 'rb')
    except IOError as e:
        raise ParseError('"%s": %s' % (arg.orig, e))
    return (
        os.path.basename(filename),
        f,
        mime_type or get_content_type(filename),
    )


import os
from typing import Callable, Dict, IO, List, Optional, Tuple, Union
from httpie.cli.argtypes import KeyValueArg
from httpie.cli.constants import (
    SEPARATORS_GROUP_MULTIPART, SEPARATOR_DATA_EMBED_FILE_CONTENTS,
    SEPARATOR_DATA_EMBED_RAW_JSON_FILE,
    SEPARATOR_DATA_RAW_JSON, SEPARATOR_DATA_STRING, SEPARATOR_FILE_UPLOAD,
    SEPARATOR_FILE_UPLOAD_TYPE, SEPARATOR_HEADER, SEPARATOR_HEADER_EMPTY,
    SEPARATOR_QUERY_PARAM,
)
from httpie.cli.dicts import (
    MultipartRequestDataDict, RequestDataDict, RequestFilesDict,
    RequestHeadersDict, RequestJSONDataDict,
    RequestQueryParamsDict,
)
from httpie.cli.exceptions import ParseError
from httpie.utils import (get_content_type, load_json_preserve_order)

JSONType = Union[str, bool, int, list, dict]

def process_data_embed_raw_json_file_arg(arg: KeyValueArg) -> JSONType:
    contents = load_text_file(arg)
    value = load_json(arg, contents)
    return value


import os
from typing import Callable, Dict, IO, List, Optional, Tuple, Union
from httpie.cli.argtypes import KeyValueArg
from httpie.cli.constants import (
    SEPARATORS_GROUP_MULTIPART, SEPARATOR_DATA_EMBED_FILE_CONTENTS,
    SEPARATOR_DATA_EMBED_RAW_JSON_FILE,
    SEPARATOR_DATA_RAW_JSON, SEPARATOR_DATA_STRING, SEPARATOR_FILE_UPLOAD,
    SEPARATOR_FILE_UPLOAD_TYPE, SEPARATOR_HEADER, SEPARATOR_HEADER_EMPTY,
    SEPARATOR_QUERY_PARAM,
)
from httpie.cli.dicts import (
    MultipartRequestDataDict, RequestDataDict, RequestFilesDict,
    RequestHeadersDict, RequestJSONDataDict,
    RequestQueryParamsDict,
)
from httpie.cli.exceptions import ParseError
from httpie.utils import (get_content_type, load_json_preserve_order)

JSONType = Union[str, bool, int, list, dict]

def process_data_raw_json_embed_arg(arg: KeyValueArg) -> JSONType:
    value = load_json(arg, arg.value)
    return value


import os
from typing import Callable, Dict, IO, List, Optional, Tuple, Union
from httpie.cli.argtypes import KeyValueArg
from httpie.cli.constants import (
    SEPARATORS_GROUP_MULTIPART, SEPARATOR_DATA_EMBED_FILE_CONTENTS,
    SEPARATOR_DATA_EMBED_RAW_JSON_FILE,
    SEPARATOR_DATA_RAW_JSON, SEPARATOR_DATA_STRING, SEPARATOR_FILE_UPLOAD,
    SEPARATOR_FILE_UPLOAD_TYPE, SEPARATOR_HEADER, SEPARATOR_HEADER_EMPTY,
    SEPARATOR_QUERY_PARAM,
)
from httpie.cli.dicts import (
    MultipartRequestDataDict, RequestDataDict, RequestFilesDict,
    RequestHeadersDict, RequestJSONDataDict,
    RequestQueryParamsDict,
)
from httpie.cli.exceptions import ParseError
from httpie.utils import (get_content_type, load_json_preserve_order)

JSONType = Union[str, bool, int, list, dict]

def load_text_file(item: KeyValueArg) -> str:
    path = item.value
    try:
        with open(os.path.expanduser(path), 'rb') as f:
            return f.read().decode()
    except IOError as e:
        raise ParseError('"%s": %s' % (item.orig, e))
    except UnicodeDecodeError:
        raise ParseError(
            '"%s": cannot embed the content of "%s",'
            ' not a UTF8 or ASCII-encoded text file'
            % (item.orig, item.value)
        )


import argparse
import http.client
import json
import sys
from contextlib import contextmanager
from pathlib import Path
from typing import Callable, Iterable, Union
from urllib.parse import urlparse, urlunparse
import requests
import urllib3
from httpie import __version__
from httpie.cli.dicts import RequestHeadersDict
from httpie.plugins.registry import plugin_manager
from httpie.sessions import get_httpie_session
from httpie.ssl import AVAILABLE_SSL_VERSION_ARG_MAPPING, HTTPieHTTPSAdapter
from httpie.uploads import (
    compress_request, prepare_request_body,
    get_multipart_data_and_content_type,
)
from httpie.utils import get_expired_cookies, repr_dict

FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded; charset=utf-8'
JSON_CONTENT_TYPE = 'application/json'
JSON_ACCEPT = f'{JSON_CONTENT_TYPE}, */*;q=0.5'
DEFAULT_UA = f'HTTPie/{__version__}'

def collect_messages(
    args: argparse.Namespace,
    config_dir: Path,
    request_body_read_callback: Callable[[bytes], None] = None,
) -> Iterable[Union[requests.PreparedRequest, requests.Response]]:
    httpie_session = None
    httpie_session_headers = None
    if args.session or args.session_read_only:
        httpie_session = get_httpie_session(
            config_dir=config_dir,
            session_name=args.session or args.session_read_only,
            host=args.headers.get('Host'),
            url=args.url,
        )
        httpie_session_headers = httpie_session.headers

    request_kwargs = make_request_kwargs(
        args=args,
        base_headers=httpie_session_headers,
        request_body_read_callback=request_body_read_callback
    )
    send_kwargs = make_send_kwargs(args)
    send_kwargs_mergeable_from_env = make_send_kwargs_mergeable_from_env(args)
    requests_session = build_requests_session(
        ssl_version=args.ssl_version,
        ciphers=args.ciphers,
        verify=bool(send_kwargs_mergeable_from_env['verify'])
    )

    if httpie_session:
        httpie_session.update_headers(request_kwargs['headers'])
        requests_session.cookies = httpie_session.cookies
        if args.auth_plugin:
            # Save auth from CLI to HTTPie session.
            httpie_session.auth = {
                'type': args.auth_plugin.auth_type,
                'raw_auth': args.auth_plugin.raw_auth,
            }
        elif httpie_session.auth:
            # Apply auth from HTTPie session
            request_kwargs['auth'] = httpie_session.auth

    if args.debug:
        # TODO: reflect the split between request and send kwargs.
        dump_request(request_kwargs)

    request = requests.Request(**request_kwargs)
    prepared_request = requests_session.prepare_request(request)
    if args.path_as_is:
        prepared_request.url = ensure_path_as_is(
            orig_url=args.url,
            prepped_url=prepared_request.url,
        )
    if args.compress and prepared_request.body:
        compress_request(
            request=prepared_request,
            always=args.compress > 1,
        )
    response_count = 0
    expired_cookies = []
    while prepared_request:
        yield prepared_request
        if not args.offline:
            send_kwargs_merged = requests_session.merge_environment_settings(
                url=prepared_request.url,
                **send_kwargs_mergeable_from_env,
            )
            with max_headers(args.max_headers):
                response = requests_session.send(
                    request=prepared_request,
                    **send_kwargs_merged,
                    **send_kwargs,
                )

            # noinspection PyProtectedMember
            expired_cookies += get_expired_cookies(
                headers=response.raw._original_response.msg._headers
            )

            response_count += 1
            if response.next:
                if args.max_redirects and response_count == args.max_redirects:
                    raise requests.TooManyRedirects
                if args.follow:
                    prepared_request = response.next
                    if args.all:
                        yield response
                    continue
            yield response
        break

    if httpie_session:
        if httpie_session.is_new() or not args.session_read_only:
            httpie_session.cookies = requests_session.cookies
            httpie_session.remove_cookies(
                # TODO: take path & domain into account?
                cookie['name'] for cookie in expired_cookies
            )
            httpie_session.save()


import argparse
import http.client
import json
import sys
from contextlib import contextmanager
from pathlib import Path
from typing import Callable, Iterable, Union
from urllib.parse import urlparse, urlunparse
import requests
import urllib3
from httpie import __version__
from httpie.cli.dicts import RequestHeadersDict
from httpie.plugins.registry import plugin_manager
from httpie.sessions import get_httpie_session
from httpie.ssl import AVAILABLE_SSL_VERSION_ARG_MAPPING, HTTPieHTTPSAdapter
from httpie.uploads import (
    compress_request, prepare_request_body,
    get_multipart_data_and_content_type,
)
from httpie.utils import get_expired_cookies, repr_dict

FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded; charset=utf-8'
JSON_CONTENT_TYPE = 'application/json'
JSON_ACCEPT = f'{JSON_CONTENT_TYPE}, */*;q=0.5'
DEFAULT_UA = f'HTTPie/{__version__}'

def build_requests_session(
    verify: bool,
    ssl_version: str = None,
    ciphers: str = None,
) -> requests.Session:
    requests_session = requests.Session()

    # Install our adapter.
    https_adapter = HTTPieHTTPSAdapter(
        ciphers=ciphers,
        verify=verify,
        ssl_version=(
            AVAILABLE_SSL_VERSION_ARG_MAPPING[ssl_version]
            if ssl_version else None
        ),
    )
    requests_session.mount('https://', https_adapter)

    # Install adapters from plugins.
    for plugin_cls in plugin_manager.get_transport_plugins():
        transport_plugin = plugin_cls()
        requests_session.mount(
            prefix=transport_plugin.prefix,
            adapter=transport_plugin.get_adapter(),
        )

    return requests_session


import argparse
import http.client
import json
import sys
from contextlib import contextmanager
from pathlib import Path
from typing import Callable, Iterable, Union
from urllib.parse import urlparse, urlunparse
import requests
import urllib3
from httpie import __version__
from httpie.cli.dicts import RequestHeadersDict
from httpie.plugins.registry import plugin_manager
from httpie.sessions import get_httpie_session
from httpie.ssl import AVAILABLE_SSL_VERSION_ARG_MAPPING, HTTPieHTTPSAdapter
from httpie.uploads import (
    compress_request, prepare_request_body,
    get_multipart_data_and_content_type,
)
from httpie.utils import get_expired_cookies, repr_dict

FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded; charset=utf-8'
JSON_CONTENT_TYPE = 'application/json'
JSON_ACCEPT = f'{JSON_CONTENT_TYPE}, */*;q=0.5'
DEFAULT_UA = f'HTTPie/{__version__}'

def finalize_headers(headers: RequestHeadersDict) -> RequestHeadersDict:
    final_headers = RequestHeadersDict()
    for name, value in headers.items():
        if value is not None:
            # “leading or trailing LWS MAY be removed without
            # changing the semantics of the field value”
            # <https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html>
            # Also, requests raises `InvalidHeader` for leading spaces.
            value = value.strip()
            if isinstance(value, str):
                # See <https://github.com/httpie/httpie/issues/212>
                value = value.encode('utf8')
        final_headers[name] = value
    return final_headers


import argparse
import http.client
import json
import sys
from contextlib import contextmanager
from pathlib import Path
from typing import Callable, Iterable, Union
from urllib.parse import urlparse, urlunparse
import requests
import urllib3
from httpie import __version__
from httpie.cli.dicts import RequestHeadersDict
from httpie.plugins.registry import plugin_manager
from httpie.sessions import get_httpie_session
from httpie.ssl import AVAILABLE_SSL_VERSION_ARG_MAPPING, HTTPieHTTPSAdapter
from httpie.uploads import (
    compress_request, prepare_request_body,
    get_multipart_data_and_content_type,
)
from httpie.utils import get_expired_cookies, repr_dict

FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded; charset=utf-8'
JSON_CONTENT_TYPE = 'application/json'
JSON_ACCEPT = f'{JSON_CONTENT_TYPE}, */*;q=0.5'
DEFAULT_UA = f'HTTPie/{__version__}'

def make_default_headers(args: argparse.Namespace) -> RequestHeadersDict:
    default_headers = RequestHeadersDict({
        'User-Agent': DEFAULT_UA
    })

    auto_json = args.data and not args.form
    if args.json or auto_json:
        default_headers['Accept'] = JSON_ACCEPT
        if args.json or (auto_json and args.data):
            default_headers['Content-Type'] = JSON_CONTENT_TYPE

    elif args.form and not args.files:
        # If sending files, `requests` will set
        # the `Content-Type` for us.
        default_headers['Content-Type'] = FORM_CONTENT_TYPE
    return default_headers


import argparse
import http.client
import json
import sys
from contextlib import contextmanager
from pathlib import Path
from typing import Callable, Iterable, Union
from urllib.parse import urlparse, urlunparse
import requests
import urllib3
from httpie import __version__
from httpie.cli.dicts import RequestHeadersDict
from httpie.plugins.registry import plugin_manager
from httpie.sessions import get_httpie_session
from httpie.ssl import AVAILABLE_SSL_VERSION_ARG_MAPPING, HTTPieHTTPSAdapter
from httpie.uploads import (
    compress_request, prepare_request_body,
    get_multipart_data_and_content_type,
)
from httpie.utils import get_expired_cookies, repr_dict

FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded; charset=utf-8'
JSON_CONTENT_TYPE = 'application/json'
JSON_ACCEPT = f'{JSON_CONTENT_TYPE}, */*;q=0.5'
DEFAULT_UA = f'HTTPie/{__version__}'

def make_send_kwargs(args: argparse.Namespace) -> dict:
    kwargs = {
        'timeout': args.timeout or None,
        'allow_redirects': False,
    }
    return kwargs


import argparse
import http.client
import json
import sys
from contextlib import contextmanager
from pathlib import Path
from typing import Callable, Iterable, Union
from urllib.parse import urlparse, urlunparse
import requests
import urllib3
from httpie import __version__
from httpie.cli.dicts import RequestHeadersDict
from httpie.plugins.registry import plugin_manager
from httpie.sessions import get_httpie_session
from httpie.ssl import AVAILABLE_SSL_VERSION_ARG_MAPPING, HTTPieHTTPSAdapter
from httpie.uploads import (
    compress_request, prepare_request_body,
    get_multipart_data_and_content_type,
)
from httpie.utils import get_expired_cookies, repr_dict

FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded; charset=utf-8'
JSON_CONTENT_TYPE = 'application/json'
JSON_ACCEPT = f'{JSON_CONTENT_TYPE}, */*;q=0.5'
DEFAULT_UA = f'HTTPie/{__version__}'

def make_send_kwargs_mergeable_from_env(args: argparse.Namespace) -> dict:
    cert = None
    if args.cert:
        cert = args.cert
        if args.cert_key:
            cert = cert, args.cert_key
    kwargs = {
        'proxies': {p.key: p.value for p in args.proxy},
        'stream': True,
        'verify': {
            'yes': True,
            'true': True,
            'no': False,
            'false': False,
        }.get(args.verify.lower(), args.verify),
        'cert': cert,
    }
    return kwargs


import argparse
import http.client
import json
import sys
from contextlib import contextmanager
from pathlib import Path
from typing import Callable, Iterable, Union
from urllib.parse import urlparse, urlunparse
import requests
import urllib3
from httpie import __version__
from httpie.cli.dicts import RequestHeadersDict
from httpie.plugins.registry import plugin_manager
from httpie.sessions import get_httpie_session
from httpie.ssl import AVAILABLE_SSL_VERSION_ARG_MAPPING, HTTPieHTTPSAdapter
from httpie.uploads import (
    compress_request, prepare_request_body,
    get_multipart_data_and_content_type,
)
from httpie.utils import get_expired_cookies, repr_dict

FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded; charset=utf-8'
JSON_CONTENT_TYPE = 'application/json'
JSON_ACCEPT = f'{JSON_CONTENT_TYPE}, */*;q=0.5'
DEFAULT_UA = f'HTTPie/{__version__}'

def make_request_kwargs(
    args: argparse.Namespace,
    base_headers: RequestHeadersDict = None,
    request_body_read_callback=lambda chunk: chunk
) -> dict:
    """
    Translate our `args` into `requests.Request` keyword arguments.

    """
    files = args.files
    # Serialize JSON data, if needed.
    data = args.data
    auto_json = data and not args.form
    if (args.json or auto_json) and isinstance(data, dict):
        if data:
            data = json.dumps(data)
        else:
            # We need to set data to an empty string to prevent requests
            # from assigning an empty list to `response.request.data`.
            data = ''

    # Finalize headers.
    headers = make_default_headers(args)
    if base_headers:
        headers.update(base_headers)
    headers.update(args.headers)
    if args.offline and args.chunked and 'Transfer-Encoding' not in headers:
        # When online, we let requests set the header instead to be able more
        # easily verify chunking is taking place.
        headers['Transfer-Encoding'] = 'chunked'
    headers = finalize_headers(headers)

    if (args.form and files) or args.multipart:
        data, headers['Content-Type'] = get_multipart_data_and_content_type(
            data=args.multipart_data,
            boundary=args.boundary,
            content_type=args.headers.get('Content-Type'),
        )

    kwargs = {
        'method': args.method.lower(),
        'url': args.url,
        'headers': headers,
        'data': prepare_request_body(
            body=data,
            body_read_callback=request_body_read_callback,
            chunked=args.chunked,
            offline=args.offline,
            content_length_header_value=headers.get('Content-Length'),
        ),
        'auth': args.auth,
        'params': args.params.items(),
    }

    return kwargs


import errno
import json
import os
from pathlib import Path
from typing import Union
from httpie import __version__
from httpie.compat import is_windows

ENV_XDG_CONFIG_HOME = 'XDG_CONFIG_HOME'
ENV_HTTPIE_CONFIG_DIR = 'HTTPIE_CONFIG_DIR'
DEFAULT_CONFIG_DIRNAME = 'httpie'
DEFAULT_RELATIVE_XDG_CONFIG_HOME = Path('.config')
DEFAULT_RELATIVE_LEGACY_CONFIG_DIR = Path('.httpie')
DEFAULT_WINDOWS_CONFIG_DIR = Path(
    os.path.expandvars('%APPDATA%')) / DEFAULT_CONFIG_DIRNAME
DEFAULT_CONFIG_DIR = get_default_config_dir()

def get_default_config_dir() -> Path:
    """
    Return the path to the httpie configuration directory.

    This directory isn't guaranteed to exist, and nor are any of its
    ancestors (only the legacy ~/.httpie, if returned, is guaranteed to exist).

    XDG Base Directory Specification support:

        <https://wiki.archlinux.org/index.php/XDG_Base_Directory>

        $XDG_CONFIG_HOME is supported; $XDG_CONFIG_DIRS is not

    """
    # 1. explicitly set through env
    env_config_dir = os.environ.get(ENV_HTTPIE_CONFIG_DIR)
    if env_config_dir:
        return Path(env_config_dir)

    # 2. Windows
    if is_windows:
        return DEFAULT_WINDOWS_CONFIG_DIR

    home_dir = Path.home()

    # 3. legacy ~/.httpie
    legacy_config_dir = home_dir / DEFAULT_RELATIVE_LEGACY_CONFIG_DIR
    if legacy_config_dir.exists():
        return legacy_config_dir

    # 4. XDG
    xdg_config_home_dir = os.environ.get(
        ENV_XDG_CONFIG_HOME,  # 4.1. explicit
        home_dir / DEFAULT_RELATIVE_XDG_CONFIG_HOME  # 4.2. default
    )
    return Path(xdg_config_home_dir) / DEFAULT_CONFIG_DIRNAME


import errno
import json
import os
from pathlib import Path
from typing import Union
from httpie import __version__
from httpie.compat import is_windows

ENV_XDG_CONFIG_HOME = 'XDG_CONFIG_HOME'
ENV_HTTPIE_CONFIG_DIR = 'HTTPIE_CONFIG_DIR'
DEFAULT_CONFIG_DIRNAME = 'httpie'
DEFAULT_RELATIVE_XDG_CONFIG_HOME = Path('.config')
DEFAULT_RELATIVE_LEGACY_CONFIG_DIR = Path('.httpie')
DEFAULT_WINDOWS_CONFIG_DIR = Path(
    os.path.expandvars('%APPDATA%')) / DEFAULT_CONFIG_DIRNAME
DEFAULT_CONFIG_DIR = get_default_config_dir()

class BaseConfigDict(dict):

    name = None

    helpurl = None

    about = None

    def __init__(self, path: Path):
        super().__init__()
        self.path = path

    def ensure_directory(self):
        try:
            self.path.parent.mkdir(mode=0o700, parents=True)
        except OSError as e:
            if e.errno != errno.EEXIST:
                raise


import errno
import json
import os
from pathlib import Path
from typing import Union
from httpie import __version__
from httpie.compat import is_windows

ENV_XDG_CONFIG_HOME = 'XDG_CONFIG_HOME'
ENV_HTTPIE_CONFIG_DIR = 'HTTPIE_CONFIG_DIR'
DEFAULT_CONFIG_DIRNAME = 'httpie'
DEFAULT_RELATIVE_XDG_CONFIG_HOME = Path('.config')
DEFAULT_RELATIVE_LEGACY_CONFIG_DIR = Path('.httpie')
DEFAULT_WINDOWS_CONFIG_DIR = Path(
    os.path.expandvars('%APPDATA%')) / DEFAULT_CONFIG_DIRNAME
DEFAULT_CONFIG_DIR = get_default_config_dir()

class Config(BaseConfigDict):

    FILENAME = 'config.json'

    DEFAULTS = {
        'default_options': []
    }

    def __init__(self, directory: Union[str, Path] = DEFAULT_CONFIG_DIR):
        self.directory = Path(directory)
        super().__init__(path=self.directory / self.FILENAME)
        self.update(self.DEFAULTS)

class BaseConfigDict(dict):

    name = None

    helpurl = None

    about = None

    def __init__(self, path: Path):
        super().__init__()
        self.path = path

    def load(self):
        config_type = type(self).__name__.lower()
        try:
            with self.path.open('rt') as f:
                try:
                    data = json.load(f)
                except ValueError as e:
                    raise ConfigFileError(
                        f'invalid {config_type} file: {e} [{self.path}]'
                    )
                self.update(data)
        except IOError as e:
            if e.errno != errno.ENOENT:
                raise ConfigFileError(f'cannot read {config_type} file: {e}')


import errno
import json
import os
from pathlib import Path
from typing import Union
from httpie import __version__
from httpie.compat import is_windows

ENV_XDG_CONFIG_HOME = 'XDG_CONFIG_HOME'
ENV_HTTPIE_CONFIG_DIR = 'HTTPIE_CONFIG_DIR'
DEFAULT_CONFIG_DIRNAME = 'httpie'
DEFAULT_RELATIVE_XDG_CONFIG_HOME = Path('.config')
DEFAULT_RELATIVE_LEGACY_CONFIG_DIR = Path('.httpie')
DEFAULT_WINDOWS_CONFIG_DIR = Path(
    os.path.expandvars('%APPDATA%')) / DEFAULT_CONFIG_DIRNAME
DEFAULT_CONFIG_DIR = get_default_config_dir()

class BaseConfigDict(dict):

    name = None

    helpurl = None

    about = None

    def __init__(self, path: Path):
        super().__init__()
        self.path = path

    def save(self, fail_silently=False):
        self['__meta__'] = {
            'httpie': __version__
        }
        if self.helpurl:
            self['__meta__']['help'] = self.helpurl

        if self.about:
            self['__meta__']['about'] = self.about

        self.ensure_directory()

        json_string = json.dumps(
            obj=self,
            indent=4,
            sort_keys=True,
            ensure_ascii=True,
        )
        try:
            self.path.write_text(json_string + '\n')
        except IOError:
            if not fail_silently:
                raise


import sys
import os
from pathlib import Path
from typing import IO, Optional
from httpie.compat import is_windows
from httpie.config import DEFAULT_CONFIG_DIR, Config, ConfigFileError
from httpie.utils import repr_dict

class Environment:

    is_windows: bool = is_windows

    config_dir: Path = DEFAULT_CONFIG_DIR

    stdin: Optional[IO] = sys.stdin

    stdin_isatty: bool = stdin.isatty() if stdin else False

    stdin_encoding: str = None

    stdout: IO = sys.stdout

    stdout_isatty: bool = stdout.isatty()

    stdout_encoding: str = None

    stderr: IO = sys.stderr

    stderr_isatty: bool = stderr.isatty()

    colors = 256

    program_name: str = 'http'

    _config: Config = None

    def __init__(self, devnull=None, **kwargs):
        """
        Use keyword arguments to overwrite
        any of the class attributes for this instance.

        """
        assert all(hasattr(type(self), attr) for attr in kwargs.keys())
        self.__dict__.update(**kwargs)

        # The original STDERR unaffected by --quiet’ing.
        self._orig_stderr = self.stderr
        self._devnull = devnull

        # Keyword arguments > stream.encoding > default utf8
        if self.stdin and self.stdin_encoding is None:
            self.stdin_encoding = getattr(
                self.stdin, 'encoding', None) or 'utf8'
        if self.stdout_encoding is None:
            actual_stdout = self.stdout
            if is_windows:
                # noinspection PyUnresolvedReferences
                from colorama import AnsiToWin32
                if isinstance(self.stdout, AnsiToWin32):
                    # noinspection PyUnresolvedReferences
                    actual_stdout = self.stdout.wrapped
            self.stdout_encoding = getattr(
                actual_stdout, 'encoding', None) or 'utf8'


import argparse
import os
import platform
import sys
from typing import List, Optional, Tuple, Union
import requests
from pygments import __version__ as pygments_version
from requests import __version__ as requests_version
from httpie import __version__ as httpie_version
from httpie.cli.constants import OUT_REQ_BODY, OUT_REQ_HEAD, OUT_RESP_BODY, OUT_RESP_HEAD
from httpie.client import collect_messages
from httpie.context import Environment
from httpie.downloads import Downloader
from httpie.output.writer import write_message, write_stream, MESSAGE_SEPARATOR_BYTES
from httpie.plugins.registry import plugin_manager
from httpie.status import ExitStatus, http_status_to_exit_status

def main(args: List[Union[str, bytes]] = sys.argv, env=Environment()) -> ExitStatus:
    """
    The main function.

    Pre-process args, handle some special types of invocations,
    and run the main program with error handling.

    Return exit status code.

    """
    program_name, *args = args
    env.program_name = os.path.basename(program_name)
    args = decode_raw_args(args, env.stdin_encoding)
    plugin_manager.load_installed_plugins()

    from httpie.cli.definition import parser

    if env.config.default_options:
        args = env.config.default_options + args

    include_debug_info = '--debug' in args
    include_traceback = include_debug_info or '--traceback' in args

    if include_debug_info:
        print_debug_info(env)
        if args == ['--debug']:
            return ExitStatus.SUCCESS

    exit_status = ExitStatus.SUCCESS

    try:
        parsed_args = parser.parse_args(
            args=args,
            env=env,
        )
    except KeyboardInterrupt:
        env.stderr.write('\n')
        if include_traceback:
            raise
        exit_status = ExitStatus.ERROR_CTRL_C
    except SystemExit as e:
        if e.code != ExitStatus.SUCCESS:
            env.stderr.write('\n')
            if include_traceback:
                raise
            exit_status = ExitStatus.ERROR
    else:
        try:
            exit_status = program(
                args=parsed_args,
                env=env,
            )
        except KeyboardInterrupt:
            env.stderr.write('\n')
            if include_traceback:
                raise
            exit_status = ExitStatus.ERROR_CTRL_C
        except SystemExit as e:
            if e.code != ExitStatus.SUCCESS:
                env.stderr.write('\n')
                if include_traceback:
                    raise
                exit_status = ExitStatus.ERROR
        except requests.Timeout:
            exit_status = ExitStatus.ERROR_TIMEOUT
            env.log_error(f'Request timed out ({parsed_args.timeout}s).')
        except requests.TooManyRedirects:
            exit_status = ExitStatus.ERROR_TOO_MANY_REDIRECTS
            env.log_error(
                f'Too many redirects'
                f' (--max-redirects={parsed_args.max_redirects}).'
            )
        except Exception as e:
            # TODO: Further distinction between expected and unexpected errors.
            msg = str(e)
            if hasattr(e, 'request'):
                request = e.request
                if hasattr(request, 'url'):
                    msg = (
                        f'{msg} while doing a {request.method}'
                        f' request to URL: {request.url}'
                    )
            env.log_error(f'{type(e).__name__}: {msg}')
            if include_traceback:
                raise
            exit_status = ExitStatus.ERROR

    return exit_status


import argparse
import os
import platform
import sys
from typing import List, Optional, Tuple, Union
import requests
from pygments import __version__ as pygments_version
from requests import __version__ as requests_version
from httpie import __version__ as httpie_version
from httpie.cli.constants import OUT_REQ_BODY, OUT_REQ_HEAD, OUT_RESP_BODY, OUT_RESP_HEAD
from httpie.client import collect_messages
from httpie.context import Environment
from httpie.downloads import Downloader
from httpie.output.writer import write_message, write_stream, MESSAGE_SEPARATOR_BYTES
from httpie.plugins.registry import plugin_manager
from httpie.status import ExitStatus, http_status_to_exit_status

def get_output_options(
    args: argparse.Namespace,
    message: Union[requests.PreparedRequest, requests.Response]
) -> Tuple[bool, bool]:
    return {
        requests.PreparedRequest: (
            OUT_REQ_HEAD in args.output_options,
            OUT_REQ_BODY in args.output_options,
        ),
        requests.Response: (
            OUT_RESP_HEAD in args.output_options,
            OUT_RESP_BODY in args.output_options,
        ),
    }[type(message)]


import argparse
import os
import platform
import sys
from typing import List, Optional, Tuple, Union
import requests
from pygments import __version__ as pygments_version
from requests import __version__ as requests_version
from httpie import __version__ as httpie_version
from httpie.cli.constants import OUT_REQ_BODY, OUT_REQ_HEAD, OUT_RESP_BODY, OUT_RESP_HEAD
from httpie.client import collect_messages
from httpie.context import Environment
from httpie.downloads import Downloader
from httpie.output.writer import write_message, write_stream, MESSAGE_SEPARATOR_BYTES
from httpie.plugins.registry import plugin_manager
from httpie.status import ExitStatus, http_status_to_exit_status

def program(args: argparse.Namespace, env: Environment) -> ExitStatus:
    """
    The main program without error handling.

    """
    # TODO: Refactor and drastically simplify, especially so that the separator logic is elsewhere.
    exit_status = ExitStatus.SUCCESS
    downloader = None
    initial_request: Optional[requests.PreparedRequest] = None
    final_response: Optional[requests.Response] = None

    def separate():
        getattr(env.stdout, 'buffer', env.stdout).write(MESSAGE_SEPARATOR_BYTES)

    def request_body_read_callback(chunk: bytes):
        should_pipe_to_stdout = bool(
            # Request body output desired
            OUT_REQ_BODY in args.output_options
            # & not `.read()` already pre-request (e.g., for  compression)
            and initial_request
            # & non-EOF chunk
            and chunk
        )
        if should_pipe_to_stdout:
            msg = requests.PreparedRequest()
            msg.is_body_upload_chunk = True
            msg.body = chunk
            msg.headers = initial_request.headers
            write_message(requests_message=msg, env=env, args=args, with_body=True, with_headers=False)

    try:
        if args.download:
            args.follow = True  # --download implies --follow.
            downloader = Downloader(output_file=args.output_file, progress_file=env.stderr, resume=args.download_resume)
            downloader.pre_request(args.headers)
        messages = collect_messages(args=args, config_dir=env.config.directory,
                                    request_body_read_callback=request_body_read_callback)
        force_separator = False
        prev_with_body = False

        # Process messages as they’re generated
        for message in messages:
            is_request = isinstance(message, requests.PreparedRequest)
            with_headers, with_body = get_output_options(args=args, message=message)
            do_write_body = with_body
            if prev_with_body and (with_headers or with_body) and (force_separator or not env.stdout_isatty):
                # Separate after a previous message with body, if needed. See test_tokens.py.
                separate()
            force_separator = False
            if is_request:
                if not initial_request:
                    initial_request = message
                    is_streamed_upload = not isinstance(message.body, (str, bytes))
                    if with_body:
                        do_write_body = not is_streamed_upload
                        force_separator = is_streamed_upload and env.stdout_isatty
            else:
                final_response = message
                if args.check_status or downloader:
                    exit_status = http_status_to_exit_status(http_status=message.status_code, follow=args.follow)
                    if exit_status != ExitStatus.SUCCESS and (not env.stdout_isatty or args.quiet):
                        env.log_error(f'HTTP {message.raw.status} {message.raw.reason}', level='warning')
            write_message(requests_message=message, env=env, args=args, with_headers=with_headers,
                          with_body=do_write_body)
            prev_with_body = with_body

        # Cleanup
        if force_separator:
            separate()
        if downloader and exit_status == ExitStatus.SUCCESS:
            # Last response body download.
            download_stream, download_to = downloader.start(
                initial_url=initial_request.url,
                final_response=final_response,
            )
            write_stream(stream=download_stream, outfile=download_to, flush=False)
            downloader.finish()
            if downloader.interrupted:
                exit_status = ExitStatus.ERROR
                env.log_error(
                    'Incomplete download: size=%d; downloaded=%d' % (
                        downloader.status.total_size,
                        downloader.status.downloaded
                    ))
        return exit_status

    finally:
        if downloader and not downloader.finished:
            downloader.failed()
        if not isinstance(args, list) and args.output_file and args.output_file_specified:
            args.output_file.close()


from typing import Iterable, Optional
from urllib.parse import urlsplit

class HTTPResponse(HTTPMessage):

    def iter_lines(self, chunk_size):
        return ((line, b'\n') for line in self._orig.iter_lines(chunk_size))


from typing import Iterable, Optional
from urllib.parse import urlsplit

class HTTPRequest(HTTPMessage):

    def iter_body(self, chunk_size):
        yield self.body


from typing import Iterable, Optional
from urllib.parse import urlsplit

class HTTPRequest(HTTPMessage):

    def iter_lines(self, chunk_size):
        yield self.body, b''


import json
from typing import Optional, Type
import pygments.lexer
import pygments.lexers
import pygments.style
import pygments.styles
import pygments.token
from pygments.formatters.terminal import TerminalFormatter
from pygments.formatters.terminal256 import Terminal256Formatter
from pygments.lexer import Lexer
from pygments.lexers.special import TextLexer
from pygments.lexers.text import HttpLexer as PygmentsHttpLexer
from pygments.util import ClassNotFound
from httpie.compat import is_windows
from httpie.context import Environment
from httpie.plugins import FormatterPlugin

AUTO_STYLE = 'auto'
DEFAULT_STYLE = AUTO_STYLE
SOLARIZED_STYLE = 'solarized'
AVAILABLE_STYLES = set(pygments.styles.get_all_styles())

def get_lexer(
    mime: str,
    explicit_json=False,
    body=''
) -> Optional[Type[Lexer]]:
    # Build candidate mime type and lexer names.
    mime_types, lexer_names = [mime], []
    type_, subtype = mime.split('/', 1)
    if '+' not in subtype:
        lexer_names.append(subtype)
    else:
        subtype_name, subtype_suffix = subtype.split('+', 1)
        lexer_names.extend([subtype_name, subtype_suffix])
        mime_types.extend([
            '%s/%s' % (type_, subtype_name),
            '%s/%s' % (type_, subtype_suffix)
        ])

    # As a last resort, if no lexer feels responsible, and
    # the subtype contains 'json', take the JSON lexer
    if 'json' in subtype:
        lexer_names.append('json')

    # Try to resolve the right lexer.
    lexer = None
    for mime_type in mime_types:
        try:
            lexer = pygments.lexers.get_lexer_for_mimetype(mime_type)
            break
        except ClassNotFound:
            pass
    else:
        for name in lexer_names:
            try:
                lexer = pygments.lexers.get_lexer_by_name(name)
            except ClassNotFound:
                pass

    if explicit_json and body and (not lexer or isinstance(lexer, TextLexer)):
        # JSON response with an incorrect Content-Type?
        try:
            json.loads(body)  # FIXME: the body also gets parsed in json.py
        except ValueError:
            pass  # Nope
        else:
            lexer = pygments.lexers.get_lexer_by_name('json')

    return lexer