Example #1
0
    def get_field_info(self, field):
        """
        Given an instance of a serializer field, return a dictionary
        of metadata about it.
        """
        field_info = OrderedDict()
        field_info['type'] = self.label_lookup[field]
        field_info['required'] = getattr(field, 'required', False)

        for attr in [
                'read_only', 'label', 'help_text', 'min_length', 'max_length'
        ]:
            value = getattr(field, attr, None)
            if value is not None and value != '':
                field_info[attr] = force_text(value, strings_only=True)

        if hasattr(field, 'choices'):
            field_info['choices'] = [{
                'value':
                choice_value,
                'display_name':
                force_text(choice_name, strings_only=True)
            } for choice_value, choice_name in field.choices.items()]

        return field_info
    def __init__(self, wait=None, detail=None):
        if detail is not None:
            self.detail = force_text(detail)
        else:
            self.detail = force_text(self.default_detail)

        if wait is None:
            self.wait = None
        else:
            self.wait = math.ceil(wait)
            self.detail += ' ' + force_text(
                self.extra_detail % {'wait': self.wait})
Example #3
0
 def get_encoded_filename(self, filename_parm):
     """
     Handle encoded filenames per RFC6266. See also:
     http://tools.ietf.org/html/rfc2231#section-4
     """
     encoded_filename = force_text(filename_parm['filename*'])
     try:
         charset, lang, filename = encoded_filename.split('\'', 2)
         filename = urlparse.unquote(filename)
     except (ValueError, LookupError):
         filename = force_text(filename_parm['filename'])
     return filename
Example #4
0
 def get_encoded_filename(self, filename_parm):
     """
     Handle encoded filenames per RFC6266. See also:
     http://tools.ietf.org/html/rfc2231#section-4
     """
     encoded_filename = force_text(filename_parm['filename*'])
     try:
         charset, lang, filename = encoded_filename.split('\'', 2)
         filename = urlparse.unquote(filename)
     except (ValueError, LookupError):
         filename = force_text(filename_parm['filename'])
     return filename
    def __init__(self, wait=None, detail=None):
        if detail is not None:
            self.detail = force_text(detail)
        else:
            self.detail = force_text(self.default_detail)

        if wait is None:
            self.wait = None
        else:
            self.wait = math.ceil(wait)
            self.detail += ' ' + force_text(
                self.extra_detail % {'wait': self.wait}
            )
 def default(self, o):
     # For Date Time string spec, see ECMA 262
     # http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
     if isinstance(o, Promise):
         return force_text(o)
     elif isinstance(o, datetime.datetime):
         r = o.isoformat()
         if o.microsecond:
             r = r[:23] + r[26:]
         if r.endswith('+00:00'):
             r = r[:-6] + 'Z'
         return r
     elif isinstance(o, datetime.date):
         return o.isoformat()
     elif isinstance(o, datetime.time):
         if timezone and timezone.is_aware(o):
             raise ValueError("JSON can't represent timezone-aware times.")
         r = o.isoformat()
         if o.microsecond:
             r = r[:12]
         return r
     elif isinstance(o, datetime.timedelta):
         return str(o.total_seconds())
     elif isinstance(o, decimal.Decimal):
         return str(o)
     elif hasattr(o, 'tolist'):
         return o.tolist()
     elif hasattr(o, '__iter__'):
         return [i for i in o]
     return super(JSONEncoder, self).default(o)
def force_text_recursive(data):
    if isinstance(data, list):
        return [force_text_recursive(item) for item in data]
    elif isinstance(data, dict):
        return dict([(key, force_text_recursive(value))
                     for key, value in data.items()])
    return force_text(data)
 def as_form_field(self):
     values = {}
     for key, value in self.value.items():
         if isinstance(value, (list, dict)):
             values[key] = value
         else:
             values[key] = "" if value is None else force_text(value)
     return self.__class__(self._field, values, self.errors, self._prefix)
 def as_form_field(self):
     values = {}
     for key, value in self.value.items():
         if isinstance(value, (list, dict)):
             values[key] = value
         else:
             values[key] = '' if value is None else force_text(value)
     return self.__class__(self._field, values, self.errors, self._prefix)
