Mercurial > libervia-backend
comparison sat/tools/utils.py @ 2562:26edcf3a30eb
core, setup: huge cleaning:
- moved directories from src and frontends/src to sat and sat_frontends, which is the recommanded naming convention
- move twisted directory to root
- removed all hacks from setup.py, and added missing dependencies, it is now clean
- use https URL for website in setup.py
- removed "Environment :: X11 Applications :: GTK", as wix is deprecated and removed
- renamed sat.sh to sat and fixed its installation
- added python_requires to specify Python version needed
- replaced glib2reactor which use deprecated code by gtk3reactor
sat can now be installed directly from virtualenv without using --system-site-packages anymore \o/
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 02 Apr 2018 19:44:50 +0200 |
parents | src/tools/utils.py@0046283a285d |
children | 8e204f0d3193 |
comparison
equal
deleted
inserted
replaced
2561:bd30dc3ffe5a | 2562:26edcf3a30eb |
---|---|
1 #!/usr/bin/env python2 | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # SAT: a jabber client | |
5 # Copyright (C) 2009-2018 Jérôme Poisson (goffi@goffi.org) | |
6 | |
7 # This program is free software: you can redistribute it and/or modify | |
8 # it under the terms of the GNU Affero General Public License as published by | |
9 # the Free Software Foundation, either version 3 of the License, or | |
10 # (at your option) any later version. | |
11 | |
12 # This program is distributed in the hope that it will be useful, | |
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 # GNU Affero General Public License for more details. | |
16 | |
17 # You should have received a copy of the GNU Affero General Public License | |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | |
20 """ various useful methods """ | |
21 | |
22 import unicodedata | |
23 import os.path | |
24 from sat.core.log import getLogger | |
25 log = getLogger(__name__) | |
26 import datetime | |
27 from dateutil import parser as dateutil_parser | |
28 import calendar | |
29 import time | |
30 import sys | |
31 import random | |
32 import inspect | |
33 import textwrap | |
34 import functools | |
35 | |
36 | |
37 def clean_ustr(ustr): | |
38 """Clean unicode string | |
39 | |
40 remove special characters from unicode string | |
41 """ | |
42 def valid_chars(unicode_source): | |
43 for char in unicode_source: | |
44 if unicodedata.category(char) == 'Cc' and char!='\n': | |
45 continue | |
46 yield char | |
47 return ''.join(valid_chars(ustr)) | |
48 | |
49 def partial(func, *fixed_args, **fixed_kwargs): | |
50 # FIXME: temporary hack to workaround the fact that inspect.getargspec is not working with functools.partial | |
51 # making partial unusable with current D-bus module (in addMethod). | |
52 # Should not be needed anywore once moved to Python 3 | |
53 | |
54 ori_args = inspect.getargspec(func).args | |
55 func = functools.partial(func, *fixed_args, **fixed_kwargs) | |
56 if ori_args[0] == 'self': | |
57 del ori_args[0] | |
58 ori_args = ori_args[len(fixed_args):] | |
59 for kw in fixed_kwargs: | |
60 ori_args.remove(kw) | |
61 | |
62 exec(textwrap.dedent('''\ | |
63 def method({args}): | |
64 return func({kw_args}) | |
65 ''').format( | |
66 args = ', '.join(ori_args), | |
67 kw_args = ', '.join([a+'='+a for a in ori_args])) | |
68 , locals()) | |
69 | |
70 return method | |
71 | |
72 def xmpp_date(timestamp=None, with_time=True): | |
73 """Return date according to XEP-0082 specification | |
74 | |
75 to avoid reveling the timezone, we always return UTC dates | |
76 the string returned by this method is valid with RFC 3339 | |
77 @param timestamp(None, float): posix timestamp. If None current time will be used | |
78 @param with_time(bool): if True include the time | |
79 @return(unicode): XEP-0082 formatted date and time | |
80 """ | |
81 template_date = u"%Y-%m-%d" | |
82 template_time = u"%H:%M:%SZ" | |
83 template = u"{}T{}".format(template_date, template_time) if with_time else template_date | |
84 return datetime.datetime.utcfromtimestamp(time.time() if timestamp is None else timestamp).strftime(template) | |
85 | |
86 def date_parse(value): | |
87 """Parse a date and return corresponding unix timestamp | |
88 | |
89 @param value(unicode): date to parse, in any format supported by dateutil.parser | |
90 @return (int): timestamp | |
91 """ | |
92 return calendar.timegm(dateutil_parser.parse(unicode(value)).utctimetuple()) | |
93 | |
94 def generatePassword(vocabulary=None, size=20): | |
95 """Generate a password with random characters. | |
96 | |
97 @param vocabulary(iterable): characters to use to create password | |
98 @param size(int): number of characters in the password to generate | |
99 @return (unicode): generated password | |
100 """ | |
101 random.seed() | |
102 if vocabulary is None: | |
103 vocabulary = [chr(i) for i in range(0x30,0x3A) + range(0x41,0x5B) + range (0x61,0x7B)] | |
104 return u''.join([random.choice(vocabulary) for i in range(15)]) | |
105 | |
106 def getRepositoryData(module, as_string=True, is_path=False, save_dir_path=None): | |
107 """Retrieve info on current mecurial repository | |
108 | |
109 Data is gotten by using the following methods, in order: | |
110 - using "hg" executable | |
111 - looking for a ".hg_data" file in the root of the module | |
112 this file must contain the data dictionnary serialized with pickle | |
113 - looking for a .hg/dirstate in parent directory of module (or in module/.hg if | |
114 is_path is True), and parse dirstate file to get revision | |
115 @param module(unicode): module to look for (e.g. sat, libervia) | |
116 module can be a path if is_path is True (see below) | |
117 @param as_string(bool): if True return a string, else return a dictionary | |
118 @param is_path(bool): if True "module" is not handled as a module name, but as an | |
119 absolute path to the parent of a ".hg" directory | |
120 @param save_path(str, None): if not None, the value will be saved to given path as a pickled dict | |
121 /!\\ the .hg_data file in the given directory will be overwritten | |
122 @return (unicode, dictionary): retrieved info in a nice string, | |
123 or a dictionary with retrieved data (key is not present if data is not found), | |
124 key can be: | |
125 - node: full revision number (40 bits) | |
126 - branch: branch name | |
127 - date: ISO 8601 format date | |
128 - tag: latest tag used in hierarchie | |
129 """ | |
130 if sys.platform == "android": | |
131 # FIXME: workaround to avoid trouble on android, need to be fixed properly | |
132 return u"Cagou android build" | |
133 from distutils.spawn import find_executable | |
134 import subprocess | |
135 KEYS=("node", "node_short", "branch", "date", "tag") | |
136 ori_cwd = os.getcwd() | |
137 | |
138 if is_path: | |
139 repos_root = module | |
140 else: | |
141 repos_root = os.path.dirname(module.__file__) | |
142 | |
143 hg_path = find_executable('hg') | |
144 | |
145 if hg_path is not None: | |
146 os.chdir(repos_root) | |
147 try: | |
148 hg_data_raw = subprocess.check_output(["hg","log", "-r", "-1", "--template","{node}\n{node|short}\n{branch}\n{date|isodate}\n{latesttag}"]) | |
149 except subprocess.CalledProcessError: | |
150 hg_data = {} | |
151 else: | |
152 hg_data = dict(zip(KEYS, hg_data_raw.split('\n'))) | |
153 try: | |
154 hg_data['modified'] = '+' in subprocess.check_output(["hg","id","-i"]) | |
155 except subprocess.CalledProcessError: | |
156 pass | |
157 else: | |
158 hg_data = {} | |
159 | |
160 if not hg_data: | |
161 # .hg_data pickle method | |
162 log.debug(u"Mercurial not available or working, trying other methods") | |
163 if save_dir_path is None: | |
164 log.debug(u"trying .hg_data method") | |
165 | |
166 try: | |
167 with open(os.path.join(repos_root, '.hg_data')) as f: | |
168 import cPickle as pickle | |
169 hg_data = pickle.load(f) | |
170 except IOError as e: | |
171 log.debug(u"Can't access .hg_data file: {}".format(e)) | |
172 except pickle.UnpicklingError: | |
173 log.warning(u"Error while reading {}, can't get repos data".format(f.name)) | |
174 | |
175 if not hg_data: | |
176 # .hg/dirstate method | |
177 log.debug(u"trying dirstate method") | |
178 if is_path: | |
179 os.chdir(repos_root) | |
180 else: | |
181 os.chdir(os.path.relpath('..', repos_root)) | |
182 try: | |
183 with open('.hg/dirstate') as hg_dirstate: | |
184 hg_data['node'] = hg_dirstate.read(20).encode('hex') | |
185 hg_data['node_short'] = hg_data['node'][:12] | |
186 except IOError: | |
187 log.warning(u"Can't access repository data") | |
188 | |
189 # we restore original working dir | |
190 os.chdir(ori_cwd) | |
191 | |
192 # data saving | |
193 if save_dir_path is not None and hg_data: | |
194 if not os.path.isdir(save_dir_path): | |
195 log.warning(u"Given path is not a directory, can't save data") | |
196 else: | |
197 import cPickle as pickle | |
198 dest_path = os.path.join(save_dir_path, ".hg_data") | |
199 try: | |
200 with open(dest_path, 'w') as f: | |
201 pickle.dump(hg_data, f, 2) | |
202 except IOError as e: | |
203 log.warning(u"Can't save file to {path}: {reason}".format( | |
204 path=dest_path, reason=e)) | |
205 else: | |
206 log.debug(u"repository data saved to {}".format(dest_path)) | |
207 | |
208 if as_string: | |
209 if not hg_data: | |
210 return u'repository data unknown' | |
211 strings = [u'rev', hg_data['node_short']] | |
212 try: | |
213 if hg_data['modified']: | |
214 strings.append(u"[M]") | |
215 except KeyError: | |
216 pass | |
217 try: | |
218 strings.extend([u'({branch} {date})'.format(**hg_data)]) | |
219 except KeyError: | |
220 pass | |
221 | |
222 return u' '.join(strings) | |
223 else: | |
224 return hg_data |