diff --git a/rhodecode/lib/helpers.py b/rhodecode/lib/helpers.py --- a/rhodecode/lib/helpers.py +++ b/rhodecode/lib/helpers.py @@ -29,15 +29,16 @@ import collections import os import random import hashlib -from io import StringIO +import io import textwrap -import urllib.request, urllib.parse, urllib.error +import urllib.request +import urllib.parse +import urllib.error import math import logging import re import time import string -import hashlib import regex from collections import OrderedDict @@ -70,19 +71,22 @@ from webhelpers2.html.tags import ( form as insecure_form, auto_discovery_link, checkbox, end_form, file, hidden, image, javascript_link, link_to, link_to_if, link_to_unless, ol, - select as raw_select, stylesheet_link, submit, text, password, textarea, + stylesheet_link, submit, text, password, textarea, ul, radio, Options) from webhelpers2.number import format_byte_size +# python3.11 backport fixes for webhelpers2 +from rhodecode.lib._vendor.webhelpers_backports import raw_select from rhodecode.lib.action_parser import action_parser from rhodecode.lib.pagination import Page, RepoPage, SqlPage from rhodecode.lib import ext_json from rhodecode.lib.ext_json import json -from rhodecode.lib.str_utils import safe_bytes +from rhodecode.lib.str_utils import safe_bytes, convert_special_chars from rhodecode.lib.utils import repo_name_slug, get_custom_lexer +from rhodecode.lib.str_utils import safe_str from rhodecode.lib.utils2 import ( - str2bool, safe_unicode, safe_str, + str2bool, get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime, AttributeDict, safe_int, md5, md5_safe, get_host_info) from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links @@ -123,11 +127,11 @@ def asset(path, ver=None, **kwargs): default_html_escape_table = { - ord('&'): u'&', - ord('<'): u'<', - ord('>'): u'>', - ord('"'): u'"', - ord("'"): u''', + ord('&'): '&', + ord('<'): '<', + ord('>'): '>', + ord('"'): '"', + ord("'"): ''', } @@ -265,14 +269,12 @@ class _ToolTip(object): tooltip = _ToolTip() -files_icon = u'' +files_icon = '' def files_breadcrumbs(repo_name, repo_type, commit_id, file_path, landing_ref_name=None, at_ref=None, limit_items=False, linkify_last_item=False, hide_last_item=False, copy_path_icon=True): - if isinstance(file_path, str): - file_path = safe_unicode(file_path) if at_ref: route_qry = {'at': at_ref} @@ -282,7 +284,7 @@ def files_breadcrumbs(repo_name, repo_ty default_landing_ref = commit_id # first segment is a `HOME` link to repo files root location - root_name = literal(u'') + root_name = literal('') url_segments = [ link_to( @@ -345,7 +347,6 @@ def files_breadcrumbs(repo_name, repo_ty def files_url_data(request): - import urllib.request, urllib.parse, urllib.error matchdict = request.matchdict if 'f_path' not in matchdict: @@ -426,17 +427,17 @@ class CodeHtmlFormatter(HtmlFormatter): My code Html Formatter for source codes """ - def wrap(self, source, outfile): + def wrap(self, source): return self._wrap_div(self._wrap_pre(self._wrap_code(source))) def _wrap_code(self, source): for cnt, it in enumerate(source): i, t = it - t = '
%s
' % (cnt + 1, t) + t = f'
{t}
' yield i, t def _wrap_tablelinenos(self, inner): - dummyoutfile = StringIO.StringIO() + dummyoutfile = io.StringIO() lncount = 0 for t, line in inner: if t: @@ -594,7 +595,7 @@ def unique_color_generator(n=10000, satu h %= 1 HSV_tuple = [h, saturation, lightness] RGB_tuple = hsv_to_rgb(*HSV_tuple) - yield map(lambda x: str(int(x * 256)), RGB_tuple) + yield [str(int(x * 256)) for x in RGB_tuple] def color_hasher(n=10000, saturation=0.10, lightness=0.95): @@ -692,7 +693,7 @@ class _Message(object): __unicode__ = __str__ def __html__(self): - return escape(safe_unicode(self.message)) + return escape(safe_str(self.message)) class Flash(object): @@ -771,7 +772,7 @@ class Flash(object): for message in messages: payloads.append({ 'message': { - 'message': u'{}'.format(message.message), + 'message': '{}'.format(message.message), 'level': message.category, 'force': True, 'subdata': message.sub_data @@ -816,8 +817,7 @@ def hide_credentials(url): from rhodecode.lib.utils2 import credentials_filter return credentials_filter(url) - -import pytz +import zoneinfo import tzlocal local_timezone = tzlocal.get_localzone() @@ -829,9 +829,10 @@ def get_timezone(datetime_iso, time_is_l if time_is_local and isinstance(datetime_iso, datetime) and not datetime_iso.tzinfo: force_timezone = os.environ.get('RC_TIMEZONE', '') if force_timezone: - force_timezone = pytz.timezone(force_timezone) + force_timezone = zoneinfo.ZoneInfo(force_timezone) timezone = force_timezone or local_timezone - offset = timezone.localize(datetime_iso).strftime('%z') + + offset = datetime_iso.replace(tzinfo=timezone).strftime('%z') tzinfo = '{}:{}'.format(offset[:-2], offset[-2:]) return tzinfo @@ -883,9 +884,9 @@ def format_date(date): if date: _fmt = "%a, %d %b %Y %H:%M:%S" - return safe_unicode(date.strftime(_fmt)) - - return u"" + return safe_str(date.strftime(_fmt)) + + return "" class _RepoChecker(object): @@ -1021,7 +1022,8 @@ def author_string(email): def person_by_id(id_, show_attr="username_and_name"): # attr to return from fetched user - person_getter = lambda usr: getattr(usr, show_attr) + def person_getter(usr): + return getattr(usr, show_attr) #maybe it's an ID ? if str(id_).isdigit() or isinstance(id_, int): @@ -1074,7 +1076,7 @@ def extract_metatags(value): if not value: return tags, '' - for key, val in tags_paterns.items(): + for key, val in list(tags_paterns.items()): pat, replace_html = val tags.extend([(key, x.group()) for x in pat.finditer(value)]) value = pat.sub('', value) @@ -1093,8 +1095,8 @@ def style_metatag(tag_type, value): tag_data = tags_paterns.get(tag_type) if tag_data: pat, replace_html = tag_data - # convert to plain `unicode` instead of a markup tag to be used in - # regex expressions. safe_unicode doesn't work here + # convert to plain `str` instead of a markup tag to be used in + # regex expressions. safe_str doesn't work here html_value = pat.sub(replace_html, value) return html_value @@ -1117,7 +1119,7 @@ def bool2icon(value, show_at_false=True) def b64(inp): - return base64.b64encode(inp) + return base64.b64encode(safe_bytes(inp)) #============================================================================== # PERMS @@ -1230,23 +1232,22 @@ class InitialsGravatar(object): return color_bank[pos] def normalize_email(self, email_address): - import unicodedata # default host used to fill in the fake/missing email default_host = 'localhost' if not email_address: - email_address = '%s@%s' % (User.DEFAULT_USER, default_host) - - email_address = safe_unicode(email_address) - - if u'@' not in email_address: - email_address = u'%s@%s' % (email_address, default_host) - - if email_address.endswith(u'@'): - email_address = u'%s%s' % (email_address, default_host) - - email_address = unicodedata.normalize('NFKD', email_address)\ - .encode('ascii', 'ignore') + email_address = f'{User.DEFAULT_USER}@{default_host}' + + email_address = safe_str(email_address) + + if '@' not in email_address: + email_address = f'{email_address}@{default_host}' + + if email_address.endswith('@'): + email_address = f'{email_address}{default_host}' + + email_address = convert_special_chars(email_address) + return email_address def get_initials(self): @@ -1266,12 +1267,11 @@ class InitialsGravatar(object): Function also normalizes the non-ascii characters to they ascii representation, eg Ą => A """ - import unicodedata # replace non-ascii to ascii - first_name = unicodedata.normalize( - 'NFKD', safe_unicode(self.first_name)).encode('ascii', 'ignore') - last_name = unicodedata.normalize( - 'NFKD', safe_unicode(self.last_name)).encode('ascii', 'ignore') + first_name = convert_special_chars(self.first_name) + last_name = convert_special_chars(self.last_name) + # multi word last names, Guido Von Rossum, we take the last part only + last_name = last_name.split(' ', 1)[-1] # do NFKD encoding, and also make sure email has proper format email_address = self.normalize_email(self.email_address) @@ -1286,9 +1286,9 @@ class InitialsGravatar(object): else: initials = [prefix[0], server[0]] - # then try to replace either first_name or last_name + # get first letter of first and last names to create initials fn_letter = (first_name or " ")[0].strip() - ln_letter = (last_name.split(' ', 1)[-1] or " ")[0].strip() + ln_letter = (last_name or " ")[0].strip() if fn_letter: initials[0] = fn_letter @@ -1305,6 +1305,7 @@ class InitialsGravatar(object): viewBox="-15 -10 439.165 429.164" xml:space="preserve" + font-family="{font_family} style="background:{background};" >