def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=True):
    """
    Converts any URLs in text into clickable links.

    Works on http://, https://, www. links and links ending in .org, .net or
    .com. Links can have trailing punctuation (periods, commas, close-parens)
    and leading punctuation (opening parens) and it'll still do the right
    thing.

    If trim_url_limit is not None, the URLs in link text longer than this limit
    will truncated to trim_url_limit-3 characters and appended with an elipsis.

    If nofollow is True, the URLs in link text will get a rel="nofollow"
    attribute.

    If autoescape is True, the link text and URLs will get autoescaped.
    """
    trim_url = lambda x, limit=trim_url_limit: limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x
    safe_input = isinstance(text, SafeData)
    words = word_split_re.split(force_text(text))
    nofollow_attr = nofollow and ' rel="nofollow"' or ''
    for i, word in enumerate(words):
        match = None
        if '.' in word or '@' in word or ':' in word:
            match = punctuation_re.match(word)
        if match:
            lead, middle, trail = match.groups()
            # Make URL we want to point to.
            url = None
            if middle.startswith('http://') or middle.startswith('https://'):
                url = middle
            elif middle.startswith('www.') or ('@' not in middle and \
                    middle and middle[0] in string.ascii_letters + string.digits and \
                    (middle.endswith('.org') or middle.endswith('.net') or middle.endswith('.com'))):
                url = 'http://%s' % middle
            elif '@' in middle and not ':' in middle and simple_email_re.match(middle):
                url = 'mailto:%s' % middle
                nofollow_attr = ''
            # Make link.
            if url:
                trimmed = trim_url(middle)
                if autoescape and not safe_input:
                    lead, trail = escape(lead), escape(trail)
                    url, trimmed = escape(url), escape(trimmed)
                middle = '<a href="%s"%s>%s</a>' % (url, nofollow_attr, trimmed)
                words[i] = mark_safe('%s%s%s' % (lead, middle, trail))
            else:
                if safe_input:
                    words[i] = mark_safe(word)
                elif autoescape:
                    words[i] = escape(word)
        elif safe_input:
            words[i] = mark_safe(word)
        elif autoescape:
            words[i] = escape(word)
    return mark_safe(''.join(words))
Example #11
0
 def metadata(self):
     metadata = SortedDict()
     metadata["type"] = self.type_label
     metadata["required"] = getattr(self, "required", False)
     optional_attrs = ["read_only", "label", "help_text", "min_length", "max_length"]
     for attr in optional_attrs:
         value = getattr(self, attr, None)
         if value is not None and value != "":
             metadata[attr] = force_text(value, strings_only=True)
     return metadata
Example #12
0
 def metadata(self):
     metadata = SortedDict()
     metadata['type'] = self.type_label
     metadata['required'] = getattr(self, 'required', False)
     optional_attrs = ['read_only', 'label', 'help_text',
                       'min_length', 'max_length']
     for attr in optional_attrs:
         value = getattr(self, attr, None)
         if value is not None and value != '':
             metadata[attr] = force_text(value, strings_only=True)
     return metadata
Example #13
0
def strip_multiple_choice_msg(help_text):
    """
    Remove the 'Hold down "control" ...' message that is Django enforces in
    select multiple fields on ModelForms.  (Required for 1.5 and earlier)

    See https://code.djangoproject.com/ticket/9321
    """
    multiple_choice_msg = _(' Hold down "Control", or "Command" on a Mac, to select more than one.')
    multiple_choice_msg = force_text(multiple_choice_msg)

    return help_text.replace(multiple_choice_msg, '')
def force_text_recursive(data):
    if isinstance(data, list):
        return [
            force_text_recursive(item) for item in data
        ]
    elif isinstance(data, dict):
        return dict([
            (key, force_text_recursive(value))
            for key, value in data.items()
        ])
    return force_text(data)
Example #15
0
    def get_field_info(self, field):
        """
        Given an instance of a serializer field, return a dictionary
        of metadata about it.
        """
        field_info = OrderedDict()
        field_info['type'] = self.label_lookup[field]
        field_info['required'] = getattr(field, 'required', False)

        for attr in ['read_only', 'label', 'help_text', 'min_length', 'max_length']:
            value = getattr(field, attr, None)
            if value is not None and value != '':
                field_info[attr] = force_text(value, strings_only=True)

        if hasattr(field, 'choices'):
            field_info['choices'] = [
                {
                    'value': choice_value,
                    'display_name': force_text(choice_name, strings_only=True)
                }
                for choice_value, choice_name in field.choices.items()
            ]

        return field_info
Example #16
0
def _force_text_recursive(data):
    """
    Descend into a nested data structure, forcing any
    lazy translation strings into plain text.
    """
    if isinstance(data, list):
        return [
            _force_text_recursive(item) for item in data
        ]
    elif isinstance(data, dict):
        return dict([
            (key, _force_text_recursive(value))
            for key, value in data.items()
        ])
    return force_text(data)
