Files
@ 3f8cd215f5eb
Branch filter:
Location: kallithea/kallithea/lib/vcs/utils/helpers.py - annotation
3f8cd215f5eb
7.6 KiB
text/x-python
hooks: don't pass arguments to log_pull_action when calling from git - it uses set_hook_environment
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | d1addaf7a91e dabdc356393b d1addaf7a91e d1addaf7a91e 0a277465fddf 9685f50a69d0 0a277465fddf d1addaf7a91e d1addaf7a91e d1521d421610 d1521d421610 d1521d421610 9685f50a69d0 9685f50a69d0 9685f50a69d0 d1addaf7a91e f3fab7b124f2 0a277465fddf d727e81e0097 d1addaf7a91e d1addaf7a91e 0a277465fddf d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e 5b6174420e30 5b6174420e30 5b6174420e30 5b6174420e30 d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e f0e8d673f2a2 d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e 0f4402c519ff d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e 0f4402c519ff d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e f3fab7b124f2 d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e 141066b8a89a d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e c1ed9572b965 d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1addaf7a91e d1521d421610 d1521d421610 d1521d421610 d1521d421610 d1521d421610 d1521d421610 d727e81e0097 d727e81e0097 d727e81e0097 d727e81e0097 d727e81e0097 d727e81e0097 d727e81e0097 d1521d421610 d1521d421610 d727e81e0097 d727e81e0097 d727e81e0097 d727e81e0097 d727e81e0097 d727e81e0097 d1521d421610 d1521d421610 d1521d421610 d1521d421610 d1521d421610 | """
Utilities aimed to help achieve mostly basic tasks.
"""
import datetime
import logging
import os
import re
import time
import urllib.request
import mercurial.url
from pygments import highlight
from pygments.formatters import TerminalFormatter
from pygments.lexers import ClassNotFound, guess_lexer_for_filename
from kallithea.lib.vcs import backends
from kallithea.lib.vcs.exceptions import RepositoryError, VCSError
from kallithea.lib.vcs.utils import safe_str
from kallithea.lib.vcs.utils.paths import abspath
ALIASES = ['hg', 'git']
def get_scm(path, search_up=False, explicit_alias=None):
"""
Returns one of alias from ``ALIASES`` (in order of precedence same as
shortcuts given in ``ALIASES``) and top working dir path for the given
argument. If no scm-specific directory is found or more than one scm is
found at that directory, ``VCSError`` is raised.
:param search_up: if set to ``True``, this function would try to
move up to parent directory every time no scm is recognized for the
currently checked path. Default: ``False``.
:param explicit_alias: can be one of available backend aliases, when given
it will return given explicit alias in repositories under more than one
version control, if explicit_alias is different than found it will raise
VCSError
"""
if not os.path.isdir(path):
raise VCSError("Given path %s is not a directory" % path)
while True:
found_scms = [(scm, path) for scm in get_scms_for_path(path)]
if found_scms or not search_up:
break
newpath = abspath(path, '..')
if newpath == path:
break
path = newpath
if len(found_scms) > 1:
for scm in found_scms:
if scm[0] == explicit_alias:
return scm
raise VCSError('More than one [%s] scm found at given path %s'
% (', '.join((x[0] for x in found_scms)), path))
if len(found_scms) == 0:
raise VCSError('No scm found at given path %s' % path)
return found_scms[0]
def get_scms_for_path(path):
"""
Returns all scm's found at the given path. If no scm is recognized
- empty list is returned.
:param path: path to directory which should be checked. May be callable.
:raises VCSError: if given ``path`` is not a directory
"""
if hasattr(path, '__call__'):
path = path()
if not os.path.isdir(path):
raise VCSError("Given path %r is not a directory" % path)
result = []
for key in ALIASES:
# find .hg / .git
dirname = os.path.join(path, '.' + key)
if os.path.isdir(dirname):
result.append(key)
continue
# find rm__.hg / rm__.git too - left overs from old method for deleting
dirname = os.path.join(path, 'rm__.' + key)
if os.path.isdir(dirname):
return result
# We still need to check if it's not bare repository as
# bare repos don't have working directories
try:
backends.get_backend(key)(path)
result.append(key)
continue
except RepositoryError:
# Wrong backend
pass
except VCSError:
# No backend at all
pass
return result
def get_highlighted_code(name, code, type='terminal'):
"""
If pygments are available on the system
then returned output is colored. Otherwise
unchanged content is returned.
"""
try:
lexer = guess_lexer_for_filename(name, code)
formatter = TerminalFormatter()
content = highlight(code, lexer, formatter)
except ClassNotFound:
logging.debug("Couldn't guess Lexer, will not use pygments.")
content = code
return content
def parse_changesets(text):
"""
Returns dictionary with *start*, *main* and *end* ids.
Examples::
>>> parse_changesets('aaabbb')
{'start': None, 'main': 'aaabbb', 'end': None}
>>> parse_changesets('aaabbb..cccddd')
{'start': 'aaabbb', 'end': 'cccddd', 'main': None}
"""
text = text.strip()
CID_RE = r'[a-zA-Z0-9]+'
if '..' not in text:
m = re.match(r'^(?P<cid>%s)$' % CID_RE, text)
if m:
return {
'start': None,
'main': text,
'end': None,
}
else:
RE = r'^(?P<start>%s)?\.{2,3}(?P<end>%s)?$' % (CID_RE, CID_RE)
m = re.match(RE, text)
if m:
result = m.groupdict()
result['main'] = None
return result
raise ValueError("IDs not recognized")
def parse_datetime(text):
"""
Parses given text and returns ``datetime.datetime`` instance or raises
``ValueError``.
:param text: string of desired date/datetime or something more verbose,
like *yesterday*, *2weeks 3days*, etc.
"""
text = text.strip().lower()
INPUT_FORMATS = (
'%Y-%m-%d %H:%M:%S',
'%Y-%m-%d %H:%M',
'%Y-%m-%d',
'%m/%d/%Y %H:%M:%S',
'%m/%d/%Y %H:%M',
'%m/%d/%Y',
'%m/%d/%y %H:%M:%S',
'%m/%d/%y %H:%M',
'%m/%d/%y',
)
for format in INPUT_FORMATS:
try:
return datetime.datetime(*time.strptime(text, format)[:6])
except ValueError:
pass
# Try descriptive texts
if text == 'tomorrow':
future = datetime.datetime.now() + datetime.timedelta(days=1)
args = future.timetuple()[:3] + (23, 59, 59)
return datetime.datetime(*args)
elif text == 'today':
return datetime.datetime(*datetime.datetime.today().timetuple()[:3])
elif text == 'now':
return datetime.datetime.now()
elif text == 'yesterday':
past = datetime.datetime.now() - datetime.timedelta(days=1)
return datetime.datetime(*past.timetuple()[:3])
else:
days = 0
matched = re.match(
r'^((?P<weeks>\d+) ?w(eeks?)?)? ?((?P<days>\d+) ?d(ays?)?)?$', text)
if matched:
groupdict = matched.groupdict()
if groupdict['days']:
days += int(matched.groupdict()['days'])
if groupdict['weeks']:
days += int(matched.groupdict()['weeks']) * 7
past = datetime.datetime.now() - datetime.timedelta(days=days)
return datetime.datetime(*past.timetuple()[:3])
raise ValueError('Wrong date: "%s"' % text)
def get_dict_for_attrs(obj, attrs):
"""
Returns dictionary for each attribute from given ``obj``.
"""
data = {}
for attr in attrs:
data[attr] = getattr(obj, attr)
return data
def get_urllib_request_handlers(url_obj):
handlers = []
test_uri, authinfo = url_obj.authinfo()
if authinfo:
# authinfo is a tuple (realm, uris, user, password) where 'uris' itself
# is a tuple of URIs.
# If url_obj is obtained via mercurial.util.url, the obtained authinfo
# values will be bytes, e.g.
# (None, (b'http://127.0.0.1/repo', b'127.0.0.1'), b'user', b'pass')
# However, urllib expects strings, not bytes, so we must convert them.
# create a password manager
passmgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
passmgr.add_password(
safe_str(authinfo[0]) if authinfo[0] else None, # realm
tuple(safe_str(x) for x in authinfo[1]), # uris
safe_str(authinfo[2]), # user
safe_str(authinfo[3]), # password
)
handlers.extend((mercurial.url.httpbasicauthhandler(passmgr),
mercurial.url.httpdigestauthhandler(passmgr)))
return test_uri, handlers
|