318bee91701d9fa926e4882da67d408da562ab2c
[pub/Android/ownCloud.git] / third_party / transifex-client / txclib / utils.py
1 import os, sys, re, errno
2 try:
3 from json import loads as parse_json, dumps as compile_json
4 except ImportError:
5 from simplejson import loads as parse_json, dumps as compile_json
6 import urllib2 # This should go and instead use do_url_request everywhere
7
8 from urls import API_URLS
9 from txclib.log import logger
10 from txclib.exceptions import UnknownCommandError
11
12
13 def find_dot_tx(path = os.path.curdir, previous = None):
14 """
15 Return the path where .tx folder is found.
16
17 The 'path' should be a DIRECTORY.
18 This process is functioning recursively from the current directory to each
19 one of the ancestors dirs.
20 """
21 path = os.path.abspath(path)
22 if path == previous:
23 return None
24 joined = os.path.join(path, ".tx")
25 if os.path.isdir(joined):
26 return path
27 else:
28 return find_dot_tx(os.path.dirname(path), path)
29
30
31 #################################################
32 # Parse file filter expressions and create regex
33
34 def regex_from_filefilter(file_filter, root_path = os.path.curdir):
35 """
36 Create proper regex from <lang> expression
37 """
38 # Force expr to be a valid regex expr (escaped) but keep <lang> intact
39 expr_re = re.escape(os.path.join(root_path, file_filter))
40 expr_re = expr_re.replace("\\<lang\\>", '<lang>').replace(
41 '<lang>', '([^%(sep)s]+)' % { 'sep': re.escape(os.path.sep)})
42
43 return "^%s$" % expr_re
44
45
46 TX_URLS = {
47 'resource': '(?P<hostname>https?://(\w|\.|:|-)+)/projects/p/(?P<project>(\w|-)+)/resource/(?P<resource>(\w|-)+)/?$',
48 'release': '(?P<hostname>https?://(\w|\.|:|-)+)/projects/p/(?P<project>(\w|-)+)/r/(?P<release>(\w|-)+)/?$',
49 'project': '(?P<hostname>https?://(\w|\.|:|-)+)/projects/p/(?P<project>(\w|-)+)/?$',
50 }
51
52
53 def parse_tx_url(url):
54 """
55 Try to match given url to any of the valid url patterns specified in
56 TX_URLS. If not match is found, we raise exception
57 """
58 for type in TX_URLS.keys():
59 pattern = TX_URLS[type]
60 m = re.match(pattern, url)
61 if m:
62 return type, m.groupdict()
63
64 raise Exception("tx: Malformed url given. Please refer to our docs: http://bit.ly/txautor")
65
66
67 def get_details(api_call, username, password, *args, **kwargs):
68 """
69 Get the tx project info through the API.
70
71 This function can also be used to check the existence of a project.
72 """
73 import base64
74 url = (API_URLS[api_call] % (kwargs)).encode('UTF-8')
75
76 req = urllib2.Request(url=url)
77 base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
78 authheader = "Basic %s" % base64string
79 req.add_header("Authorization", authheader)
80
81 try:
82 fh = urllib2.urlopen(req)
83 raw = fh.read()
84 fh.close()
85 remote_project = parse_json(raw)
86 except urllib2.HTTPError, e:
87 if e.code in [401, 403, 404]:
88 raise e
89 else:
90 # For other requests, we should print the message as well
91 raise Exception("Remote server replied: %s" % e.read())
92 except urllib2.URLError, e:
93 error = e.args[0]
94 raise Exception("Remote server replied: %s" % error[1])
95
96 return remote_project
97
98
99 def valid_slug(slug):
100 """
101 Check if a slug contains only valid characters.
102
103 Valid chars include [-_\w]
104 """
105 try:
106 a, b = slug.split('.')
107 except ValueError:
108 return False
109 else:
110 if re.match("^[A-Za-z0-9_-]*$", a) and re.match("^[A-Za-z0-9_-]*$", b):
111 return True
112 return False
113
114
115 def discover_commands():
116 """
117 Inspect commands.py and find all available commands
118 """
119 import inspect
120 from txclib import commands
121
122 command_table = {}
123 fns = inspect.getmembers(commands, inspect.isfunction)
124
125 for name, fn in fns:
126 if name.startswith("cmd_"):
127 command_table.update({
128 name.split("cmd_")[1]:fn
129 })
130
131 return command_table
132
133
134 def exec_command(command, *args, **kwargs):
135 """
136 Execute given command
137 """
138 commands = discover_commands()
139 try:
140 cmd_fn = commands[command]
141 except KeyError:
142 raise UnknownCommandError
143 cmd_fn(*args,**kwargs)
144
145
146 def mkdir_p(path):
147 try:
148 if path:
149 os.makedirs(path)
150 except OSError, exc: # Python >2.5
151 if exc.errno == errno.EEXIST:
152 pass
153 else:
154 raise
155
156
157 def confirm(prompt='Continue?', default=True):
158 """
159 Prompt the user for a Yes/No answer.
160
161 Args:
162 prompt: The text displayed to the user ([Y/n] will be appended)
163 default: If the default value will be yes or no
164 """
165 valid_yes = ['Y', 'y', 'Yes', 'yes', ]
166 valid_no = ['N', 'n', 'No', 'no', ]
167 if default:
168 prompt = prompt + '[Y/n]'
169 valid_yes.append('')
170 else:
171 prompt = prompt + '[y/N]'
172 valid_no.append('')
173
174 ans = raw_input(prompt)
175 while (ans not in valid_yes and ans not in valid_no):
176 ans = raw_input(prompt)
177
178 return ans in valid_yes
179
180
181 # Stuff for command line colored output
182
183 COLORS = [
184 'BLACK', 'RED', 'GREEN', 'YELLOW',
185 'BLUE', 'MAGENTA', 'CYAN', 'WHITE'
186 ]
187
188 DISABLE_COLORS = False
189
190
191 def color_text(text, color_name, bold=False):
192 """
193 This command can be used to colorify command line output. If the shell
194 doesn't support this or the --disable-colors options has been set, it just
195 returns the plain text.
196
197 Usage:
198 print "%s" % color_text("This text is red", "RED")
199 """
200 if color_name in COLORS and not DISABLE_COLORS:
201 return '\033[%s;%sm%s\033[0m' % (
202 int(bold), COLORS.index(color_name) + 30, text)
203 else:
204 return text
205
206
207 ##############################################
208 # relpath implementation taken from Python 2.7
209
210 if not hasattr(os.path, 'relpath'):
211 if os.path is sys.modules.get('ntpath'):
212 def relpath(path, start=os.path.curdir):
213 """Return a relative version of a path"""
214
215 if not path:
216 raise ValueError("no path specified")
217 start_list = os.path.abspath(start).split(os.path.sep)
218 path_list = os.path.abspath(path).split(os.path.sep)
219 if start_list[0].lower() != path_list[0].lower():
220 unc_path, rest = os.path.splitunc(path)
221 unc_start, rest = os.path.splitunc(start)
222 if bool(unc_path) ^ bool(unc_start):
223 raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)"
224 % (path, start))
225 else:
226 raise ValueError("path is on drive %s, start on drive %s"
227 % (path_list[0], start_list[0]))
228 # Work out how much of the filepath is shared by start and path.
229 for i in range(min(len(start_list), len(path_list))):
230 if start_list[i].lower() != path_list[i].lower():
231 break
232 else:
233 i += 1
234
235 rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
236 if not rel_list:
237 return os.path.curdir
238 return os.path.join(*rel_list)
239
240 else:
241 # default to posixpath definition
242 def relpath(path, start=os.path.curdir):
243 """Return a relative version of a path"""
244
245 if not path:
246 raise ValueError("no path specified")
247
248 start_list = os.path.abspath(start).split(os.path.sep)
249 path_list = os.path.abspath(path).split(os.path.sep)
250
251 # Work out how much of the filepath is shared by start and path.
252 i = len(os.path.commonprefix([start_list, path_list]))
253
254 rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
255 if not rel_list:
256 return os.path.curdir
257 return os.path.join(*rel_list)
258 else:
259 from os.path import relpath