Example #17
0
    def get_filename(self, stream, media_type, parser_context):
        """
        Detects the uploaded file name. First searches a 'filename' url kwarg.
        Then tries to parse Content-Disposition header.
        """
        try:
            return parser_context['kwargs']['filename']
        except KeyError:
            pass

        try:
            meta = parser_context['request'].META
            disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode('utf-8'))
            return force_text(disposition[1]['filename'])
        except (AttributeError, KeyError):
            pass
Example #18
0
    def get_filename(self, stream, media_type, parser_context):
        """
        Detects the uploaded file name. First searches a 'filename' url kwarg.
        Then tries to parse Content-Disposition header.
        """
        try:
            return parser_context['kwargs']['filename']
        except KeyError:
            pass

        try:
            meta = parser_context['request'].META
            disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode('utf-8'))
            return force_text(disposition[1]['filename'])
        except (AttributeError, KeyError):
            pass
Example #19
0
    def to_native(self, value):
        """
        Converts the field's value into it's simple representation.
        """
        if is_simple_callable(value):
            value = value()

        if is_protected_type(value):
            return value
        elif is_non_str_iterable(value) and not isinstance(value, (dict, six.string_types)):
            return [self.to_native(item) for item in value]
        elif isinstance(value, dict):
            # Make sure we preserve field ordering, if it exists
            ret = SortedDict()
            for key, val in value.items():
                ret[key] = self.to_native(val)
            return ret
        return force_text(value)
Example #20
0
def dedent(content):
    """
    Remove leading indent from a block of text.
    Used when generating descriptions from docstrings.

    Note that python's `textwrap.dedent` doesn't quite cut it,
    as it fails to dedent multiline docstrings that include
    unindented text on the initial line.
    """
    content = force_text(content)
    whitespace_counts = [len(line) - len(line.lstrip(" ")) for line in content.splitlines()[1:] if line.lstrip()]

    # unindent the content if needed
    if whitespace_counts:
        whitespace_pattern = "^" + (" " * min(whitespace_counts))
        content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), "", content)

    return content.strip()
Example #21
0
    def to_native(self, value):
        """
        Converts the field's value into it's simple representation.
        """
        if is_simple_callable(value):
            value = value()

        if is_protected_type(value):
            return value
        elif (is_non_str_iterable(value)
              and not isinstance(value, (dict, six.string_types))):
            return [self.to_native(item) for item in value]
        elif isinstance(value, dict):
            # Make sure we preserve field ordering, if it exists
            ret = SortedDict()
            for key, val in value.items():
                ret[key] = self.to_native(val)
            return ret
        return force_text(value)
Example #22
0
 def default(self, o):
     # A dictionary may have a date as it's key, explicitly handle this
     if isinstance(o, dict):
         result = {}
         for key, value in o.iteritems():
             result[self.default(key)] = self.default(value)
         return result
     # For Date Time string spec, see ECMA 262
     # http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
     elif isinstance(o, Promise):
         return force_text(o)
     elif isinstance(o, (geometry.LineString, geometry.MultiLineString,
                         geometry.MultiPoint, geometry.MultiPolygon,
                         geometry.Point, geometry.Polygon)):
         return o.__geo_interface__
     elif isinstance(o, datetime.datetime):
         r = o.isoformat()
         if o.microsecond:
             r = r[:23] + r[26:]
         if r.endswith('+00:00'):
             r = r[:-6] + 'Z'
         return r
     elif isinstance(o, datetime.date):
         return o.isoformat()
     elif isinstance(o, datetime.time):
         if timezone and timezone.is_aware(o):
             raise ValueError("JSON can't represent timezone-aware times.")
         r = o.isoformat()
         if o.microsecond:
             r = r[:12]
         return r
     elif isinstance(o, datetime.timedelta):
         return str(o.total_seconds())
     elif isinstance(o, decimal.Decimal):
         return str(o)
     elif hasattr(o, 'tolist'):
         return o.tolist()
     elif hasattr(o, '__iter__'):
         return [self.default(i) for i in o]
     else:
         return o
def smart_repr(value):
    if isinstance(value, models.Manager):
        return manager_repr(value)

    if isinstance(value, Promise) and value._delegate_text:
        value = force_text(value)

    value = repr(value)

    # Representations like u'help text'
    # should simply be presented as 'help text'
    if value.startswith("u'") and value.endswith("'"):
        return value[1:]

    # Representations like
    # <django.core.validators.RegexValidator object at 0x1047af050>
    # Should be presented as
    # <django.core.validators.RegexValidator object>
    value = re.sub(' at 0x[0-9a-f]{4,32}>', '>', value)

    return value
