##// END OF EJS Templates
fix unreachable code errors
marcink -
r3885:712610e0 beta
parent child Browse files
Show More
@@ -1,171 +1,170 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.bin.gist
3 rhodecode.bin.gist
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Gist CLI client for RhodeCode
6 Gist CLI client for RhodeCode
7
7
8 :created_on: May 9, 2013
8 :created_on: May 9, 2013
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2013 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2013 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 from __future__ import with_statement
26 from __future__ import with_statement
27 import os
27 import os
28 import sys
28 import sys
29 import stat
29 import stat
30 import argparse
30 import argparse
31 import fileinput
31 import fileinput
32
32
33 from rhodecode.bin.base import json, api_call, RcConf, FORMAT_JSON, FORMAT_PRETTY
33 from rhodecode.bin.base import json, api_call, RcConf, FORMAT_JSON, FORMAT_PRETTY
34
34
35
35
36 def argparser(argv):
36 def argparser(argv):
37 usage = (
37 usage = (
38 "rhodecode-gist [-h] [--format=FORMAT] [--apikey=APIKEY] [--apihost=APIHOST] "
38 "rhodecode-gist [-h] [--format=FORMAT] [--apikey=APIKEY] [--apihost=APIHOST] "
39 "[--config=CONFIG] [--save-config] [GIST OPTIONS] "
39 "[--config=CONFIG] [--save-config] [GIST OPTIONS] "
40 "[filename or stdin use - for terminal stdin ]\n"
40 "[filename or stdin use - for terminal stdin ]\n"
41 "Create config file: rhodecode-gist --apikey=<key> --apihost=http://rhodecode.server --save-config"
41 "Create config file: rhodecode-gist --apikey=<key> --apihost=http://rhodecode.server --save-config"
42 )
42 )
43
43
44 parser = argparse.ArgumentParser(description='RhodeCode Gist cli',
44 parser = argparse.ArgumentParser(description='RhodeCode Gist cli',
45 usage=usage)
45 usage=usage)
46
46
47 ## config
47 ## config
48 group = parser.add_argument_group('config')
48 group = parser.add_argument_group('config')
49 group.add_argument('--apikey', help='api access key')
49 group.add_argument('--apikey', help='api access key')
50 group.add_argument('--apihost', help='api host')
50 group.add_argument('--apihost', help='api host')
51 group.add_argument('--config', help='config file path DEFAULT: ~/.rhodecode')
51 group.add_argument('--config', help='config file path DEFAULT: ~/.rhodecode')
52 group.add_argument('--save-config', action='store_true',
52 group.add_argument('--save-config', action='store_true',
53 help='save the given config into a file')
53 help='save the given config into a file')
54
54
55 group = parser.add_argument_group('GIST')
55 group = parser.add_argument_group('GIST')
56 group.add_argument('-p', '--private', action='store_true',
56 group.add_argument('-p', '--private', action='store_true',
57 help='create private Gist')
57 help='create private Gist')
58 group.add_argument('-f', '--filename',
58 group.add_argument('-f', '--filename',
59 help='set uploaded gist filename, '
59 help='set uploaded gist filename, '
60 'also defines syntax highlighting')
60 'also defines syntax highlighting')
61 group.add_argument('-d', '--description', help='Gist description')
61 group.add_argument('-d', '--description', help='Gist description')
62 group.add_argument('-l', '--lifetime', metavar='MINUTES',
62 group.add_argument('-l', '--lifetime', metavar='MINUTES',
63 help='gist lifetime in minutes, -1 (DEFAULT) is forever')
63 help='gist lifetime in minutes, -1 (DEFAULT) is forever')
64 group.add_argument('--format', dest='format', type=str,
64 group.add_argument('--format', dest='format', type=str,
65 help='output format DEFAULT: `%s` can '
65 help='output format DEFAULT: `%s` can '
66 'be also `%s`' % (FORMAT_PRETTY, FORMAT_JSON),
66 'be also `%s`' % (FORMAT_PRETTY, FORMAT_JSON),
67 default=FORMAT_PRETTY
67 default=FORMAT_PRETTY
68 )
68 )
69 args, other = parser.parse_known_args()
69 args, other = parser.parse_known_args()
70 return parser, args, other
70 return parser, args, other
71
71
72
72
73 def _run(argv):
73 def _run(argv):
74 conf = None
74 conf = None
75 parser, args, other = argparser(argv)
75 parser, args, other = argparser(argv)
76
76
77 api_credentials_given = (args.apikey and args.apihost)
77 api_credentials_given = (args.apikey and args.apihost)
78 if args.save_config:
78 if args.save_config:
79 if not api_credentials_given:
79 if not api_credentials_given:
80 raise parser.error('--save-config requires --apikey and --apihost')
80 raise parser.error('--save-config requires --apikey and --apihost')
81 conf = RcConf(config_location=args.config,
81 conf = RcConf(config_location=args.config,
82 autocreate=True, config={'apikey': args.apikey,
82 autocreate=True, config={'apikey': args.apikey,
83 'apihost': args.apihost})
83 'apihost': args.apihost})
84 sys.exit()
84 sys.exit()
85
85
86 if not conf:
86 if not conf:
87 conf = RcConf(config_location=args.config, autoload=True)
87 conf = RcConf(config_location=args.config, autoload=True)
88 if not conf:
88 if not conf:
89 if not api_credentials_given:
89 if not api_credentials_given:
90 parser.error('Could not find config file and missing '
90 parser.error('Could not find config file and missing '
91 '--apikey or --apihost in params')
91 '--apikey or --apihost in params')
92
92
93 apikey = args.apikey or conf['apikey']
93 apikey = args.apikey or conf['apikey']
94 host = args.apihost or conf['apihost']
94 host = args.apihost or conf['apihost']
95 DEFAULT_FILENAME = 'gistfile1.txt'
95 DEFAULT_FILENAME = 'gistfile1.txt'
96 if other:
96 if other:
97 # skip multifiles for now
97 # skip multifiles for now
98 filename = other[0]
98 filename = other[0]
99 if filename == '-':
99 if filename == '-':
100 filename = DEFAULT_FILENAME
100 filename = DEFAULT_FILENAME
101 gist_content = ''
101 gist_content = ''
102 for line in fileinput.input('-'):
102 for line in fileinput.input('-'):
103 gist_content += line
103 gist_content += line
104 else:
104 else:
105 with open(filename, 'rb') as f:
105 with open(filename, 'rb') as f:
106 gist_content = f.read()
106 gist_content = f.read()
107
107
108 else:
108 else:
109 filename = DEFAULT_FILENAME
109 filename = DEFAULT_FILENAME
110 gist_content = None
110 gist_content = None
111 # little bit hacky but cross platform check where the
111 # little bit hacky but cross platform check where the
112 # stdin comes from we skip the terminal case it can be handled by '-'
112 # stdin comes from we skip the terminal case it can be handled by '-'
113 mode = os.fstat(0).st_mode
113 mode = os.fstat(0).st_mode
114 if stat.S_ISFIFO(mode):
114 if stat.S_ISFIFO(mode):
115 # "stdin is piped"
115 # "stdin is piped"
116 gist_content = sys.stdin.read()
116 gist_content = sys.stdin.read()
117 elif stat.S_ISREG(mode):
117 elif stat.S_ISREG(mode):
118 # "stdin is redirected"
118 # "stdin is redirected"
119 gist_content = sys.stdin.read()
119 gist_content = sys.stdin.read()
120 else:
120 else:
121 # "stdin is terminal"
121 # "stdin is terminal"
122 pass
122 pass
123
123
124 # make sure we don't upload binary stuff
124 # make sure we don't upload binary stuff
125 if gist_content and '\0' in gist_content:
125 if gist_content and '\0' in gist_content:
126 raise Exception('Error: binary files upload is not possible')
126 raise Exception('Error: binary files upload is not possible')
127
127
128 filename = os.path.basename(args.filename or filename)
128 filename = os.path.basename(args.filename or filename)
129 if gist_content:
129 if gist_content:
130 files = {
130 files = {
131 filename: {
131 filename: {
132 'content': gist_content,
132 'content': gist_content,
133 'lexer': None
133 'lexer': None
134 }
134 }
135 }
135 }
136
136
137 margs = dict(
137 margs = dict(
138 gist_lifetime=args.lifetime,
138 gist_lifetime=args.lifetime,
139 gist_description=args.description,
139 gist_description=args.description,
140 gist_type='private' if args.private else 'public',
140 gist_type='private' if args.private else 'public',
141 files=files
141 files=files
142 )
142 )
143
143
144 json_data = api_call(apikey, host, 'create_gist', **margs)['result']
144 json_data = api_call(apikey, host, 'create_gist', **margs)['result']
145 if args.format == FORMAT_JSON:
145 if args.format == FORMAT_JSON:
146 print json.dumps(json_data)
146 print json.dumps(json_data)
147 elif args.format == FORMAT_PRETTY:
147 elif args.format == FORMAT_PRETTY:
148 print 'Created %s gist %s' % (json_data['gist_type'],
148 print 'Created %s gist %s' % (json_data['gist_type'],
149 json_data['gist_url'])
149 json_data['gist_url'])
150 return 0
150 return 0
151
151
152
152
153 def main(argv=None):
153 def main(argv=None):
154 """
154 """
155 Main execution function for cli
155 Main execution function for cli
156
156
157 :param argv:
157 :param argv:
158 """
158 """
159 if argv is None:
159 if argv is None:
160 argv = sys.argv
160 argv = sys.argv
161
161
162 try:
162 try:
163 return _run(argv)
163 return _run(argv)
164 except Exception, e:
164 except Exception, e:
165 raise
166 print e
165 print e
167 return 1
166 return 1
168
167
169
168
170 if __name__ == '__main__':
169 if __name__ == '__main__':
171 sys.exit(main(sys.argv))
170 sys.exit(main(sys.argv))
@@ -1,646 +1,644 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Some simple helper functions
6 Some simple helper functions
7
7
8 :created_on: Jan 5, 2011
8 :created_on: Jan 5, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import re
27 import re
28 import sys
28 import sys
29 import time
29 import time
30 import uuid
30 import uuid
31 import datetime
31 import datetime
32 import traceback
32 import traceback
33 import webob
33 import webob
34
34
35 from pylons.i18n.translation import _, ungettext
35 from pylons.i18n.translation import _, ungettext
36 from rhodecode.lib.vcs.utils.lazy import LazyProperty
36 from rhodecode.lib.vcs.utils.lazy import LazyProperty
37 from rhodecode.lib.compat import json
37 from rhodecode.lib.compat import json
38
38
39
39
40 def __get_lem():
40 def __get_lem():
41 """
41 """
42 Get language extension map based on what's inside pygments lexers
42 Get language extension map based on what's inside pygments lexers
43 """
43 """
44 from pygments import lexers
44 from pygments import lexers
45 from string import lower
45 from string import lower
46 from collections import defaultdict
46 from collections import defaultdict
47
47
48 d = defaultdict(lambda: [])
48 d = defaultdict(lambda: [])
49
49
50 def __clean(s):
50 def __clean(s):
51 s = s.lstrip('*')
51 s = s.lstrip('*')
52 s = s.lstrip('.')
52 s = s.lstrip('.')
53
53
54 if s.find('[') != -1:
54 if s.find('[') != -1:
55 exts = []
55 exts = []
56 start, stop = s.find('['), s.find(']')
56 start, stop = s.find('['), s.find(']')
57
57
58 for suffix in s[start + 1:stop]:
58 for suffix in s[start + 1:stop]:
59 exts.append(s[:s.find('[')] + suffix)
59 exts.append(s[:s.find('[')] + suffix)
60 return map(lower, exts)
60 return map(lower, exts)
61 else:
61 else:
62 return map(lower, [s])
62 return map(lower, [s])
63
63
64 for lx, t in sorted(lexers.LEXERS.items()):
64 for lx, t in sorted(lexers.LEXERS.items()):
65 m = map(__clean, t[-2])
65 m = map(__clean, t[-2])
66 if m:
66 if m:
67 m = reduce(lambda x, y: x + y, m)
67 m = reduce(lambda x, y: x + y, m)
68 for ext in m:
68 for ext in m:
69 desc = lx.replace('Lexer', '')
69 desc = lx.replace('Lexer', '')
70 d[ext].append(desc)
70 d[ext].append(desc)
71
71
72 return dict(d)
72 return dict(d)
73
73
74
74
75 def str2bool(_str):
75 def str2bool(_str):
76 """
76 """
77 returs True/False value from given string, it tries to translate the
77 returs True/False value from given string, it tries to translate the
78 string into boolean
78 string into boolean
79
79
80 :param _str: string value to translate into boolean
80 :param _str: string value to translate into boolean
81 :rtype: boolean
81 :rtype: boolean
82 :returns: boolean from given string
82 :returns: boolean from given string
83 """
83 """
84 if _str is None:
84 if _str is None:
85 return False
85 return False
86 if _str in (True, False):
86 if _str in (True, False):
87 return _str
87 return _str
88 _str = str(_str).strip().lower()
88 _str = str(_str).strip().lower()
89 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
89 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
90
90
91
91
92 def aslist(obj, sep=None, strip=True):
92 def aslist(obj, sep=None, strip=True):
93 """
93 """
94 Returns given string separated by sep as list
94 Returns given string separated by sep as list
95
95
96 :param obj:
96 :param obj:
97 :param sep:
97 :param sep:
98 :param strip:
98 :param strip:
99 """
99 """
100 if isinstance(obj, (basestring)):
100 if isinstance(obj, (basestring)):
101 lst = obj.split(sep)
101 lst = obj.split(sep)
102 if strip:
102 if strip:
103 lst = [v.strip() for v in lst]
103 lst = [v.strip() for v in lst]
104 return lst
104 return lst
105 elif isinstance(obj, (list, tuple)):
105 elif isinstance(obj, (list, tuple)):
106 return obj
106 return obj
107 elif obj is None:
107 elif obj is None:
108 return []
108 return []
109 else:
109 else:
110 return [obj]
110 return [obj]
111
111
112
112
113 def convert_line_endings(line, mode):
113 def convert_line_endings(line, mode):
114 """
114 """
115 Converts a given line "line end" accordingly to given mode
115 Converts a given line "line end" accordingly to given mode
116
116
117 Available modes are::
117 Available modes are::
118 0 - Unix
118 0 - Unix
119 1 - Mac
119 1 - Mac
120 2 - DOS
120 2 - DOS
121
121
122 :param line: given line to convert
122 :param line: given line to convert
123 :param mode: mode to convert to
123 :param mode: mode to convert to
124 :rtype: str
124 :rtype: str
125 :return: converted line according to mode
125 :return: converted line according to mode
126 """
126 """
127 from string import replace
127 from string import replace
128
128
129 if mode == 0:
129 if mode == 0:
130 line = replace(line, '\r\n', '\n')
130 line = replace(line, '\r\n', '\n')
131 line = replace(line, '\r', '\n')
131 line = replace(line, '\r', '\n')
132 elif mode == 1:
132 elif mode == 1:
133 line = replace(line, '\r\n', '\r')
133 line = replace(line, '\r\n', '\r')
134 line = replace(line, '\n', '\r')
134 line = replace(line, '\n', '\r')
135 elif mode == 2:
135 elif mode == 2:
136 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
136 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
137 return line
137 return line
138
138
139
139
140 def detect_mode(line, default):
140 def detect_mode(line, default):
141 """
141 """
142 Detects line break for given line, if line break couldn't be found
142 Detects line break for given line, if line break couldn't be found
143 given default value is returned
143 given default value is returned
144
144
145 :param line: str line
145 :param line: str line
146 :param default: default
146 :param default: default
147 :rtype: int
147 :rtype: int
148 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
148 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
149 """
149 """
150 if line.endswith('\r\n'):
150 if line.endswith('\r\n'):
151 return 2
151 return 2
152 elif line.endswith('\n'):
152 elif line.endswith('\n'):
153 return 0
153 return 0
154 elif line.endswith('\r'):
154 elif line.endswith('\r'):
155 return 1
155 return 1
156 else:
156 else:
157 return default
157 return default
158
158
159
159
160 def generate_api_key(username, salt=None):
160 def generate_api_key(username, salt=None):
161 """
161 """
162 Generates unique API key for given username, if salt is not given
162 Generates unique API key for given username, if salt is not given
163 it'll be generated from some random string
163 it'll be generated from some random string
164
164
165 :param username: username as string
165 :param username: username as string
166 :param salt: salt to hash generate KEY
166 :param salt: salt to hash generate KEY
167 :rtype: str
167 :rtype: str
168 :returns: sha1 hash from username+salt
168 :returns: sha1 hash from username+salt
169 """
169 """
170 from tempfile import _RandomNameSequence
170 from tempfile import _RandomNameSequence
171 import hashlib
171 import hashlib
172
172
173 if salt is None:
173 if salt is None:
174 salt = _RandomNameSequence().next()
174 salt = _RandomNameSequence().next()
175
175
176 return hashlib.sha1(username + salt).hexdigest()
176 return hashlib.sha1(username + salt).hexdigest()
177
177
178
178
179 def safe_int(val, default=None):
179 def safe_int(val, default=None):
180 """
180 """
181 Returns int() of val if val is not convertable to int use default
181 Returns int() of val if val is not convertable to int use default
182 instead
182 instead
183
183
184 :param val:
184 :param val:
185 :param default:
185 :param default:
186 """
186 """
187
187
188 try:
188 try:
189 val = int(val)
189 val = int(val)
190 except (ValueError, TypeError):
190 except (ValueError, TypeError):
191 val = default
191 val = default
192
192
193 return val
193 return val
194
194
195
195
196 def safe_unicode(str_, from_encoding=None):
196 def safe_unicode(str_, from_encoding=None):
197 """
197 """
198 safe unicode function. Does few trick to turn str_ into unicode
198 safe unicode function. Does few trick to turn str_ into unicode
199
199
200 In case of UnicodeDecode error we try to return it with encoding detected
200 In case of UnicodeDecode error we try to return it with encoding detected
201 by chardet library if it fails fallback to unicode with errors replaced
201 by chardet library if it fails fallback to unicode with errors replaced
202
202
203 :param str_: string to decode
203 :param str_: string to decode
204 :rtype: unicode
204 :rtype: unicode
205 :returns: unicode object
205 :returns: unicode object
206 """
206 """
207 if isinstance(str_, unicode):
207 if isinstance(str_, unicode):
208 return str_
208 return str_
209
209
210 if not from_encoding:
210 if not from_encoding:
211 import rhodecode
211 import rhodecode
212 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
212 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
213 'utf8'), sep=',')
213 'utf8'), sep=',')
214 from_encoding = DEFAULT_ENCODINGS
214 from_encoding = DEFAULT_ENCODINGS
215
215
216 if not isinstance(from_encoding, (list, tuple)):
216 if not isinstance(from_encoding, (list, tuple)):
217 from_encoding = [from_encoding]
217 from_encoding = [from_encoding]
218
218
219 try:
219 try:
220 return unicode(str_)
220 return unicode(str_)
221 except UnicodeDecodeError:
221 except UnicodeDecodeError:
222 pass
222 pass
223
223
224 for enc in from_encoding:
224 for enc in from_encoding:
225 try:
225 try:
226 return unicode(str_, enc)
226 return unicode(str_, enc)
227 except UnicodeDecodeError:
227 except UnicodeDecodeError:
228 pass
228 pass
229
229
230 try:
230 try:
231 import chardet
231 import chardet
232 encoding = chardet.detect(str_)['encoding']
232 encoding = chardet.detect(str_)['encoding']
233 if encoding is None:
233 if encoding is None:
234 raise Exception()
234 raise Exception()
235 return str_.decode(encoding)
235 return str_.decode(encoding)
236 except (ImportError, UnicodeDecodeError, Exception):
236 except (ImportError, UnicodeDecodeError, Exception):
237 return unicode(str_, from_encoding[0], 'replace')
237 return unicode(str_, from_encoding[0], 'replace')
238
238
239
239
240 def safe_str(unicode_, to_encoding=None):
240 def safe_str(unicode_, to_encoding=None):
241 """
241 """
242 safe str function. Does few trick to turn unicode_ into string
242 safe str function. Does few trick to turn unicode_ into string
243
243
244 In case of UnicodeEncodeError we try to return it with encoding detected
244 In case of UnicodeEncodeError we try to return it with encoding detected
245 by chardet library if it fails fallback to string with errors replaced
245 by chardet library if it fails fallback to string with errors replaced
246
246
247 :param unicode_: unicode to encode
247 :param unicode_: unicode to encode
248 :rtype: str
248 :rtype: str
249 :returns: str object
249 :returns: str object
250 """
250 """
251
251
252 # if it's not basestr cast to str
252 # if it's not basestr cast to str
253 if not isinstance(unicode_, basestring):
253 if not isinstance(unicode_, basestring):
254 return str(unicode_)
254 return str(unicode_)
255
255
256 if isinstance(unicode_, str):
256 if isinstance(unicode_, str):
257 return unicode_
257 return unicode_
258
258
259 if not to_encoding:
259 if not to_encoding:
260 import rhodecode
260 import rhodecode
261 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
261 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
262 'utf8'), sep=',')
262 'utf8'), sep=',')
263 to_encoding = DEFAULT_ENCODINGS
263 to_encoding = DEFAULT_ENCODINGS
264
264
265 if not isinstance(to_encoding, (list, tuple)):
265 if not isinstance(to_encoding, (list, tuple)):
266 to_encoding = [to_encoding]
266 to_encoding = [to_encoding]
267
267
268 for enc in to_encoding:
268 for enc in to_encoding:
269 try:
269 try:
270 return unicode_.encode(enc)
270 return unicode_.encode(enc)
271 except UnicodeEncodeError:
271 except UnicodeEncodeError:
272 pass
272 pass
273
273
274 try:
274 try:
275 import chardet
275 import chardet
276 encoding = chardet.detect(unicode_)['encoding']
276 encoding = chardet.detect(unicode_)['encoding']
277 if encoding is None:
277 if encoding is None:
278 raise UnicodeEncodeError()
278 raise UnicodeEncodeError()
279
279
280 return unicode_.encode(encoding)
280 return unicode_.encode(encoding)
281 except (ImportError, UnicodeEncodeError):
281 except (ImportError, UnicodeEncodeError):
282 return unicode_.encode(to_encoding[0], 'replace')
282 return unicode_.encode(to_encoding[0], 'replace')
283
283
284 return safe_str
285
286
284
287 def remove_suffix(s, suffix):
285 def remove_suffix(s, suffix):
288 if s.endswith(suffix):
286 if s.endswith(suffix):
289 s = s[:-1 * len(suffix)]
287 s = s[:-1 * len(suffix)]
290 return s
288 return s
291
289
292
290
293 def remove_prefix(s, prefix):
291 def remove_prefix(s, prefix):
294 if s.startswith(prefix):
292 if s.startswith(prefix):
295 s = s[len(prefix):]
293 s = s[len(prefix):]
296 return s
294 return s
297
295
298
296
299 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
297 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
300 """
298 """
301 Custom engine_from_config functions that makes sure we use NullPool for
299 Custom engine_from_config functions that makes sure we use NullPool for
302 file based sqlite databases. This prevents errors on sqlite. This only
300 file based sqlite databases. This prevents errors on sqlite. This only
303 applies to sqlalchemy versions < 0.7.0
301 applies to sqlalchemy versions < 0.7.0
304
302
305 """
303 """
306 import sqlalchemy
304 import sqlalchemy
307 from sqlalchemy import engine_from_config as efc
305 from sqlalchemy import engine_from_config as efc
308 import logging
306 import logging
309
307
310 if int(sqlalchemy.__version__.split('.')[1]) < 7:
308 if int(sqlalchemy.__version__.split('.')[1]) < 7:
311
309
312 # This solution should work for sqlalchemy < 0.7.0, and should use
310 # This solution should work for sqlalchemy < 0.7.0, and should use
313 # proxy=TimerProxy() for execution time profiling
311 # proxy=TimerProxy() for execution time profiling
314
312
315 from sqlalchemy.pool import NullPool
313 from sqlalchemy.pool import NullPool
316 url = configuration[prefix + 'url']
314 url = configuration[prefix + 'url']
317
315
318 if url.startswith('sqlite'):
316 if url.startswith('sqlite'):
319 kwargs.update({'poolclass': NullPool})
317 kwargs.update({'poolclass': NullPool})
320 return efc(configuration, prefix, **kwargs)
318 return efc(configuration, prefix, **kwargs)
321 else:
319 else:
322 import time
320 import time
323 from sqlalchemy import event
321 from sqlalchemy import event
324 from sqlalchemy.engine import Engine
322 from sqlalchemy.engine import Engine
325
323
326 log = logging.getLogger('sqlalchemy.engine')
324 log = logging.getLogger('sqlalchemy.engine')
327 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
325 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
328 engine = efc(configuration, prefix, **kwargs)
326 engine = efc(configuration, prefix, **kwargs)
329
327
330 def color_sql(sql):
328 def color_sql(sql):
331 COLOR_SEQ = "\033[1;%dm"
329 COLOR_SEQ = "\033[1;%dm"
332 COLOR_SQL = YELLOW
330 COLOR_SQL = YELLOW
333 normal = '\x1b[0m'
331 normal = '\x1b[0m'
334 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
332 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
335
333
336 if configuration['debug']:
334 if configuration['debug']:
337 #attach events only for debug configuration
335 #attach events only for debug configuration
338
336
339 def before_cursor_execute(conn, cursor, statement,
337 def before_cursor_execute(conn, cursor, statement,
340 parameters, context, executemany):
338 parameters, context, executemany):
341 context._query_start_time = time.time()
339 context._query_start_time = time.time()
342 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
340 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
343
341
344 def after_cursor_execute(conn, cursor, statement,
342 def after_cursor_execute(conn, cursor, statement,
345 parameters, context, executemany):
343 parameters, context, executemany):
346 total = time.time() - context._query_start_time
344 total = time.time() - context._query_start_time
347 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
345 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
348
346
349 event.listen(engine, "before_cursor_execute",
347 event.listen(engine, "before_cursor_execute",
350 before_cursor_execute)
348 before_cursor_execute)
351 event.listen(engine, "after_cursor_execute",
349 event.listen(engine, "after_cursor_execute",
352 after_cursor_execute)
350 after_cursor_execute)
353
351
354 return engine
352 return engine
355
353
356
354
357 def age(prevdate, show_short_version=False, now=None):
355 def age(prevdate, show_short_version=False, now=None):
358 """
356 """
359 turns a datetime into an age string.
357 turns a datetime into an age string.
360 If show_short_version is True, then it will generate a not so accurate but shorter string,
358 If show_short_version is True, then it will generate a not so accurate but shorter string,
361 example: 2days ago, instead of 2 days and 23 hours ago.
359 example: 2days ago, instead of 2 days and 23 hours ago.
362
360
363 :param prevdate: datetime object
361 :param prevdate: datetime object
364 :param show_short_version: if it should aproximate the date and return a shorter string
362 :param show_short_version: if it should aproximate the date and return a shorter string
365 :rtype: unicode
363 :rtype: unicode
366 :returns: unicode words describing age
364 :returns: unicode words describing age
367 """
365 """
368 now = now or datetime.datetime.now()
366 now = now or datetime.datetime.now()
369 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
367 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
370 deltas = {}
368 deltas = {}
371 future = False
369 future = False
372
370
373 if prevdate > now:
371 if prevdate > now:
374 now, prevdate = prevdate, now
372 now, prevdate = prevdate, now
375 future = True
373 future = True
376 if future:
374 if future:
377 prevdate = prevdate.replace(microsecond=0)
375 prevdate = prevdate.replace(microsecond=0)
378 # Get date parts deltas
376 # Get date parts deltas
379 from dateutil import relativedelta
377 from dateutil import relativedelta
380 for part in order:
378 for part in order:
381 d = relativedelta.relativedelta(now, prevdate)
379 d = relativedelta.relativedelta(now, prevdate)
382 deltas[part] = getattr(d, part + 's')
380 deltas[part] = getattr(d, part + 's')
383
381
384 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
382 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
385 # not 1 hour, -59 minutes and -59 seconds)
383 # not 1 hour, -59 minutes and -59 seconds)
386 for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
384 for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
387 part = order[num]
385 part = order[num]
388 carry_part = order[num - 1]
386 carry_part = order[num - 1]
389
387
390 if deltas[part] < 0:
388 if deltas[part] < 0:
391 deltas[part] += length
389 deltas[part] += length
392 deltas[carry_part] -= 1
390 deltas[carry_part] -= 1
393
391
394 # Same thing for days except that the increment depends on the (variable)
392 # Same thing for days except that the increment depends on the (variable)
395 # number of days in the month
393 # number of days in the month
396 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
394 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
397 if deltas['day'] < 0:
395 if deltas['day'] < 0:
398 if prevdate.month == 2 and (prevdate.year % 4 == 0 and
396 if prevdate.month == 2 and (prevdate.year % 4 == 0 and
399 (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)):
397 (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)):
400 deltas['day'] += 29
398 deltas['day'] += 29
401 else:
399 else:
402 deltas['day'] += month_lengths[prevdate.month - 1]
400 deltas['day'] += month_lengths[prevdate.month - 1]
403
401
404 deltas['month'] -= 1
402 deltas['month'] -= 1
405
403
406 if deltas['month'] < 0:
404 if deltas['month'] < 0:
407 deltas['month'] += 12
405 deltas['month'] += 12
408 deltas['year'] -= 1
406 deltas['year'] -= 1
409
407
410 # Format the result
408 # Format the result
411 fmt_funcs = {
409 fmt_funcs = {
412 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
410 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
413 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
411 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
414 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
412 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
415 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
413 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
416 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
414 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
417 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
415 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
418 }
416 }
419
417
420 for i, part in enumerate(order):
418 for i, part in enumerate(order):
421 value = deltas[part]
419 value = deltas[part]
422 if value == 0:
420 if value == 0:
423 continue
421 continue
424
422
425 if i < 5:
423 if i < 5:
426 sub_part = order[i + 1]
424 sub_part = order[i + 1]
427 sub_value = deltas[sub_part]
425 sub_value = deltas[sub_part]
428 else:
426 else:
429 sub_value = 0
427 sub_value = 0
430
428
431 if sub_value == 0 or show_short_version:
429 if sub_value == 0 or show_short_version:
432 if future:
430 if future:
433 return _(u'in %s') % fmt_funcs[part](value)
431 return _(u'in %s') % fmt_funcs[part](value)
434 else:
432 else:
435 return _(u'%s ago') % fmt_funcs[part](value)
433 return _(u'%s ago') % fmt_funcs[part](value)
436 if future:
434 if future:
437 return _(u'in %s and %s') % (fmt_funcs[part](value),
435 return _(u'in %s and %s') % (fmt_funcs[part](value),
438 fmt_funcs[sub_part](sub_value))
436 fmt_funcs[sub_part](sub_value))
439 else:
437 else:
440 return _(u'%s and %s ago') % (fmt_funcs[part](value),
438 return _(u'%s and %s ago') % (fmt_funcs[part](value),
441 fmt_funcs[sub_part](sub_value))
439 fmt_funcs[sub_part](sub_value))
442
440
443 return _(u'just now')
441 return _(u'just now')
444
442
445
443
446 def uri_filter(uri):
444 def uri_filter(uri):
447 """
445 """
448 Removes user:password from given url string
446 Removes user:password from given url string
449
447
450 :param uri:
448 :param uri:
451 :rtype: unicode
449 :rtype: unicode
452 :returns: filtered list of strings
450 :returns: filtered list of strings
453 """
451 """
454 if not uri:
452 if not uri:
455 return ''
453 return ''
456
454
457 proto = ''
455 proto = ''
458
456
459 for pat in ('https://', 'http://'):
457 for pat in ('https://', 'http://'):
460 if uri.startswith(pat):
458 if uri.startswith(pat):
461 uri = uri[len(pat):]
459 uri = uri[len(pat):]
462 proto = pat
460 proto = pat
463 break
461 break
464
462
465 # remove passwords and username
463 # remove passwords and username
466 uri = uri[uri.find('@') + 1:]
464 uri = uri[uri.find('@') + 1:]
467
465
468 # get the port
466 # get the port
469 cred_pos = uri.find(':')
467 cred_pos = uri.find(':')
470 if cred_pos == -1:
468 if cred_pos == -1:
471 host, port = uri, None
469 host, port = uri, None
472 else:
470 else:
473 host, port = uri[:cred_pos], uri[cred_pos + 1:]
471 host, port = uri[:cred_pos], uri[cred_pos + 1:]
474
472
475 return filter(None, [proto, host, port])
473 return filter(None, [proto, host, port])
476
474
477
475
478 def credentials_filter(uri):
476 def credentials_filter(uri):
479 """
477 """
480 Returns a url with removed credentials
478 Returns a url with removed credentials
481
479
482 :param uri:
480 :param uri:
483 """
481 """
484
482
485 uri = uri_filter(uri)
483 uri = uri_filter(uri)
486 #check if we have port
484 #check if we have port
487 if len(uri) > 2 and uri[2]:
485 if len(uri) > 2 and uri[2]:
488 uri[2] = ':' + uri[2]
486 uri[2] = ':' + uri[2]
489
487
490 return ''.join(uri)
488 return ''.join(uri)
491
489
492
490
493 def get_changeset_safe(repo, rev):
491 def get_changeset_safe(repo, rev):
494 """
492 """
495 Safe version of get_changeset if this changeset doesn't exists for a
493 Safe version of get_changeset if this changeset doesn't exists for a
496 repo it returns a Dummy one instead
494 repo it returns a Dummy one instead
497
495
498 :param repo:
496 :param repo:
499 :param rev:
497 :param rev:
500 """
498 """
501 from rhodecode.lib.vcs.backends.base import BaseRepository
499 from rhodecode.lib.vcs.backends.base import BaseRepository
502 from rhodecode.lib.vcs.exceptions import RepositoryError
500 from rhodecode.lib.vcs.exceptions import RepositoryError
503 from rhodecode.lib.vcs.backends.base import EmptyChangeset
501 from rhodecode.lib.vcs.backends.base import EmptyChangeset
504 if not isinstance(repo, BaseRepository):
502 if not isinstance(repo, BaseRepository):
505 raise Exception('You must pass an Repository '
503 raise Exception('You must pass an Repository '
506 'object as first argument got %s', type(repo))
504 'object as first argument got %s', type(repo))
507
505
508 try:
506 try:
509 cs = repo.get_changeset(rev)
507 cs = repo.get_changeset(rev)
510 except RepositoryError:
508 except RepositoryError:
511 cs = EmptyChangeset(requested_revision=rev)
509 cs = EmptyChangeset(requested_revision=rev)
512 return cs
510 return cs
513
511
514
512
515 def datetime_to_time(dt):
513 def datetime_to_time(dt):
516 if dt:
514 if dt:
517 return time.mktime(dt.timetuple())
515 return time.mktime(dt.timetuple())
518
516
519
517
520 def time_to_datetime(tm):
518 def time_to_datetime(tm):
521 if tm:
519 if tm:
522 if isinstance(tm, basestring):
520 if isinstance(tm, basestring):
523 try:
521 try:
524 tm = float(tm)
522 tm = float(tm)
525 except ValueError:
523 except ValueError:
526 return
524 return
527 return datetime.datetime.fromtimestamp(tm)
525 return datetime.datetime.fromtimestamp(tm)
528
526
529 MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
527 MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
530
528
531
529
532 def extract_mentioned_users(s):
530 def extract_mentioned_users(s):
533 """
531 """
534 Returns unique usernames from given string s that have @mention
532 Returns unique usernames from given string s that have @mention
535
533
536 :param s: string to get mentions
534 :param s: string to get mentions
537 """
535 """
538 usrs = set()
536 usrs = set()
539 for username in re.findall(MENTIONS_REGEX, s):
537 for username in re.findall(MENTIONS_REGEX, s):
540 usrs.add(username)
538 usrs.add(username)
541
539
542 return sorted(list(usrs), key=lambda k: k.lower())
540 return sorted(list(usrs), key=lambda k: k.lower())
543
541
544
542
545 class AttributeDict(dict):
543 class AttributeDict(dict):
546 def __getattr__(self, attr):
544 def __getattr__(self, attr):
547 return self.get(attr, None)
545 return self.get(attr, None)
548 __setattr__ = dict.__setitem__
546 __setattr__ = dict.__setitem__
549 __delattr__ = dict.__delitem__
547 __delattr__ = dict.__delitem__
550
548
551
549
552 def fix_PATH(os_=None):
550 def fix_PATH(os_=None):
553 """
551 """
554 Get current active python path, and append it to PATH variable to fix issues
552 Get current active python path, and append it to PATH variable to fix issues
555 of subprocess calls and different python versions
553 of subprocess calls and different python versions
556 """
554 """
557 if os_ is None:
555 if os_ is None:
558 import os
556 import os
559 else:
557 else:
560 os = os_
558 os = os_
561
559
562 cur_path = os.path.split(sys.executable)[0]
560 cur_path = os.path.split(sys.executable)[0]
563 if not os.environ['PATH'].startswith(cur_path):
561 if not os.environ['PATH'].startswith(cur_path):
564 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
562 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
565
563
566
564
567 def obfuscate_url_pw(engine):
565 def obfuscate_url_pw(engine):
568 _url = engine or ''
566 _url = engine or ''
569 from sqlalchemy.engine import url as sa_url
567 from sqlalchemy.engine import url as sa_url
570 try:
568 try:
571 _url = sa_url.make_url(engine)
569 _url = sa_url.make_url(engine)
572 if _url.password:
570 if _url.password:
573 _url.password = 'XXXXX'
571 _url.password = 'XXXXX'
574 except Exception:
572 except Exception:
575 pass
573 pass
576 return str(_url)
574 return str(_url)
577
575
578
576
579 def get_server_url(environ):
577 def get_server_url(environ):
580 req = webob.Request(environ)
578 req = webob.Request(environ)
581 return req.host_url + req.script_name
579 return req.host_url + req.script_name
582
580
583
581
584 def _extract_extras(env=None):
582 def _extract_extras(env=None):
585 """
583 """
586 Extracts the rc extras data from os.environ, and wraps it into named
584 Extracts the rc extras data from os.environ, and wraps it into named
587 AttributeDict object
585 AttributeDict object
588 """
586 """
589 if not env:
587 if not env:
590 env = os.environ
588 env = os.environ
591
589
592 try:
590 try:
593 rc_extras = json.loads(env['RC_SCM_DATA'])
591 rc_extras = json.loads(env['RC_SCM_DATA'])
594 except Exception:
592 except Exception:
595 print os.environ
593 print os.environ
596 print >> sys.stderr, traceback.format_exc()
594 print >> sys.stderr, traceback.format_exc()
597 rc_extras = {}
595 rc_extras = {}
598
596
599 try:
597 try:
600 for k in ['username', 'repository', 'locked_by', 'scm', 'make_lock',
598 for k in ['username', 'repository', 'locked_by', 'scm', 'make_lock',
601 'action', 'ip']:
599 'action', 'ip']:
602 rc_extras[k]
600 rc_extras[k]
603 except KeyError, e:
601 except KeyError, e:
604 raise Exception('Missing key %s in os.environ %s' % (e, rc_extras))
602 raise Exception('Missing key %s in os.environ %s' % (e, rc_extras))
605
603
606 return AttributeDict(rc_extras)
604 return AttributeDict(rc_extras)
607
605
608
606
609 def _set_extras(extras):
607 def _set_extras(extras):
610 os.environ['RC_SCM_DATA'] = json.dumps(extras)
608 os.environ['RC_SCM_DATA'] = json.dumps(extras)
611
609
612
610
613 def unique_id(hexlen=32):
611 def unique_id(hexlen=32):
614 alphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjklmnpqrstuvwxyz"
612 alphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjklmnpqrstuvwxyz"
615 return suuid(truncate_to=hexlen, alphabet=alphabet)
613 return suuid(truncate_to=hexlen, alphabet=alphabet)
616
614
617
615
618 def suuid(url=None, truncate_to=22, alphabet=None):
616 def suuid(url=None, truncate_to=22, alphabet=None):
619 """
617 """
620 Generate and return a short URL safe UUID.
618 Generate and return a short URL safe UUID.
621
619
622 If the url parameter is provided, set the namespace to the provided
620 If the url parameter is provided, set the namespace to the provided
623 URL and generate a UUID.
621 URL and generate a UUID.
624
622
625 :param url to get the uuid for
623 :param url to get the uuid for
626 :truncate_to: truncate the basic 22 UUID to shorter version
624 :truncate_to: truncate the basic 22 UUID to shorter version
627
625
628 The IDs won't be universally unique any longer, but the probability of
626 The IDs won't be universally unique any longer, but the probability of
629 a collision will still be very low.
627 a collision will still be very low.
630 """
628 """
631 # Define our alphabet.
629 # Define our alphabet.
632 _ALPHABET = alphabet or "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
630 _ALPHABET = alphabet or "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
633
631
634 # If no URL is given, generate a random UUID.
632 # If no URL is given, generate a random UUID.
635 if url is None:
633 if url is None:
636 unique_id = uuid.uuid4().int
634 unique_id = uuid.uuid4().int
637 else:
635 else:
638 unique_id = uuid.uuid3(uuid.NAMESPACE_URL, url).int
636 unique_id = uuid.uuid3(uuid.NAMESPACE_URL, url).int
639
637
640 alphabet_length = len(_ALPHABET)
638 alphabet_length = len(_ALPHABET)
641 output = []
639 output = []
642 while unique_id > 0:
640 while unique_id > 0:
643 digit = unique_id % alphabet_length
641 digit = unique_id % alphabet_length
644 output.append(_ALPHABET[digit])
642 output.append(_ALPHABET[digit])
645 unique_id = int(unique_id / alphabet_length)
643 unique_id = int(unique_id / alphabet_length)
646 return "".join(output)[:truncate_to]
644 return "".join(output)[:truncate_to]
@@ -1,190 +1,188 b''
1 """
1 """
2 This module provides some useful tools for ``vcs`` like annotate/diff html
2 This module provides some useful tools for ``vcs`` like annotate/diff html
3 output. It also includes some internal helpers.
3 output. It also includes some internal helpers.
4 """
4 """
5 import sys
5 import sys
6 import time
6 import time
7 import datetime
7 import datetime
8
8
9
9
10 def makedate():
10 def makedate():
11 lt = time.localtime()
11 lt = time.localtime()
12 if lt[8] == 1 and time.daylight:
12 if lt[8] == 1 and time.daylight:
13 tz = time.altzone
13 tz = time.altzone
14 else:
14 else:
15 tz = time.timezone
15 tz = time.timezone
16 return time.mktime(lt), tz
16 return time.mktime(lt), tz
17
17
18
18
19 def aslist(obj, sep=None, strip=True):
19 def aslist(obj, sep=None, strip=True):
20 """
20 """
21 Returns given string separated by sep as list
21 Returns given string separated by sep as list
22
22
23 :param obj:
23 :param obj:
24 :param sep:
24 :param sep:
25 :param strip:
25 :param strip:
26 """
26 """
27 if isinstance(obj, (basestring)):
27 if isinstance(obj, (basestring)):
28 lst = obj.split(sep)
28 lst = obj.split(sep)
29 if strip:
29 if strip:
30 lst = [v.strip() for v in lst]
30 lst = [v.strip() for v in lst]
31 return lst
31 return lst
32 elif isinstance(obj, (list, tuple)):
32 elif isinstance(obj, (list, tuple)):
33 return obj
33 return obj
34 elif obj is None:
34 elif obj is None:
35 return []
35 return []
36 else:
36 else:
37 return [obj]
37 return [obj]
38
38
39
39
40 def date_fromtimestamp(unixts, tzoffset=0):
40 def date_fromtimestamp(unixts, tzoffset=0):
41 """
41 """
42 Makes a local datetime object out of unix timestamp
42 Makes a local datetime object out of unix timestamp
43
43
44 :param unixts:
44 :param unixts:
45 :param tzoffset:
45 :param tzoffset:
46 """
46 """
47
47
48 return datetime.datetime.fromtimestamp(float(unixts))
48 return datetime.datetime.fromtimestamp(float(unixts))
49
49
50
50
51 def safe_int(val, default=None):
51 def safe_int(val, default=None):
52 """
52 """
53 Returns int() of val if val is not convertable to int use default
53 Returns int() of val if val is not convertable to int use default
54 instead
54 instead
55
55
56 :param val:
56 :param val:
57 :param default:
57 :param default:
58 """
58 """
59
59
60 try:
60 try:
61 val = int(val)
61 val = int(val)
62 except (ValueError, TypeError):
62 except (ValueError, TypeError):
63 val = default
63 val = default
64
64
65 return val
65 return val
66
66
67
67
68 def safe_unicode(str_, from_encoding=None):
68 def safe_unicode(str_, from_encoding=None):
69 """
69 """
70 safe unicode function. Does few trick to turn str_ into unicode
70 safe unicode function. Does few trick to turn str_ into unicode
71
71
72 In case of UnicodeDecode error we try to return it with encoding detected
72 In case of UnicodeDecode error we try to return it with encoding detected
73 by chardet library if it fails fallback to unicode with errors replaced
73 by chardet library if it fails fallback to unicode with errors replaced
74
74
75 :param str_: string to decode
75 :param str_: string to decode
76 :rtype: unicode
76 :rtype: unicode
77 :returns: unicode object
77 :returns: unicode object
78 """
78 """
79 if isinstance(str_, unicode):
79 if isinstance(str_, unicode):
80 return str_
80 return str_
81
81
82 if not from_encoding:
82 if not from_encoding:
83 from rhodecode.lib.vcs.conf import settings
83 from rhodecode.lib.vcs.conf import settings
84 from_encoding = settings.DEFAULT_ENCODINGS
84 from_encoding = settings.DEFAULT_ENCODINGS
85
85
86 if not isinstance(from_encoding, (list, tuple)):
86 if not isinstance(from_encoding, (list, tuple)):
87 from_encoding = [from_encoding]
87 from_encoding = [from_encoding]
88
88
89 try:
89 try:
90 return unicode(str_)
90 return unicode(str_)
91 except UnicodeDecodeError:
91 except UnicodeDecodeError:
92 pass
92 pass
93
93
94 for enc in from_encoding:
94 for enc in from_encoding:
95 try:
95 try:
96 return unicode(str_, enc)
96 return unicode(str_, enc)
97 except UnicodeDecodeError:
97 except UnicodeDecodeError:
98 pass
98 pass
99
99
100 try:
100 try:
101 import chardet
101 import chardet
102 encoding = chardet.detect(str_)['encoding']
102 encoding = chardet.detect(str_)['encoding']
103 if encoding is None:
103 if encoding is None:
104 raise Exception()
104 raise Exception()
105 return str_.decode(encoding)
105 return str_.decode(encoding)
106 except (ImportError, UnicodeDecodeError, Exception):
106 except (ImportError, UnicodeDecodeError, Exception):
107 return unicode(str_, from_encoding[0], 'replace')
107 return unicode(str_, from_encoding[0], 'replace')
108
108
109
109
110 def safe_str(unicode_, to_encoding=None):
110 def safe_str(unicode_, to_encoding=None):
111 """
111 """
112 safe str function. Does few trick to turn unicode_ into string
112 safe str function. Does few trick to turn unicode_ into string
113
113
114 In case of UnicodeEncodeError we try to return it with encoding detected
114 In case of UnicodeEncodeError we try to return it with encoding detected
115 by chardet library if it fails fallback to string with errors replaced
115 by chardet library if it fails fallback to string with errors replaced
116
116
117 :param unicode_: unicode to encode
117 :param unicode_: unicode to encode
118 :rtype: str
118 :rtype: str
119 :returns: str object
119 :returns: str object
120 """
120 """
121
121
122 # if it's not basestr cast to str
122 # if it's not basestr cast to str
123 if not isinstance(unicode_, basestring):
123 if not isinstance(unicode_, basestring):
124 return str(unicode_)
124 return str(unicode_)
125
125
126 if isinstance(unicode_, str):
126 if isinstance(unicode_, str):
127 return unicode_
127 return unicode_
128
128
129 if not to_encoding:
129 if not to_encoding:
130 from rhodecode.lib.vcs.conf import settings
130 from rhodecode.lib.vcs.conf import settings
131 to_encoding = settings.DEFAULT_ENCODINGS
131 to_encoding = settings.DEFAULT_ENCODINGS
132
132
133 if not isinstance(to_encoding, (list, tuple)):
133 if not isinstance(to_encoding, (list, tuple)):
134 to_encoding = [to_encoding]
134 to_encoding = [to_encoding]
135
135
136 for enc in to_encoding:
136 for enc in to_encoding:
137 try:
137 try:
138 return unicode_.encode(enc)
138 return unicode_.encode(enc)
139 except UnicodeEncodeError:
139 except UnicodeEncodeError:
140 pass
140 pass
141
141
142 try:
142 try:
143 import chardet
143 import chardet
144 encoding = chardet.detect(unicode_)['encoding']
144 encoding = chardet.detect(unicode_)['encoding']
145 if encoding is None:
145 if encoding is None:
146 raise UnicodeEncodeError()
146 raise UnicodeEncodeError()
147
147
148 return unicode_.encode(encoding)
148 return unicode_.encode(encoding)
149 except (ImportError, UnicodeEncodeError):
149 except (ImportError, UnicodeEncodeError):
150 return unicode_.encode(to_encoding[0], 'replace')
150 return unicode_.encode(to_encoding[0], 'replace')
151
151
152 return safe_str
153
154
152
155 def author_email(author):
153 def author_email(author):
156 """
154 """
157 returns email address of given author.
155 returns email address of given author.
158 If any of <,> sign are found, it fallbacks to regex findall()
156 If any of <,> sign are found, it fallbacks to regex findall()
159 and returns first found result or empty string
157 and returns first found result or empty string
160
158
161 Regex taken from http://www.regular-expressions.info/email.html
159 Regex taken from http://www.regular-expressions.info/email.html
162 """
160 """
163 import re
161 import re
164 r = author.find('>')
162 r = author.find('>')
165 l = author.find('<')
163 l = author.find('<')
166
164
167 if l == -1 or r == -1:
165 if l == -1 or r == -1:
168 # fallback to regex match of email out of a string
166 # fallback to regex match of email out of a string
169 email_re = re.compile(r"""[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!"""
167 email_re = re.compile(r"""[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!"""
170 r"""#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z"""
168 r"""#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z"""
171 r"""0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]"""
169 r"""0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]"""
172 r"""*[a-z0-9])?""", re.IGNORECASE)
170 r"""*[a-z0-9])?""", re.IGNORECASE)
173 m = re.findall(email_re, author)
171 m = re.findall(email_re, author)
174 return m[0] if m else ''
172 return m[0] if m else ''
175
173
176 return author[l + 1:r].strip()
174 return author[l + 1:r].strip()
177
175
178
176
179 def author_name(author):
177 def author_name(author):
180 """
178 """
181 get name of author, or else username.
179 get name of author, or else username.
182 It'll try to find an email in the author string and just cut it off
180 It'll try to find an email in the author string and just cut it off
183 to get the username
181 to get the username
184 """
182 """
185
183
186 if not '@' in author:
184 if not '@' in author:
187 return author
185 return author
188 else:
186 else:
189 return author.replace(author_email(author), '').replace('<', '')\
187 return author.replace(author_email(author), '').replace('<', '')\
190 .replace('>', '').strip()
188 .replace('>', '').strip()
@@ -1,221 +1,220 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.tests.test_hg_operations
3 rhodecode.tests.test_hg_operations
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Test suite for making push/pull operations
6 Test suite for making push/pull operations
7
7
8 :created_on: Dec 30, 2010
8 :created_on: Dec 30, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import sys
27 import sys
28 import shutil
28 import shutil
29 import logging
29 import logging
30 from os.path import join as jn
30 from os.path import join as jn
31 from os.path import dirname as dn
31 from os.path import dirname as dn
32
32
33 from tempfile import _RandomNameSequence
33 from tempfile import _RandomNameSequence
34 from subprocess import Popen, PIPE
34 from subprocess import Popen, PIPE
35
35
36 from paste.deploy import appconfig
36 from paste.deploy import appconfig
37 from pylons import config
37 from pylons import config
38 from sqlalchemy import engine_from_config
38 from sqlalchemy import engine_from_config
39
39
40 from rhodecode.lib.utils import add_cache
40 from rhodecode.lib.utils import add_cache
41 from rhodecode.model import init_model
41 from rhodecode.model import init_model
42 from rhodecode.model import meta
42 from rhodecode.model import meta
43 from rhodecode.model.db import User, Repository
43 from rhodecode.model.db import User, Repository
44 from rhodecode.lib.auth import get_crypt_password
44 from rhodecode.lib.auth import get_crypt_password
45
45
46 from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
46 from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
47 from rhodecode.config.environment import load_environment
47 from rhodecode.config.environment import load_environment
48
48
49 rel_path = dn(dn(dn(dn(os.path.abspath(__file__)))))
49 rel_path = dn(dn(dn(dn(os.path.abspath(__file__)))))
50 conf = appconfig('config:rc.ini', relative_to=rel_path)
50 conf = appconfig('config:rc.ini', relative_to=rel_path)
51 load_environment(conf.global_conf, conf.local_conf)
51 load_environment(conf.global_conf, conf.local_conf)
52
52
53 add_cache(conf)
53 add_cache(conf)
54
54
55 USER = 'test_admin'
55 USER = 'test_admin'
56 PASS = 'test12'
56 PASS = 'test12'
57 HOST = 'rc.local'
57 HOST = 'rc.local'
58 METHOD = 'pull'
58 METHOD = 'pull'
59 DEBUG = True
59 DEBUG = True
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62
62
63 class Command(object):
63 class Command(object):
64
64
65 def __init__(self, cwd):
65 def __init__(self, cwd):
66 self.cwd = cwd
66 self.cwd = cwd
67
67
68 def execute(self, cmd, *args):
68 def execute(self, cmd, *args):
69 """Runs command on the system with given ``args``.
69 """Runs command on the system with given ``args``.
70 """
70 """
71
71
72 command = cmd + ' ' + ' '.join(args)
72 command = cmd + ' ' + ' '.join(args)
73 log.debug('Executing %s' % command)
73 log.debug('Executing %s' % command)
74 if DEBUG:
74 if DEBUG:
75 print command
75 print command
76 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
76 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
77 stdout, stderr = p.communicate()
77 stdout, stderr = p.communicate()
78 if DEBUG:
78 if DEBUG:
79 print stdout, stderr
79 print stdout, stderr
80 return stdout, stderr
80 return stdout, stderr
81
81
82
82
83 def get_session():
83 def get_session():
84 engine = engine_from_config(conf, 'sqlalchemy.db1.')
84 engine = engine_from_config(conf, 'sqlalchemy.db1.')
85 init_model(engine)
85 init_model(engine)
86 sa = meta.Session
86 sa = meta.Session
87 return sa
87 return sa
88
88
89
89
90 def create_test_user(force=True):
90 def create_test_user(force=True):
91 print 'creating test user'
91 print 'creating test user'
92 sa = get_session()
92 sa = get_session()
93
93
94 user = sa.query(User).filter(User.username == USER).scalar()
94 user = sa.query(User).filter(User.username == USER).scalar()
95
95
96 if force and user is not None:
96 if force and user is not None:
97 print 'removing current user'
97 print 'removing current user'
98 for repo in sa.query(Repository).filter(Repository.user == user).all():
98 for repo in sa.query(Repository).filter(Repository.user == user).all():
99 sa.delete(repo)
99 sa.delete(repo)
100 sa.delete(user)
100 sa.delete(user)
101 sa.commit()
101 sa.commit()
102
102
103 if user is None or force:
103 if user is None or force:
104 print 'creating new one'
104 print 'creating new one'
105 new_usr = User()
105 new_usr = User()
106 new_usr.username = USER
106 new_usr.username = USER
107 new_usr.password = get_crypt_password(PASS)
107 new_usr.password = get_crypt_password(PASS)
108 new_usr.email = 'mail@mail.com'
108 new_usr.email = 'mail@mail.com'
109 new_usr.name = 'test'
109 new_usr.name = 'test'
110 new_usr.lastname = 'lasttestname'
110 new_usr.lastname = 'lasttestname'
111 new_usr.active = True
111 new_usr.active = True
112 new_usr.admin = True
112 new_usr.admin = True
113 sa.add(new_usr)
113 sa.add(new_usr)
114 sa.commit()
114 sa.commit()
115
115
116 print 'done'
116 print 'done'
117
117
118
118
119 def create_test_repo(force=True):
119 def create_test_repo(force=True):
120 print 'creating test repo'
120 print 'creating test repo'
121 from rhodecode.model.repo import RepoModel
121 from rhodecode.model.repo import RepoModel
122 sa = get_session()
122 sa = get_session()
123
123
124 user = sa.query(User).filter(User.username == USER).scalar()
124 user = sa.query(User).filter(User.username == USER).scalar()
125 if user is None:
125 if user is None:
126 raise Exception('user not found')
126 raise Exception('user not found')
127
127
128 repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
128 repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
129
129
130 if repo is None:
130 if repo is None:
131 print 'repo not found creating'
131 print 'repo not found creating'
132
132
133 form_data = {'repo_name': HG_REPO,
133 form_data = {'repo_name': HG_REPO,
134 'repo_type': 'hg',
134 'repo_type': 'hg',
135 'private':False,
135 'private':False,
136 'clone_uri': '' }
136 'clone_uri': '' }
137 rm = RepoModel(sa)
137 rm = RepoModel(sa)
138 rm.base_path = '/home/hg'
138 rm.base_path = '/home/hg'
139 rm.create(form_data, user)
139 rm.create(form_data, user)
140
140
141 print 'done'
141 print 'done'
142
142
143
143
144 def set_anonymous_access(enable=True):
144 def set_anonymous_access(enable=True):
145 sa = get_session()
145 sa = get_session()
146 user = sa.query(User).filter(User.username == 'default').one()
146 user = sa.query(User).filter(User.username == 'default').one()
147 user.active = enable
147 user.active = enable
148 sa.add(user)
148 sa.add(user)
149 sa.commit()
149 sa.commit()
150
150
151
151
152 def get_anonymous_access():
152 def get_anonymous_access():
153 sa = get_session()
153 sa = get_session()
154 return sa.query(User).filter(User.username == 'default').one().active
154 return sa.query(User).filter(User.username == 'default').one().active
155
155
156
156
157 #==============================================================================
157 #==============================================================================
158 # TESTS
158 # TESTS
159 #==============================================================================
159 #==============================================================================
160 def test_clone_with_credentials(no_errors=False, repo=HG_REPO, method=METHOD,
160 def test_clone_with_credentials(no_errors=False, repo=HG_REPO, method=METHOD,
161 seq=None, backend='hg'):
161 seq=None, backend='hg'):
162 cwd = path = jn(TESTS_TMP_PATH, repo)
162 cwd = path = jn(TESTS_TMP_PATH, repo)
163
163
164 if seq == None:
164 if seq == None:
165 seq = _RandomNameSequence().next()
165 seq = _RandomNameSequence().next()
166
166
167 try:
167 try:
168 shutil.rmtree(path, ignore_errors=True)
168 shutil.rmtree(path, ignore_errors=True)
169 os.makedirs(path)
169 os.makedirs(path)
170 #print 'made dirs %s' % jn(path)
170 #print 'made dirs %s' % jn(path)
171 except OSError:
171 except OSError:
172 raise
172 raise
173
173
174 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
174 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
175 {'user': USER,
175 {'user': USER,
176 'pass': PASS,
176 'pass': PASS,
177 'host': HOST,
177 'host': HOST,
178 'cloned_repo': repo, }
178 'cloned_repo': repo, }
179
179
180 dest = path + seq
180 dest = path + seq
181 if method == 'pull':
181 if method == 'pull':
182 stdout, stderr = Command(cwd).execute(backend, method, '--cwd', dest, clone_url)
182 stdout, stderr = Command(cwd).execute(backend, method, '--cwd', dest, clone_url)
183 else:
183 else:
184 stdout, stderr = Command(cwd).execute(backend, method, clone_url, dest)
184 stdout, stderr = Command(cwd).execute(backend, method, clone_url, dest)
185 print stdout,'sdasdsadsa'
185 print stdout,'sdasdsadsa'
186 if not no_errors:
186 if not no_errors:
187 if backend == 'hg':
187 if backend == 'hg':
188 assert """adding file changes""" in stdout, 'no messages about cloning'
188 assert """adding file changes""" in stdout, 'no messages about cloning'
189 assert """abort""" not in stderr , 'got error from clone'
189 assert """abort""" not in stderr , 'got error from clone'
190 elif backend == 'git':
190 elif backend == 'git':
191 assert """Cloning into""" in stdout, 'no messages about cloning'
191 assert """Cloning into""" in stdout, 'no messages about cloning'
192
192
193 if __name__ == '__main__':
193 if __name__ == '__main__':
194 try:
194 try:
195 create_test_user(force=False)
195 create_test_user(force=False)
196 seq = None
196 seq = None
197 import time
197 import time
198
198
199 try:
199 try:
200 METHOD = sys.argv[3]
200 METHOD = sys.argv[3]
201 except Exception:
201 except Exception:
202 pass
202 pass
203
203
204 try:
204 try:
205 backend = sys.argv[4]
205 backend = sys.argv[4]
206 except Exception:
206 except Exception:
207 backend = 'hg'
207 backend = 'hg'
208
208
209 if METHOD == 'pull':
209 if METHOD == 'pull':
210 seq = _RandomNameSequence().next()
210 seq = _RandomNameSequence().next()
211 test_clone_with_credentials(repo=sys.argv[1], method='clone',
211 test_clone_with_credentials(repo=sys.argv[1], method='clone',
212 seq=seq, backend=backend)
212 seq=seq, backend=backend)
213 s = time.time()
213 s = time.time()
214 for i in range(1, int(sys.argv[2]) + 1):
214 for i in range(1, int(sys.argv[2]) + 1):
215 print 'take', i
215 print 'take', i
216 test_clone_with_credentials(repo=sys.argv[1], method=METHOD,
216 test_clone_with_credentials(repo=sys.argv[1], method=METHOD,
217 seq=seq, backend=backend)
217 seq=seq, backend=backend)
218 print 'time taken %.3f' % (time.time() - s)
218 print 'time taken %.3f' % (time.time() - s)
219 except Exception, e:
219 except Exception, e:
220 raise
221 sys.exit('stop on %s' % e)
220 sys.exit('stop on %s' % e)
General Comments 0
You need to be logged in to leave comments. Login now