<commit_before>"""
Core Python language utilties.

.. versionadded:: 0.0.2
"""
from __future__ import unicode_literals

import keyword
from numbers import Number
import re
import sys

from taipan._compat import IS_PY3
from taipan.strings import ensure_string


__all__ = [
    'cast',

    'is_contextmanager', 'is_number',
    'ensure_boolean', 'ensure_contextmanager', 'ensure_number',

    'has_identifier_form', 'is_identifier',
    'is_keyword',
    'is_magic', 'is_dunder',
]


__missing = object()


def cast(type_, value, default=__missing):
    """Cast a value to given type, optionally returning a default, if provided.

    :param type_: Type to cast the ``value`` into
    :param value: Value to cast into ``type_``

    :return: ``value`` casted to ``type_``.
             If cast was unsuccessful and ``default`` was provided,
             it is returned instead.

    :raise AssertionError: When ``type_`` is not actually a Python type
    :raise TypeError: When cast was unsuccessful and no ``default`` was given
    """
    # ``type_`` nor being a type would theoretically be grounds for TypeError,
    # but since that kind of exception is a valid and expected outcome here
    # in some cases, we use the closest Python has to compilation error instead
    assert isinstance(type_, type)

    # conunterintuitively, invalid conversions to numeric types
    # would raise ValueError rather than the more appropriate TypeError,
    # so we correct this inconsistency
    to_number = issubclass(type_, Number)
    exception = ValueError if to_number else TypeError

    try:
        return type_(value)
    except exception:
        if default is __missing:
            if to_number:
                e = sys.exc_info()[1]

                # since Python 3 chains exceptions, we can supply slightly
                # more relevant error message while still retaining
                # the original information of ValueError as the cause
                msg = ("cannot convert %r to %r" % (value, type_) if IS_PY3
                       else str(e))
                raise TypeError(msg)
            else:<commit_msg>Remove silly exception handling from .lang.cast<commit_after>"""
Core Python language utilties.

.. versionadded:: 0.0.2
"""
from __future__ import unicode_literals

import keyword
from numbers import Number
import re

from taipan._compat import IS_PY3
from taipan.strings import ensure_string


__all__ = [
    'cast',

    'is_contextmanager', 'is_number',
    'ensure_boolean', 'ensure_contextmanager', 'ensure_number',

    'has_identifier_form', 'is_identifier',
    'is_keyword',
    'is_magic', 'is_dunder',
]


__missing = object()


def cast(type_, value, default=__missing):
    """Cast a value to given type, optionally returning a default, if provided.

    :param type_: Type to cast the ``value`` into
    :param value: Value to cast into ``type_``

    :return: ``value`` casted to ``type_``.
             If cast was unsuccessful and ``default`` was provided,
             it is returned instead.

    :raise AssertionError: When ``type_`` is not actually a Python type
    :raise TypeError: When cast was unsuccessful and no ``default`` was given
    """
    # ``type_`` nor being a type would theoretically be grounds for TypeError,
    # but since that kind of exception is a valid and expected outcome here
    # in some cases, we use the closest Python has to compilation error instead
    assert isinstance(type_, type)

    # conunterintuitively, invalid conversions to numeric types
    # would raise ValueError rather than the more appropriate TypeError,
    # so we correct this inconsistency
    to_number = issubclass(type_, Number)
    exception = ValueError if to_number else TypeError

    try:
        return type_(value)
    except exception as e:
        if default is __missing:
            if to_number:
                # since Python 3 chains exceptions, we can supply slightly
                # more relevant error message while still retaining
                # the original information of ValueError as the cause
                msg = ("cannot convert %r to %r" % (value, type_) if IS_PY3
                       else str(e))
                raise TypeError(msg)
            else:
{'fraction_non_alphanumeric': 0.077665333019534, 'fraction_numerical': 0.003059543421981643, 'mean_word_length': 2.8253825382538253, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5236799', 'n_tokens_mistral': 1146, 'n_tokens_neox': 1087, 'n_words': 470}