def smart_repr(value):
    if isinstance(value, models.Manager):
        return manager_repr(value)

    if isinstance(value, Promise) and value._delegate_text:
        value = force_text(value)

    value = repr(value)

    # Representations like u'help text'
    # should simply be presented as 'help text'
    if value.startswith("u'") and value.endswith("'"):
        return value[1:]

    # Representations like
    # <django.core.validators.RegexValidator object at 0x1047af050>
    # Should be presented as
    # <django.core.validators.RegexValidator object>
    value = re.sub(' at 0x[0-9a-f]{4,32}>', '>', value)

    return value
Example #25
0
 def default(self, obj):
     # For Date Time string spec, see ECMA 262
     # http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
     if isinstance(obj, Promise):
         return force_text(obj)
     elif isinstance(obj, datetime.datetime):
         representation = obj.isoformat()
         if obj.microsecond:
             representation = representation[:23] + representation[26:]
         if representation.endswith('+00:00'):
             representation = representation[:-6] + 'Z'
         return representation
     elif isinstance(obj, datetime.date):
         return obj.isoformat()
     elif isinstance(obj, datetime.time):
         if timezone and timezone.is_aware(obj):
             raise ValueError("JSON can't represent timezone-aware times.")
         representation = obj.isoformat()
         if obj.microsecond:
             representation = representation[:12]
         return representation
     elif isinstance(obj, datetime.timedelta):
         return six.text_type(obj.total_seconds())
     elif isinstance(obj, decimal.Decimal):
         # Serializers will coerce decimals to strings by default.
         return float(obj)
     elif isinstance(obj, QuerySet):
         return tuple(obj)
     elif hasattr(obj, 'tolist'):
         # Numpy arrays and array scalars.
         return obj.tolist()
     elif hasattr(obj, '__getitem__'):
         try:
             return dict(obj)
         except:
             pass
     elif hasattr(obj, '__iter__'):
         return tuple(item for item in obj)
     return super(JSONEncoder, self).default(obj)
 def default(self, obj):
     # For Date Time string spec, see ECMA 262
     # http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
     if isinstance(obj, Promise):
         return force_text(obj)
     elif isinstance(obj, datetime.datetime):
         representation = obj.isoformat()
         if obj.microsecond:
             representation = representation[:23] + representation[26:]
         if representation.endswith('+00:00'):
             representation = representation[:-6] + 'Z'
         return representation
     elif isinstance(obj, datetime.date):
         return obj.isoformat()
     elif isinstance(obj, datetime.time):
         if timezone and timezone.is_aware(obj):
             raise ValueError("JSON can't represent timezone-aware times.")
         representation = obj.isoformat()
         if obj.microsecond:
             representation = representation[:12]
         return representation
     elif isinstance(obj, datetime.timedelta):
         return six.text_type(obj.total_seconds())
     elif isinstance(obj, decimal.Decimal):
         # Serializers will coerce decimals to strings by default.
         return float(obj)
     elif isinstance(obj, QuerySet):
         return tuple(obj)
     elif hasattr(obj, 'tolist'):
         # Numpy arrays and array scalars.
         return obj.tolist()
     elif hasattr(obj, '__getitem__'):
         try:
             return dict(obj)
         except:
             pass
     elif hasattr(obj, '__iter__'):
         return tuple(item for item in obj)
     return super(JSONEncoder, self).default(obj)
Example #27
0
def dedent(content):
    """
    Remove leading indent from a block of text.
    Used when generating descriptions from docstrings.

    Note that python's `textwrap.dedent` doesn't quite cut it,
    as it fails to dedent multiline docstrings that include
    unindented text on the initial line.
    """
    content = force_text(content)
    whitespace_counts = [
        len(line) - len(line.lstrip(' ')) for line in content.splitlines()[1:]
        if line.lstrip()
    ]

    # unindent the content if needed
    if whitespace_counts:
        whitespace_pattern = '^' + (' ' * min(whitespace_counts))
        content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '',
                         content)

    return content.strip()
 def __init__(self, method, detail=None):
     if detail is not None:
         self.detail = force_text(detail)
     else:
         self.detail = force_text(self.default_detail) % method
 def as_form_field(self):
     return BoundField(self._field, force_text(self.value), self.errors, self._prefix)
 def __init__(self, detail=None, available_renderers=None):
     if detail is not None:
         self.detail = force_text(detail)
     else:
         self.detail = force_text(self.default_detail)
     self.available_renderers = available_renderers
 def __init__(self, detail=None, available_renderers=None):
     if detail is not None:
         self.detail = force_text(detail)
     else:
         self.detail = force_text(self.default_detail)
     self.available_renderers = available_renderers
 def __init__(self, method, detail=None):
     if detail is not None:
         self.detail = force_text(detail)
     else:
         self.detail = force_text(self.default_detail) % method
 def as_form_field(self):
     value = '' if self.value is None else force_text(self.value)
     return self.__class__(self._field, value, self.errors, self._prefix)
 def as_form_field(self):
     value = "" if self.value is None else force_text(self.value)
     return self.__class__(self._field, value, self.errors, self._prefix)
Example #35
0
def urlize_quoted_links(text,
                        trim_url_limit=None,
                        nofollow=True,
                        autoescape=True):
    """
    Converts any URLs in text into clickable links.

    Works on http://, https://, www. links and links ending in .org, .net or
    .com. Links can have trailing punctuation (periods, commas, close-parens)
    and leading punctuation (opening parens) and it'll still do the right
    thing.

    If trim_url_limit is not None, the URLs in link text longer than this limit
    will truncated to trim_url_limit-3 characters and appended with an elipsis.

    If nofollow is True, the URLs in link text will get a rel="nofollow"
    attribute.

    If autoescape is True, the link text and URLs will get autoescaped.
    """
    trim_url = lambda x, limit=trim_url_limit: limit is not None and (len(
        x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x
    safe_input = isinstance(text, SafeData)
    words = word_split_re.split(force_text(text))
    nofollow_attr = nofollow and ' rel="nofollow"' or ''
    for i, word in enumerate(words):
        match = None
        if '.' in word or '@' in word or ':' in word:
            match = punctuation_re.match(word)
        if match:
            lead, middle, trail = match.groups()
            # Make URL we want to point to.
            url = None
            if middle.startswith('http://') or middle.startswith('https://'):
                url = middle
            elif middle.startswith('www.') or ('@' not in middle and \
                    middle and middle[0] in string.ascii_letters + string.digits and \
                    (middle.endswith('.org') or middle.endswith('.net') or middle.endswith('.com'))):
                url = 'http://%s' % middle
            elif '@' in middle and not ':' in middle and simple_email_re.match(
                    middle):
                url = 'mailto:%s' % middle
                nofollow_attr = ''
            # Make link.
            if url:
                trimmed = trim_url(middle)
                if autoescape and not safe_input:
                    lead, trail = escape(lead), escape(trail)
                    url, trimmed = escape(url), escape(trimmed)
                middle = '<a href="%s"%s>%s</a>' % (url, nofollow_attr,
                                                    trimmed)
                words[i] = mark_safe('%s%s%s' % (lead, middle, trail))
            else:
                if safe_input:
                    words[i] = mark_safe(word)
                elif autoescape:
                    words[i] = escape(word)
        elif safe_input:
            words[i] = mark_safe(word)
        elif autoescape:
            words[i] = escape(word)
    return mark_safe(''.join(words))
def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=True):
    """
    Converts any URLs in text into clickable links.

    Works on http://, https://, www. links, and also on links ending in one of
    the original seven gTLDs (.com, .edu, .gov, .int, .mil, .net, and .org).
    Links can have trailing punctuation (periods, commas, close-parens) and
    leading punctuation (opening parens) and it'll still do the right thing.

    If trim_url_limit is not None, the URLs in link text longer than this limit
    will truncated to trim_url_limit-3 characters and appended with an elipsis.

    If nofollow is True, the URLs in link text will get a rel="nofollow"
    attribute.

    If autoescape is True, the link text and URLs will get autoescaped.
    """
    trim_url = lambda x, limit=trim_url_limit: limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x
    safe_input = isinstance(text, SafeData)
    words = word_split_re.split(force_text(text))
    for i, word in enumerate(words):
        if '.' in word or '@' in word or ':' in word:
            # Deal with punctuation.
            lead, middle, trail = '', word, ''
            for punctuation in TRAILING_PUNCTUATION:
                if middle.endswith(punctuation):
                    middle = middle[:-len(punctuation)]
                    trail = punctuation + trail
            for opening, closing in WRAPPING_PUNCTUATION:
                if middle.startswith(opening):
                    middle = middle[len(opening):]
                    lead = lead + opening
                # Keep parentheses at the end only if they're balanced.
                if (
                    middle.endswith(closing)
                    and middle.count(closing) == middle.count(opening) + 1
                ):
                    middle = middle[:-len(closing)]
                    trail = closing + trail

            # Make URL we want to point to.
            url = None
            nofollow_attr = ' rel="nofollow"' if nofollow else ''
            if simple_url_re.match(middle):
                url = smart_urlquote_wrapper(middle)
            elif simple_url_2_re.match(middle):
                url = smart_urlquote_wrapper('http://%s' % middle)
            elif ':' not in middle and simple_email_re.match(middle):
                local, domain = middle.rsplit('@', 1)
                try:
                    domain = domain.encode('idna').decode('ascii')
                except UnicodeError:
                    continue
                url = 'mailto:%s@%s' % (local, domain)
                nofollow_attr = ''

            # Make link.
            if url:
                trimmed = trim_url(middle)
                if autoescape and not safe_input:
                    lead, trail = escape(lead), escape(trail)
                    url, trimmed = escape(url), escape(trimmed)
                middle = '<a href="%s"%s>%s</a>' % (url, nofollow_attr, trimmed)
                words[i] = mark_safe('%s%s%s' % (lead, middle, trail))
            else:
                if safe_input:
                    words[i] = mark_safe(word)
                elif autoescape:
                    words[i] = escape(word)
        elif safe_input:
            words[i] = mark_safe(word)
        elif autoescape:
            words[i] = escape(word)
    return ''.join(words)
def urlize_quoted_links(text,
                        trim_url_limit=None,
                        nofollow=True,
                        autoescape=True):
    """
    Converts any URLs in text into clickable links.

    Works on http://, https://, www. links, and also on links ending in one of
    the original seven gTLDs (.com, .edu, .gov, .int, .mil, .net, and .org).
    Links can have trailing punctuation (periods, commas, close-parens) and
    leading punctuation (opening parens) and it'll still do the right thing.

    If trim_url_limit is not None, the URLs in link text longer than this limit
    will truncated to trim_url_limit-3 characters and appended with an elipsis.

    If nofollow is True, the URLs in link text will get a rel="nofollow"
    attribute.

    If autoescape is True, the link text and URLs will get autoescaped.
    """
    trim_url = lambda x, limit=trim_url_limit: limit is not None and (len(
        x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x
    safe_input = isinstance(text, SafeData)
    words = word_split_re.split(force_text(text))
    for i, word in enumerate(words):
        if '.' in word or '@' in word or ':' in word:
            # Deal with punctuation.
            lead, middle, trail = '', word, ''
            for punctuation in TRAILING_PUNCTUATION:
                if middle.endswith(punctuation):
                    middle = middle[:-len(punctuation)]
                    trail = punctuation + trail
            for opening, closing in WRAPPING_PUNCTUATION:
                if middle.startswith(opening):
                    middle = middle[len(opening):]
                    lead = lead + opening
                # Keep parentheses at the end only if they're balanced.
                if (middle.endswith(closing) and middle.count(closing)
                        == middle.count(opening) + 1):
                    middle = middle[:-len(closing)]
                    trail = closing + trail

            # Make URL we want to point to.
            url = None
            nofollow_attr = ' rel="nofollow"' if nofollow else ''
            if simple_url_re.match(middle):
                url = smart_urlquote_wrapper(middle)
            elif simple_url_2_re.match(middle):
                url = smart_urlquote_wrapper('http://%s' % middle)
            elif not ':' in middle and simple_email_re.match(middle):
                local, domain = middle.rsplit('@', 1)
                try:
                    domain = domain.encode('idna').decode('ascii')
                except UnicodeError:
                    continue
                url = 'mailto:%s@%s' % (local, domain)
                nofollow_attr = ''

            # Make link.
            if url:
                trimmed = trim_url(middle)
                if autoescape and not safe_input:
                    lead, trail = escape(lead), escape(trail)
                    url, trimmed = escape(url), escape(trimmed)
                middle = '<a href="%s"%s>%s</a>' % (url, nofollow_attr,
                                                    trimmed)
                words[i] = mark_safe('%s%s%s' % (lead, middle, trail))
            else:
                if safe_input:
                    words[i] = mark_safe(word)
                elif autoescape:
                    words[i] = escape(word)
        elif safe_input:
            words[i] = mark_safe(word)
        elif autoescape:
            words[i] = escape(word)
    return ''.join(words)