##// END OF EJS Templates
python3: fix usage of int/long
super-admin -
r4935:18be5f3a default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,458 +1,458 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 JSON RPC utils
23 23 """
24 24
25 25 import collections
26 26 import logging
27 27
28 28 from rhodecode.api.exc import JSONRPCError
29 29 from rhodecode.lib.auth import (
30 30 HasPermissionAnyApi, HasRepoPermissionAnyApi, HasRepoGroupPermissionAnyApi)
31 31 from rhodecode.lib.utils import safe_unicode
32 32 from rhodecode.lib.vcs.exceptions import RepositoryError
33 33 from rhodecode.lib.view_utils import get_commit_from_ref_name
34 34 from rhodecode.lib.utils2 import str2bool
35 35
36 36 log = logging.getLogger(__name__)
37 37
38 38
39 39 class OAttr(object):
40 40 """
41 41 Special Option that defines other attribute, and can default to them
42 42
43 43 Example::
44 44
45 45 def test(apiuser, userid=Optional(OAttr('apiuser')):
46 46 user = Optional.extract(userid, evaluate_locals=local())
47 47 #if we pass in userid, we get it, else it will default to apiuser
48 48 #attribute
49 49 """
50 50
51 51 def __init__(self, attr_name):
52 52 self.attr_name = attr_name
53 53
54 54 def __repr__(self):
55 55 return '<OptionalAttr:%s>' % self.attr_name
56 56
57 57 def __call__(self):
58 58 return self
59 59
60 60
61 61 class Optional(object):
62 62 """
63 63 Defines an optional parameter::
64 64
65 65 param = param.getval() if isinstance(param, Optional) else param
66 66 param = param() if isinstance(param, Optional) else param
67 67
68 68 is equivalent of::
69 69
70 70 param = Optional.extract(param)
71 71
72 72 """
73 73
74 74 def __init__(self, type_):
75 75 self.type_ = type_
76 76
77 77 def __repr__(self):
78 78 return '<Optional:%s>' % self.type_.__repr__()
79 79
80 80 def __call__(self):
81 81 return self.getval()
82 82
83 83 def getval(self, evaluate_locals=None):
84 84 """
85 85 returns value from this Optional instance
86 86 """
87 87 if isinstance(self.type_, OAttr):
88 88 param_name = self.type_.attr_name
89 89 if evaluate_locals:
90 90 return evaluate_locals[param_name]
91 91 # use params name
92 92 return param_name
93 93 return self.type_
94 94
95 95 @classmethod
96 96 def extract(cls, val, evaluate_locals=None, binary=None):
97 97 """
98 98 Extracts value from Optional() instance
99 99
100 100 :param val:
101 101 :return: original value if it's not Optional instance else
102 102 value of instance
103 103 """
104 104 if isinstance(val, cls):
105 105 val = val.getval(evaluate_locals)
106 106
107 107 if binary:
108 108 val = str2bool(val)
109 109
110 110 return val
111 111
112 112
113 113 def parse_args(cli_args, key_prefix=''):
114 114 from rhodecode.lib.utils2 import (escape_split)
115 115 kwargs = collections.defaultdict(dict)
116 116 for el in escape_split(cli_args, ','):
117 117 kv = escape_split(el, '=', 1)
118 118 if len(kv) == 2:
119 119 k, v = kv
120 120 kwargs[key_prefix + k] = v
121 121 return kwargs
122 122
123 123
124 124 def get_origin(obj):
125 125 """
126 126 Get origin of permission from object.
127 127
128 128 :param obj:
129 129 """
130 130 origin = 'permission'
131 131
132 132 if getattr(obj, 'owner_row', '') and getattr(obj, 'admin_row', ''):
133 133 # admin and owner case, maybe we should use dual string ?
134 134 origin = 'owner'
135 135 elif getattr(obj, 'owner_row', ''):
136 136 origin = 'owner'
137 137 elif getattr(obj, 'admin_row', ''):
138 138 origin = 'super-admin'
139 139 return origin
140 140
141 141
142 142 def store_update(updates, attr, name):
143 143 """
144 144 Stores param in updates dict if it's not instance of Optional
145 145 allows easy updates of passed in params
146 146 """
147 147 if not isinstance(attr, Optional):
148 148 updates[name] = attr
149 149
150 150
151 151 def has_superadmin_permission(apiuser):
152 152 """
153 153 Return True if apiuser is admin or return False
154 154
155 155 :param apiuser:
156 156 """
157 157 if HasPermissionAnyApi('hg.admin')(user=apiuser):
158 158 return True
159 159 return False
160 160
161 161
162 162 def validate_repo_permissions(apiuser, repoid, repo, perms):
163 163 """
164 164 Raise JsonRPCError if apiuser is not authorized or return True
165 165
166 166 :param apiuser:
167 167 :param repoid:
168 168 :param repo:
169 169 :param perms:
170 170 """
171 171 if not HasRepoPermissionAnyApi(*perms)(
172 172 user=apiuser, repo_name=repo.repo_name):
173 173 raise JSONRPCError('repository `%s` does not exist' % repoid)
174 174
175 175 return True
176 176
177 177
178 178 def validate_repo_group_permissions(apiuser, repogroupid, repo_group, perms):
179 179 """
180 180 Raise JsonRPCError if apiuser is not authorized or return True
181 181
182 182 :param apiuser:
183 183 :param repogroupid: just the id of repository group
184 184 :param repo_group: instance of repo_group
185 185 :param perms:
186 186 """
187 187 if not HasRepoGroupPermissionAnyApi(*perms)(
188 188 user=apiuser, group_name=repo_group.group_name):
189 189 raise JSONRPCError(
190 190 'repository group `%s` does not exist' % repogroupid)
191 191
192 192 return True
193 193
194 194
195 195 def validate_set_owner_permissions(apiuser, owner):
196 196 if isinstance(owner, Optional):
197 197 owner = get_user_or_error(apiuser.user_id)
198 198 else:
199 199 if has_superadmin_permission(apiuser):
200 200 owner = get_user_or_error(owner)
201 201 else:
202 202 # forbid setting owner for non-admins
203 203 raise JSONRPCError(
204 204 'Only RhodeCode super-admin can specify `owner` param')
205 205 return owner
206 206
207 207
208 208 def get_user_or_error(userid):
209 209 """
210 210 Get user by id or name or return JsonRPCError if not found
211 211
212 212 :param userid:
213 213 """
214 214 from rhodecode.model.user import UserModel
215 215 user_model = UserModel()
216 216
217 if isinstance(userid, (int, long)):
217 if isinstance(userid, int):
218 218 try:
219 219 user = user_model.get_user(userid)
220 220 except ValueError:
221 221 user = None
222 222 else:
223 223 user = user_model.get_by_username(userid)
224 224
225 225 if user is None:
226 226 raise JSONRPCError(
227 227 'user `%s` does not exist' % (userid,))
228 228 return user
229 229
230 230
231 231 def get_repo_or_error(repoid):
232 232 """
233 233 Get repo by id or name or return JsonRPCError if not found
234 234
235 235 :param repoid:
236 236 """
237 237 from rhodecode.model.repo import RepoModel
238 238 repo_model = RepoModel()
239 239
240 if isinstance(repoid, (int, long)):
240 if isinstance(repoid, int):
241 241 try:
242 242 repo = repo_model.get_repo(repoid)
243 243 except ValueError:
244 244 repo = None
245 245 else:
246 246 repo = repo_model.get_by_repo_name(repoid)
247 247
248 248 if repo is None:
249 249 raise JSONRPCError(
250 250 'repository `%s` does not exist' % (repoid,))
251 251 return repo
252 252
253 253
254 254 def get_repo_group_or_error(repogroupid):
255 255 """
256 256 Get repo group by id or name or return JsonRPCError if not found
257 257
258 258 :param repogroupid:
259 259 """
260 260 from rhodecode.model.repo_group import RepoGroupModel
261 261 repo_group_model = RepoGroupModel()
262 262
263 if isinstance(repogroupid, (int, long)):
263 if isinstance(repogroupid, int):
264 264 try:
265 265 repo_group = repo_group_model._get_repo_group(repogroupid)
266 266 except ValueError:
267 267 repo_group = None
268 268 else:
269 269 repo_group = repo_group_model.get_by_group_name(repogroupid)
270 270
271 271 if repo_group is None:
272 272 raise JSONRPCError(
273 273 'repository group `%s` does not exist' % (repogroupid,))
274 274 return repo_group
275 275
276 276
277 277 def get_user_group_or_error(usergroupid):
278 278 """
279 279 Get user group by id or name or return JsonRPCError if not found
280 280
281 281 :param usergroupid:
282 282 """
283 283 from rhodecode.model.user_group import UserGroupModel
284 284 user_group_model = UserGroupModel()
285 285
286 if isinstance(usergroupid, (int, long)):
286 if isinstance(usergroupid, int):
287 287 try:
288 288 user_group = user_group_model.get_group(usergroupid)
289 289 except ValueError:
290 290 user_group = None
291 291 else:
292 292 user_group = user_group_model.get_by_name(usergroupid)
293 293
294 294 if user_group is None:
295 295 raise JSONRPCError(
296 296 'user group `%s` does not exist' % (usergroupid,))
297 297 return user_group
298 298
299 299
300 300 def get_perm_or_error(permid, prefix=None):
301 301 """
302 302 Get permission by id or name or return JsonRPCError if not found
303 303
304 304 :param permid:
305 305 """
306 306 from rhodecode.model.permission import PermissionModel
307 307
308 308 perm = PermissionModel.cls.get_by_key(permid)
309 309 if perm is None:
310 310 msg = 'permission `{}` does not exist.'.format(permid)
311 311 if prefix:
312 312 msg += ' Permission should start with prefix: `{}`'.format(prefix)
313 313 raise JSONRPCError(msg)
314 314
315 315 if prefix:
316 316 if not perm.permission_name.startswith(prefix):
317 317 raise JSONRPCError('permission `%s` is invalid, '
318 318 'should start with %s' % (permid, prefix))
319 319 return perm
320 320
321 321
322 322 def get_gist_or_error(gistid):
323 323 """
324 324 Get gist by id or gist_access_id or return JsonRPCError if not found
325 325
326 326 :param gistid:
327 327 """
328 328 from rhodecode.model.gist import GistModel
329 329
330 330 gist = GistModel.cls.get_by_access_id(gistid)
331 331 if gist is None:
332 332 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
333 333 return gist
334 334
335 335
336 336 def get_pull_request_or_error(pullrequestid):
337 337 """
338 338 Get pull request by id or return JsonRPCError if not found
339 339
340 340 :param pullrequestid:
341 341 """
342 342 from rhodecode.model.pull_request import PullRequestModel
343 343
344 344 try:
345 345 pull_request = PullRequestModel().get(int(pullrequestid))
346 346 except ValueError:
347 347 raise JSONRPCError('pullrequestid must be an integer')
348 348 if not pull_request:
349 349 raise JSONRPCError('pull request `%s` does not exist' % (
350 350 pullrequestid,))
351 351 return pull_request
352 352
353 353
354 354 def build_commit_data(rhodecode_vcs_repo, commit, detail_level):
355 355 commit2 = commit
356 356 commit1 = commit.first_parent
357 357
358 358 parsed_diff = []
359 359 if detail_level == 'extended':
360 360 for f_path in commit.added_paths:
361 361 parsed_diff.append(_get_commit_dict(filename=f_path, op='A'))
362 362 for f_path in commit.changed_paths:
363 363 parsed_diff.append(_get_commit_dict(filename=f_path, op='M'))
364 364 for f_path in commit.removed_paths:
365 365 parsed_diff.append(_get_commit_dict(filename=f_path, op='D'))
366 366
367 367 elif detail_level == 'full':
368 368 from rhodecode.lib import diffs
369 369
370 370 _diff = rhodecode_vcs_repo.get_diff(commit1, commit2,)
371 371 diff_processor = diffs.DiffProcessor(_diff, format='newdiff', show_full_diff=True)
372 372
373 373 for dp in diff_processor.prepare():
374 374 del dp['stats']['ops']
375 375 _stats = dp['stats']
376 376 parsed_diff.append(_get_commit_dict(
377 377 filename=dp['filename'], op=dp['operation'],
378 378 new_revision=dp['new_revision'],
379 379 old_revision=dp['old_revision'],
380 380 raw_diff=dp['raw_diff'], stats=_stats))
381 381
382 382 return parsed_diff
383 383
384 384
385 385 def get_commit_or_error(ref, repo):
386 386 try:
387 387 ref_type, _, ref_hash = ref.split(':')
388 388 except ValueError:
389 389 raise JSONRPCError(
390 390 'Ref `{ref}` given in a wrong format. Please check the API'
391 391 ' documentation for more details'.format(ref=ref))
392 392 try:
393 393 # TODO: dan: refactor this to use repo.scm_instance().get_commit()
394 394 # once get_commit supports ref_types
395 395 return get_commit_from_ref_name(repo, ref_hash)
396 396 except RepositoryError:
397 397 raise JSONRPCError('Ref `{ref}` does not exist'.format(ref=ref))
398 398
399 399
400 400 def _get_ref_hash(repo, type_, name):
401 401 vcs_repo = repo.scm_instance()
402 402 if type_ in ['branch'] and vcs_repo.alias in ('hg', 'git'):
403 403 return vcs_repo.branches[name]
404 404 elif type_ in ['bookmark', 'book'] and vcs_repo.alias == 'hg':
405 405 return vcs_repo.bookmarks[name]
406 406 else:
407 407 raise ValueError()
408 408
409 409
410 410 def resolve_ref_or_error(ref, repo, allowed_ref_types=None):
411 411 allowed_ref_types = allowed_ref_types or ['bookmark', 'book', 'tag', 'branch']
412 412
413 413 def _parse_ref(type_, name, hash_=None):
414 414 return type_, name, hash_
415 415
416 416 try:
417 417 ref_type, ref_name, ref_hash = _parse_ref(*ref.split(':'))
418 418 except TypeError:
419 419 raise JSONRPCError(
420 420 'Ref `{ref}` given in a wrong format. Please check the API'
421 421 ' documentation for more details'.format(ref=ref))
422 422
423 423 if ref_type not in allowed_ref_types:
424 424 raise JSONRPCError(
425 425 'Ref `{ref}` type is not allowed. '
426 426 'Only:{allowed_refs} are possible.'.format(
427 427 ref=ref, allowed_refs=allowed_ref_types))
428 428
429 429 try:
430 430 ref_hash = ref_hash or _get_ref_hash(repo, ref_type, ref_name)
431 431 except (KeyError, ValueError):
432 432 raise JSONRPCError(
433 433 'The specified value:{type}:`{name}` does not exist, or is not allowed.'.format(
434 434 type=ref_type, name=ref_name))
435 435
436 436 return ':'.join([ref_type, ref_name, ref_hash])
437 437
438 438
439 439 def _get_commit_dict(
440 440 filename, op, new_revision=None, old_revision=None,
441 441 raw_diff=None, stats=None):
442 442 if stats is None:
443 443 stats = {
444 444 "added": None,
445 445 "binary": None,
446 446 "deleted": None
447 447 }
448 448 return {
449 449 "filename": safe_unicode(filename),
450 450 "op": op,
451 451
452 452 # extra details
453 453 "new_revision": new_revision,
454 454 "old_revision": old_revision,
455 455
456 456 "raw_diff": raw_diff,
457 457 "stats": stats
458 458 }
@@ -1,818 +1,818 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Authentication modules
23 23 """
24 24 import socket
25 25 import string
26 26 import colander
27 27 import copy
28 28 import logging
29 29 import time
30 30 import traceback
31 31 import warnings
32 32 import functools
33 33
34 34 from pyramid.threadlocal import get_current_registry
35 35
36 36 from rhodecode.authentication.interface import IAuthnPluginRegistry
37 37 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
38 38 from rhodecode.lib import rc_cache
39 39 from rhodecode.lib.statsd_client import StatsdClient
40 40 from rhodecode.lib.auth import PasswordGenerator, _RhodeCodeCryptoBCrypt
41 41 from rhodecode.lib.utils2 import safe_int, safe_str
42 42 from rhodecode.lib.exceptions import (LdapConnectionError, LdapUsernameError, LdapPasswordError)
43 43 from rhodecode.model.db import User
44 44 from rhodecode.model.meta import Session
45 45 from rhodecode.model.settings import SettingsModel
46 46 from rhodecode.model.user import UserModel
47 47 from rhodecode.model.user_group import UserGroupModel
48 48
49 49
50 50 log = logging.getLogger(__name__)
51 51
52 52 # auth types that authenticate() function can receive
53 53 VCS_TYPE = 'vcs'
54 54 HTTP_TYPE = 'http'
55 55
56 56 external_auth_session_key = 'rhodecode.external_auth'
57 57
58 58
59 59 class hybrid_property(object):
60 60 """
61 61 a property decorator that works both for instance and class
62 62 """
63 63 def __init__(self, fget, fset=None, fdel=None, expr=None):
64 64 self.fget = fget
65 65 self.fset = fset
66 66 self.fdel = fdel
67 67 self.expr = expr or fget
68 68 functools.update_wrapper(self, fget)
69 69
70 70 def __get__(self, instance, owner):
71 71 if instance is None:
72 72 return self.expr(owner)
73 73 else:
74 74 return self.fget(instance)
75 75
76 76 def __set__(self, instance, value):
77 77 self.fset(instance, value)
78 78
79 79 def __delete__(self, instance):
80 80 self.fdel(instance)
81 81
82 82
83 83 class LazyFormencode(object):
84 84 def __init__(self, formencode_obj, *args, **kwargs):
85 85 self.formencode_obj = formencode_obj
86 86 self.args = args
87 87 self.kwargs = kwargs
88 88
89 89 def __call__(self, *args, **kwargs):
90 90 from inspect import isfunction
91 91 formencode_obj = self.formencode_obj
92 92 if isfunction(formencode_obj):
93 93 # case we wrap validators into functions
94 94 formencode_obj = self.formencode_obj(*args, **kwargs)
95 95 return formencode_obj(*self.args, **self.kwargs)
96 96
97 97
98 98 class RhodeCodeAuthPluginBase(object):
99 99 # UID is used to register plugin to the registry
100 100 uid = None
101 101
102 102 # cache the authentication request for N amount of seconds. Some kind
103 103 # of authentication methods are very heavy and it's very efficient to cache
104 104 # the result of a call. If it's set to None (default) cache is off
105 105 AUTH_CACHE_TTL = None
106 106 AUTH_CACHE = {}
107 107
108 108 auth_func_attrs = {
109 109 "username": "unique username",
110 110 "firstname": "first name",
111 111 "lastname": "last name",
112 112 "email": "email address",
113 113 "groups": '["list", "of", "groups"]',
114 114 "user_group_sync":
115 115 'True|False defines if returned user groups should be synced',
116 116 "extern_name": "name in external source of record",
117 117 "extern_type": "type of external source of record",
118 118 "admin": 'True|False defines if user should be RhodeCode super admin',
119 119 "active":
120 120 'True|False defines active state of user internally for RhodeCode',
121 121 "active_from_extern":
122 122 "True|False|None, active state from the external auth, "
123 123 "None means use definition from RhodeCode extern_type active value"
124 124
125 125 }
126 126 # set on authenticate() method and via set_auth_type func.
127 127 auth_type = None
128 128
129 129 # set on authenticate() method and via set_calling_scope_repo, this is a
130 130 # calling scope repository when doing authentication most likely on VCS
131 131 # operations
132 132 acl_repo_name = None
133 133
134 134 # List of setting names to store encrypted. Plugins may override this list
135 135 # to store settings encrypted.
136 136 _settings_encrypted = []
137 137
138 138 # Mapping of python to DB settings model types. Plugins may override or
139 139 # extend this mapping.
140 140 _settings_type_map = {
141 141 colander.String: 'unicode',
142 142 colander.Integer: 'int',
143 143 colander.Boolean: 'bool',
144 144 colander.List: 'list',
145 145 }
146 146
147 147 # list of keys in settings that are unsafe to be logged, should be passwords
148 148 # or other crucial credentials
149 149 _settings_unsafe_keys = []
150 150
151 151 def __init__(self, plugin_id):
152 152 self._plugin_id = plugin_id
153 153
154 154 def __str__(self):
155 155 return self.get_id()
156 156
157 157 def _get_setting_full_name(self, name):
158 158 """
159 159 Return the full setting name used for storing values in the database.
160 160 """
161 161 # TODO: johbo: Using the name here is problematic. It would be good to
162 162 # introduce either new models in the database to hold Plugin and
163 163 # PluginSetting or to use the plugin id here.
164 164 return 'auth_{}_{}'.format(self.name, name)
165 165
166 166 def _get_setting_type(self, name):
167 167 """
168 168 Return the type of a setting. This type is defined by the SettingsModel
169 169 and determines how the setting is stored in DB. Optionally the suffix
170 170 `.encrypted` is appended to instruct SettingsModel to store it
171 171 encrypted.
172 172 """
173 173 schema_node = self.get_settings_schema().get(name)
174 174 db_type = self._settings_type_map.get(
175 175 type(schema_node.typ), 'unicode')
176 176 if name in self._settings_encrypted:
177 177 db_type = '{}.encrypted'.format(db_type)
178 178 return db_type
179 179
180 180 @classmethod
181 181 def docs(cls):
182 182 """
183 183 Defines documentation url which helps with plugin setup
184 184 """
185 185 return ''
186 186
187 187 @classmethod
188 188 def icon(cls):
189 189 """
190 190 Defines ICON in SVG format for authentication method
191 191 """
192 192 return ''
193 193
194 194 def is_enabled(self):
195 195 """
196 196 Returns true if this plugin is enabled. An enabled plugin can be
197 197 configured in the admin interface but it is not consulted during
198 198 authentication.
199 199 """
200 200 auth_plugins = SettingsModel().get_auth_plugins()
201 201 return self.get_id() in auth_plugins
202 202
203 203 def is_active(self, plugin_cached_settings=None):
204 204 """
205 205 Returns true if the plugin is activated. An activated plugin is
206 206 consulted during authentication, assumed it is also enabled.
207 207 """
208 208 return self.get_setting_by_name(
209 209 'enabled', plugin_cached_settings=plugin_cached_settings)
210 210
211 211 def get_id(self):
212 212 """
213 213 Returns the plugin id.
214 214 """
215 215 return self._plugin_id
216 216
217 217 def get_display_name(self, load_from_settings=False):
218 218 """
219 219 Returns a translation string for displaying purposes.
220 220 if load_from_settings is set, plugin settings can override the display name
221 221 """
222 222 raise NotImplementedError('Not implemented in base class')
223 223
224 224 def get_settings_schema(self):
225 225 """
226 226 Returns a colander schema, representing the plugin settings.
227 227 """
228 228 return AuthnPluginSettingsSchemaBase()
229 229
230 230 def _propagate_settings(self, raw_settings):
231 231 settings = {}
232 232 for node in self.get_settings_schema():
233 233 settings[node.name] = self.get_setting_by_name(
234 234 node.name, plugin_cached_settings=raw_settings)
235 235 return settings
236 236
237 237 def get_settings(self, use_cache=True):
238 238 """
239 239 Returns the plugin settings as dictionary.
240 240 """
241 241
242 242 raw_settings = SettingsModel().get_all_settings(cache=use_cache)
243 243 settings = self._propagate_settings(raw_settings)
244 244
245 245 return settings
246 246
247 247 def get_setting_by_name(self, name, default=None, plugin_cached_settings=None):
248 248 """
249 249 Returns a plugin setting by name.
250 250 """
251 251 full_name = 'rhodecode_{}'.format(self._get_setting_full_name(name))
252 252 if plugin_cached_settings:
253 253 plugin_settings = plugin_cached_settings
254 254 else:
255 255 plugin_settings = SettingsModel().get_all_settings()
256 256
257 257 if full_name in plugin_settings:
258 258 return plugin_settings[full_name]
259 259 else:
260 260 return default
261 261
262 262 def create_or_update_setting(self, name, value):
263 263 """
264 264 Create or update a setting for this plugin in the persistent storage.
265 265 """
266 266 full_name = self._get_setting_full_name(name)
267 267 type_ = self._get_setting_type(name)
268 268 db_setting = SettingsModel().create_or_update_setting(
269 269 full_name, value, type_)
270 270 return db_setting.app_settings_value
271 271
272 272 def log_safe_settings(self, settings):
273 273 """
274 274 returns a log safe representation of settings, without any secrets
275 275 """
276 276 settings_copy = copy.deepcopy(settings)
277 277 for k in self._settings_unsafe_keys:
278 278 if k in settings_copy:
279 279 del settings_copy[k]
280 280 return settings_copy
281 281
282 282 @hybrid_property
283 283 def name(self):
284 284 """
285 285 Returns the name of this authentication plugin.
286 286
287 287 :returns: string
288 288 """
289 289 raise NotImplementedError("Not implemented in base class")
290 290
291 291 def get_url_slug(self):
292 292 """
293 293 Returns a slug which should be used when constructing URLs which refer
294 294 to this plugin. By default it returns the plugin name. If the name is
295 295 not suitable for using it in an URL the plugin should override this
296 296 method.
297 297 """
298 298 return self.name
299 299
300 300 @property
301 301 def is_headers_auth(self):
302 302 """
303 303 Returns True if this authentication plugin uses HTTP headers as
304 304 authentication method.
305 305 """
306 306 return False
307 307
308 308 @hybrid_property
309 309 def is_container_auth(self):
310 310 """
311 311 Deprecated method that indicates if this authentication plugin uses
312 312 HTTP headers as authentication method.
313 313 """
314 314 warnings.warn(
315 315 'Use is_headers_auth instead.', category=DeprecationWarning)
316 316 return self.is_headers_auth
317 317
318 318 @hybrid_property
319 319 def allows_creating_users(self):
320 320 """
321 321 Defines if Plugin allows users to be created on-the-fly when
322 322 authentication is called. Controls how external plugins should behave
323 323 in terms if they are allowed to create new users, or not. Base plugins
324 324 should not be allowed to, but External ones should be !
325 325
326 326 :return: bool
327 327 """
328 328 return False
329 329
330 330 def set_auth_type(self, auth_type):
331 331 self.auth_type = auth_type
332 332
333 333 def set_calling_scope_repo(self, acl_repo_name):
334 334 self.acl_repo_name = acl_repo_name
335 335
336 336 def allows_authentication_from(
337 337 self, user, allows_non_existing_user=True,
338 338 allowed_auth_plugins=None, allowed_auth_sources=None):
339 339 """
340 340 Checks if this authentication module should accept a request for
341 341 the current user.
342 342
343 343 :param user: user object fetched using plugin's get_user() method.
344 344 :param allows_non_existing_user: if True, don't allow the
345 345 user to be empty, meaning not existing in our database
346 346 :param allowed_auth_plugins: if provided, users extern_type will be
347 347 checked against a list of provided extern types, which are plugin
348 348 auth_names in the end
349 349 :param allowed_auth_sources: authentication type allowed,
350 350 `http` or `vcs` default is both.
351 351 defines if plugin will accept only http authentication vcs
352 352 authentication(git/hg) or both
353 353 :returns: boolean
354 354 """
355 355 if not user and not allows_non_existing_user:
356 356 log.debug('User is empty but plugin does not allow empty users,'
357 357 'not allowed to authenticate')
358 358 return False
359 359
360 360 expected_auth_plugins = allowed_auth_plugins or [self.name]
361 361 if user and (user.extern_type and
362 362 user.extern_type not in expected_auth_plugins):
363 363 log.debug(
364 364 'User `%s` is bound to `%s` auth type. Plugin allows only '
365 365 '%s, skipping', user, user.extern_type, expected_auth_plugins)
366 366
367 367 return False
368 368
369 369 # by default accept both
370 370 expected_auth_from = allowed_auth_sources or [HTTP_TYPE, VCS_TYPE]
371 371 if self.auth_type not in expected_auth_from:
372 372 log.debug('Current auth source is %s but plugin only allows %s',
373 373 self.auth_type, expected_auth_from)
374 374 return False
375 375
376 376 return True
377 377
378 378 def get_user(self, username=None, **kwargs):
379 379 """
380 380 Helper method for user fetching in plugins, by default it's using
381 381 simple fetch by username, but this method can be custimized in plugins
382 382 eg. headers auth plugin to fetch user by environ params
383 383
384 384 :param username: username if given to fetch from database
385 385 :param kwargs: extra arguments needed for user fetching.
386 386 """
387 387 user = None
388 388 log.debug(
389 389 'Trying to fetch user `%s` from RhodeCode database', username)
390 390 if username:
391 391 user = User.get_by_username(username)
392 392 if not user:
393 393 log.debug('User not found, fallback to fetch user in '
394 394 'case insensitive mode')
395 395 user = User.get_by_username(username, case_insensitive=True)
396 396 else:
397 397 log.debug('provided username:`%s` is empty skipping...', username)
398 398 if not user:
399 399 log.debug('User `%s` not found in database', username)
400 400 else:
401 401 log.debug('Got DB user:%s', user)
402 402 return user
403 403
404 404 def user_activation_state(self):
405 405 """
406 406 Defines user activation state when creating new users
407 407
408 408 :returns: boolean
409 409 """
410 410 raise NotImplementedError("Not implemented in base class")
411 411
412 412 def auth(self, userobj, username, passwd, settings, **kwargs):
413 413 """
414 414 Given a user object (which may be null), username, a plaintext
415 415 password, and a settings object (containing all the keys needed as
416 416 listed in settings()), authenticate this user's login attempt.
417 417
418 418 Return None on failure. On success, return a dictionary of the form:
419 419
420 420 see: RhodeCodeAuthPluginBase.auth_func_attrs
421 421 This is later validated for correctness
422 422 """
423 423 raise NotImplementedError("not implemented in base class")
424 424
425 425 def _authenticate(self, userobj, username, passwd, settings, **kwargs):
426 426 """
427 427 Wrapper to call self.auth() that validates call on it
428 428
429 429 :param userobj: userobj
430 430 :param username: username
431 431 :param passwd: plaintext password
432 432 :param settings: plugin settings
433 433 """
434 434 auth = self.auth(userobj, username, passwd, settings, **kwargs)
435 435 if auth:
436 436 auth['_plugin'] = self.name
437 437 auth['_ttl_cache'] = self.get_ttl_cache(settings)
438 438 # check if hash should be migrated ?
439 439 new_hash = auth.get('_hash_migrate')
440 440 if new_hash:
441 441 self._migrate_hash_to_bcrypt(username, passwd, new_hash)
442 442 if 'user_group_sync' not in auth:
443 443 auth['user_group_sync'] = False
444 444 return self._validate_auth_return(auth)
445 445 return auth
446 446
447 447 def _migrate_hash_to_bcrypt(self, username, password, new_hash):
448 448 new_hash_cypher = _RhodeCodeCryptoBCrypt()
449 449 # extra checks, so make sure new hash is correct.
450 450 password_encoded = safe_str(password)
451 451 if new_hash and new_hash_cypher.hash_check(
452 452 password_encoded, new_hash):
453 453 cur_user = User.get_by_username(username)
454 454 cur_user.password = new_hash
455 455 Session().add(cur_user)
456 456 Session().flush()
457 457 log.info('Migrated user %s hash to bcrypt', cur_user)
458 458
459 459 def _validate_auth_return(self, ret):
460 460 if not isinstance(ret, dict):
461 461 raise Exception('returned value from auth must be a dict')
462 462 for k in self.auth_func_attrs:
463 463 if k not in ret:
464 464 raise Exception('Missing %s attribute from returned data' % k)
465 465 return ret
466 466
467 467 def get_ttl_cache(self, settings=None):
468 468 plugin_settings = settings or self.get_settings()
469 469 # we set default to 30, we make a compromise here,
470 470 # performance > security, mostly due to LDAP/SVN, majority
471 471 # of users pick cache_ttl to be enabled
472 472 from rhodecode.authentication import plugin_default_auth_ttl
473 473 cache_ttl = plugin_default_auth_ttl
474 474
475 if isinstance(self.AUTH_CACHE_TTL, (int, long)):
475 if isinstance(self.AUTH_CACHE_TTL, int):
476 476 # plugin cache set inside is more important than the settings value
477 477 cache_ttl = self.AUTH_CACHE_TTL
478 478 elif plugin_settings.get('cache_ttl'):
479 479 cache_ttl = safe_int(plugin_settings.get('cache_ttl'), 0)
480 480
481 481 plugin_cache_active = bool(cache_ttl and cache_ttl > 0)
482 482 return plugin_cache_active, cache_ttl
483 483
484 484
485 485 class RhodeCodeExternalAuthPlugin(RhodeCodeAuthPluginBase):
486 486
487 487 @hybrid_property
488 488 def allows_creating_users(self):
489 489 return True
490 490
491 491 def use_fake_password(self):
492 492 """
493 493 Return a boolean that indicates whether or not we should set the user's
494 494 password to a random value when it is authenticated by this plugin.
495 495 If your plugin provides authentication, then you will generally
496 496 want this.
497 497
498 498 :returns: boolean
499 499 """
500 500 raise NotImplementedError("Not implemented in base class")
501 501
502 502 def _authenticate(self, userobj, username, passwd, settings, **kwargs):
503 503 # at this point _authenticate calls plugin's `auth()` function
504 504 auth = super(RhodeCodeExternalAuthPlugin, self)._authenticate(
505 505 userobj, username, passwd, settings, **kwargs)
506 506
507 507 if auth:
508 508 # maybe plugin will clean the username ?
509 509 # we should use the return value
510 510 username = auth['username']
511 511
512 512 # if external source tells us that user is not active, we should
513 513 # skip rest of the process. This can prevent from creating users in
514 514 # RhodeCode when using external authentication, but if it's
515 515 # inactive user we shouldn't create that user anyway
516 516 if auth['active_from_extern'] is False:
517 517 log.warning(
518 518 "User %s authenticated against %s, but is inactive",
519 519 username, self.__module__)
520 520 return None
521 521
522 522 cur_user = User.get_by_username(username, case_insensitive=True)
523 523 is_user_existing = cur_user is not None
524 524
525 525 if is_user_existing:
526 526 log.debug('Syncing user `%s` from '
527 527 '`%s` plugin', username, self.name)
528 528 else:
529 529 log.debug('Creating non existing user `%s` from '
530 530 '`%s` plugin', username, self.name)
531 531
532 532 if self.allows_creating_users:
533 533 log.debug('Plugin `%s` allows to '
534 534 'create new users', self.name)
535 535 else:
536 536 log.debug('Plugin `%s` does not allow to '
537 537 'create new users', self.name)
538 538
539 539 user_parameters = {
540 540 'username': username,
541 541 'email': auth["email"],
542 542 'firstname': auth["firstname"],
543 543 'lastname': auth["lastname"],
544 544 'active': auth["active"],
545 545 'admin': auth["admin"],
546 546 'extern_name': auth["extern_name"],
547 547 'extern_type': self.name,
548 548 'plugin': self,
549 549 'allow_to_create_user': self.allows_creating_users,
550 550 }
551 551
552 552 if not is_user_existing:
553 553 if self.use_fake_password():
554 554 # Randomize the PW because we don't need it, but don't want
555 555 # them blank either
556 556 passwd = PasswordGenerator().gen_password(length=16)
557 557 user_parameters['password'] = passwd
558 558 else:
559 559 # Since the password is required by create_or_update method of
560 560 # UserModel, we need to set it explicitly.
561 561 # The create_or_update method is smart and recognises the
562 562 # password hashes as well.
563 563 user_parameters['password'] = cur_user.password
564 564
565 565 # we either create or update users, we also pass the flag
566 566 # that controls if this method can actually do that.
567 567 # raises NotAllowedToCreateUserError if it cannot, and we try to.
568 568 user = UserModel().create_or_update(**user_parameters)
569 569 Session().flush()
570 570 # enforce user is just in given groups, all of them has to be ones
571 571 # created from plugins. We store this info in _group_data JSON
572 572 # field
573 573
574 574 if auth['user_group_sync']:
575 575 try:
576 576 groups = auth['groups'] or []
577 577 log.debug(
578 578 'Performing user_group sync based on set `%s` '
579 579 'returned by `%s` plugin', groups, self.name)
580 580 UserGroupModel().enforce_groups(user, groups, self.name)
581 581 except Exception:
582 582 # for any reason group syncing fails, we should
583 583 # proceed with login
584 584 log.error(traceback.format_exc())
585 585
586 586 Session().commit()
587 587 return auth
588 588
589 589
590 590 class AuthLdapBase(object):
591 591
592 592 @classmethod
593 593 def _build_servers(cls, ldap_server_type, ldap_server, port, use_resolver=True):
594 594
595 595 def host_resolver(host, port, full_resolve=True):
596 596 """
597 597 Main work for this function is to prevent ldap connection issues,
598 598 and detect them early using a "greenified" sockets
599 599 """
600 600 host = host.strip()
601 601 if not full_resolve:
602 602 return '{}:{}'.format(host, port)
603 603
604 604 log.debug('LDAP: Resolving IP for LDAP host `%s`', host)
605 605 try:
606 606 ip = socket.gethostbyname(host)
607 607 log.debug('LDAP: Got LDAP host `%s` ip %s', host, ip)
608 608 except Exception:
609 609 raise LdapConnectionError('Failed to resolve host: `{}`'.format(host))
610 610
611 611 log.debug('LDAP: Checking if IP %s is accessible', ip)
612 612 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
613 613 try:
614 614 s.connect((ip, int(port)))
615 615 s.shutdown(socket.SHUT_RD)
616 616 log.debug('LDAP: connection to %s successful', ip)
617 617 except Exception:
618 618 raise LdapConnectionError(
619 619 'Failed to connect to host: `{}:{}`'.format(host, port))
620 620
621 621 return '{}:{}'.format(host, port)
622 622
623 623 if len(ldap_server) == 1:
624 624 # in case of single server use resolver to detect potential
625 625 # connection issues
626 626 full_resolve = True
627 627 else:
628 628 full_resolve = False
629 629
630 630 return ', '.join(
631 631 ["{}://{}".format(
632 632 ldap_server_type,
633 633 host_resolver(host, port, full_resolve=use_resolver and full_resolve))
634 634 for host in ldap_server])
635 635
636 636 @classmethod
637 637 def _get_server_list(cls, servers):
638 638 return map(string.strip, servers.split(','))
639 639
640 640 @classmethod
641 641 def get_uid(cls, username, server_addresses):
642 642 uid = username
643 643 for server_addr in server_addresses:
644 644 uid = chop_at(username, "@%s" % server_addr)
645 645 return uid
646 646
647 647 @classmethod
648 648 def validate_username(cls, username):
649 649 if "," in username:
650 650 raise LdapUsernameError(
651 651 "invalid character `,` in username: `{}`".format(username))
652 652
653 653 @classmethod
654 654 def validate_password(cls, username, password):
655 655 if not password:
656 656 msg = "Authenticating user %s with blank password not allowed"
657 657 log.warning(msg, username)
658 658 raise LdapPasswordError(msg)
659 659
660 660
661 661 def loadplugin(plugin_id):
662 662 """
663 663 Loads and returns an instantiated authentication plugin.
664 664 Returns the RhodeCodeAuthPluginBase subclass on success,
665 665 or None on failure.
666 666 """
667 667 # TODO: Disusing pyramids thread locals to retrieve the registry.
668 668 authn_registry = get_authn_registry()
669 669 plugin = authn_registry.get_plugin(plugin_id)
670 670 if plugin is None:
671 671 log.error('Authentication plugin not found: "%s"', plugin_id)
672 672 return plugin
673 673
674 674
675 675 def get_authn_registry(registry=None):
676 676 registry = registry or get_current_registry()
677 677 authn_registry = registry.queryUtility(IAuthnPluginRegistry)
678 678 return authn_registry
679 679
680 680
681 681 def authenticate(username, password, environ=None, auth_type=None,
682 682 skip_missing=False, registry=None, acl_repo_name=None):
683 683 """
684 684 Authentication function used for access control,
685 685 It tries to authenticate based on enabled authentication modules.
686 686
687 687 :param username: username can be empty for headers auth
688 688 :param password: password can be empty for headers auth
689 689 :param environ: environ headers passed for headers auth
690 690 :param auth_type: type of authentication, either `HTTP_TYPE` or `VCS_TYPE`
691 691 :param skip_missing: ignores plugins that are in db but not in environment
692 692 :returns: None if auth failed, plugin_user dict if auth is correct
693 693 """
694 694 if not auth_type or auth_type not in [HTTP_TYPE, VCS_TYPE]:
695 695 raise ValueError('auth type must be on of http, vcs got "%s" instead'
696 696 % auth_type)
697 697 headers_only = environ and not (username and password)
698 698
699 699 authn_registry = get_authn_registry(registry)
700 700
701 701 plugins_to_check = authn_registry.get_plugins_for_authentication()
702 702 log.debug('Starting ordered authentication chain using %s plugins',
703 703 [x.name for x in plugins_to_check])
704 704 for plugin in plugins_to_check:
705 705 plugin.set_auth_type(auth_type)
706 706 plugin.set_calling_scope_repo(acl_repo_name)
707 707
708 708 if headers_only and not plugin.is_headers_auth:
709 709 log.debug('Auth type is for headers only and plugin `%s` is not '
710 710 'headers plugin, skipping...', plugin.get_id())
711 711 continue
712 712
713 713 log.debug('Trying authentication using ** %s **', plugin.get_id())
714 714
715 715 # load plugin settings from RhodeCode database
716 716 plugin_settings = plugin.get_settings()
717 717 plugin_sanitized_settings = plugin.log_safe_settings(plugin_settings)
718 718 log.debug('Plugin `%s` settings:%s', plugin.get_id(), plugin_sanitized_settings)
719 719
720 720 # use plugin's method of user extraction.
721 721 user = plugin.get_user(username, environ=environ,
722 722 settings=plugin_settings)
723 723 display_user = user.username if user else username
724 724 log.debug(
725 725 'Plugin %s extracted user is `%s`', plugin.get_id(), display_user)
726 726
727 727 if not plugin.allows_authentication_from(user):
728 728 log.debug('Plugin %s does not accept user `%s` for authentication',
729 729 plugin.get_id(), display_user)
730 730 continue
731 731 else:
732 732 log.debug('Plugin %s accepted user `%s` for authentication',
733 733 plugin.get_id(), display_user)
734 734
735 735 log.info('Authenticating user `%s` using %s plugin',
736 736 display_user, plugin.get_id())
737 737
738 738 plugin_cache_active, cache_ttl = plugin.get_ttl_cache(plugin_settings)
739 739
740 740 log.debug('AUTH_CACHE_TTL for plugin `%s` active: %s (TTL: %s)',
741 741 plugin.get_id(), plugin_cache_active, cache_ttl)
742 742
743 743 user_id = user.user_id if user else 'no-user'
744 744 # don't cache for empty users
745 745 plugin_cache_active = plugin_cache_active and user_id
746 746 cache_namespace_uid = 'cache_user_auth.{}'.format(user_id)
747 747 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
748 748
749 749 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
750 750 expiration_time=cache_ttl,
751 751 condition=plugin_cache_active)
752 752 def compute_auth(
753 753 cache_name, plugin_name, username, password):
754 754
755 755 # _authenticate is a wrapper for .auth() method of plugin.
756 756 # it checks if .auth() sends proper data.
757 757 # For RhodeCodeExternalAuthPlugin it also maps users to
758 758 # Database and maps the attributes returned from .auth()
759 759 # to RhodeCode database. If this function returns data
760 760 # then auth is correct.
761 761 log.debug('Running plugin `%s` _authenticate method '
762 762 'using username and password', plugin.get_id())
763 763 return plugin._authenticate(
764 764 user, username, password, plugin_settings,
765 765 environ=environ or {})
766 766
767 767 start = time.time()
768 768 # for environ based auth, password can be empty, but then the validation is
769 769 # on the server that fills in the env data needed for authentication
770 770 plugin_user = compute_auth('auth', plugin.name, username, (password or ''))
771 771
772 772 auth_time = time.time() - start
773 773 log.debug('Authentication for plugin `%s` completed in %.4fs, '
774 774 'expiration time of fetched cache %.1fs.',
775 775 plugin.get_id(), auth_time, cache_ttl,
776 776 extra={"plugin": plugin.get_id(), "time": auth_time})
777 777
778 778 log.debug('PLUGIN USER DATA: %s', plugin_user)
779 779
780 780 statsd = StatsdClient.statsd
781 781
782 782 if plugin_user:
783 783 log.debug('Plugin returned proper authentication data')
784 784 if statsd:
785 785 elapsed_time_ms = round(1000.0 * auth_time) # use ms only
786 786 statsd.incr('rhodecode_login_success_total')
787 787 statsd.timing("rhodecode_login_timing.histogram", elapsed_time_ms,
788 788 tags=["plugin:{}".format(plugin.get_id())],
789 789 use_decimals=False
790 790 )
791 791 return plugin_user
792 792
793 793 # we failed to Auth because .auth() method didn't return proper user
794 794 log.debug("User `%s` failed to authenticate against %s",
795 795 display_user, plugin.get_id())
796 796 if statsd:
797 797 statsd.incr('rhodecode_login_fail_total')
798 798
799 799 # case when we failed to authenticate against all defined plugins
800 800 return None
801 801
802 802
803 803 def chop_at(s, sub, inclusive=False):
804 804 """Truncate string ``s`` at the first occurrence of ``sub``.
805 805
806 806 If ``inclusive`` is true, truncate just after ``sub`` rather than at it.
807 807
808 808 >>> chop_at("plutocratic brats", "rat")
809 809 'plutoc'
810 810 >>> chop_at("plutocratic brats", "rat", True)
811 811 'plutocrat'
812 812 """
813 813 pos = s.find(sub)
814 814 if pos == -1:
815 815 return s
816 816 if inclusive:
817 817 return s[:pos+len(sub)]
818 818 return s[:pos]
@@ -1,839 +1,839 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Utilities for writing code that runs on Python 2 and 3"""
3 3
4 4 # Copyright (c) 2010-2015 Benjamin Peterson
5 5 #
6 6 # Permission is hereby granted, free of charge, to any person obtaining a copy
7 7 # of this software and associated documentation files (the "Software"), to deal
8 8 # in the Software without restriction, including without limitation the rights
9 9 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 10 # copies of the Software, and to permit persons to whom the Software is
11 11 # furnished to do so, subject to the following conditions:
12 12 #
13 13 # The above copyright notice and this permission notice shall be included in all
14 14 # copies or substantial portions of the Software.
15 15 #
16 16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 17 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 18 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 19 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 20 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 22 # SOFTWARE.
23 23
24 24
25 25
26 26 import functools
27 27 import itertools
28 28 import operator
29 29 import sys
30 30 import types
31 31
32 32 __author__ = "Benjamin Peterson <benjamin@python.org>"
33 33 __version__ = "1.9.0"
34 34
35 35
36 36 # Useful for very coarse version differentiation.
37 37 PY2 = sys.version_info[0] == 2
38 38 PY3 = sys.version_info[0] == 3
39 39
40 40 if PY3:
41 41 string_types = str,
42 42 integer_types = int,
43 43 class_types = type,
44 44 text_type = str
45 45 binary_type = bytes
46 46
47 47 MAXSIZE = sys.maxsize
48 48 else:
49 49 string_types = str,
50 integer_types = (int, long)
50 integer_types = int
51 51 class_types = (type, types.ClassType)
52 52 text_type = unicode
53 53 binary_type = str
54 54
55 55 if sys.platform.startswith("java"):
56 56 # Jython always uses 32 bits.
57 57 MAXSIZE = int((1 << 31) - 1)
58 58 else:
59 59 # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
60 60 class X(object):
61 61 def __len__(self):
62 62 return 1 << 31
63 63 try:
64 64 len(X())
65 65 except OverflowError:
66 66 # 32-bit
67 67 MAXSIZE = int((1 << 31) - 1)
68 68 else:
69 69 # 64-bit
70 70 MAXSIZE = int((1 << 63) - 1)
71 71 del X
72 72
73 73
74 74 def _add_doc(func, doc):
75 75 """Add documentation to a function."""
76 76 func.__doc__ = doc
77 77
78 78
79 79 def _import_module(name):
80 80 """Import module, returning the module after the last dot."""
81 81 __import__(name)
82 82 return sys.modules[name]
83 83
84 84
85 85 class _LazyDescr(object):
86 86
87 87 def __init__(self, name):
88 88 self.name = name
89 89
90 90 def __get__(self, obj, tp):
91 91 result = self._resolve()
92 92 setattr(obj, self.name, result) # Invokes __set__.
93 93 try:
94 94 # This is a bit ugly, but it avoids running this again by
95 95 # removing this descriptor.
96 96 delattr(obj.__class__, self.name)
97 97 except AttributeError:
98 98 pass
99 99 return result
100 100
101 101
102 102 class MovedModule(_LazyDescr):
103 103
104 104 def __init__(self, name, old, new=None):
105 105 super(MovedModule, self).__init__(name)
106 106 if PY3:
107 107 if new is None:
108 108 new = name
109 109 self.mod = new
110 110 else:
111 111 self.mod = old
112 112
113 113 def _resolve(self):
114 114 return _import_module(self.mod)
115 115
116 116 def __getattr__(self, attr):
117 117 _module = self._resolve()
118 118 value = getattr(_module, attr)
119 119 setattr(self, attr, value)
120 120 return value
121 121
122 122
123 123 class _LazyModule(types.ModuleType):
124 124
125 125 def __init__(self, name):
126 126 super(_LazyModule, self).__init__(name)
127 127 self.__doc__ = self.__class__.__doc__
128 128
129 129 def __dir__(self):
130 130 attrs = ["__doc__", "__name__"]
131 131 attrs += [attr.name for attr in self._moved_attributes]
132 132 return attrs
133 133
134 134 # Subclasses should override this
135 135 _moved_attributes = []
136 136
137 137
138 138 class MovedAttribute(_LazyDescr):
139 139
140 140 def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
141 141 super(MovedAttribute, self).__init__(name)
142 142 if PY3:
143 143 if new_mod is None:
144 144 new_mod = name
145 145 self.mod = new_mod
146 146 if new_attr is None:
147 147 if old_attr is None:
148 148 new_attr = name
149 149 else:
150 150 new_attr = old_attr
151 151 self.attr = new_attr
152 152 else:
153 153 self.mod = old_mod
154 154 if old_attr is None:
155 155 old_attr = name
156 156 self.attr = old_attr
157 157
158 158 def _resolve(self):
159 159 module = _import_module(self.mod)
160 160 return getattr(module, self.attr)
161 161
162 162
163 163 class _SixMetaPathImporter(object):
164 164 """
165 165 A meta path importer to import six.moves and its submodules.
166 166
167 167 This class implements a PEP302 finder and loader. It should be compatible
168 168 with Python 2.5 and all existing versions of Python3
169 169 """
170 170 def __init__(self, six_module_name):
171 171 self.name = six_module_name
172 172 self.known_modules = {}
173 173
174 174 def _add_module(self, mod, *fullnames):
175 175 for fullname in fullnames:
176 176 self.known_modules[self.name + "." + fullname] = mod
177 177
178 178 def _get_module(self, fullname):
179 179 return self.known_modules[self.name + "." + fullname]
180 180
181 181 def find_module(self, fullname, path=None):
182 182 if fullname in self.known_modules:
183 183 return self
184 184 return None
185 185
186 186 def __get_module(self, fullname):
187 187 try:
188 188 return self.known_modules[fullname]
189 189 except KeyError:
190 190 raise ImportError("This loader does not know module " + fullname)
191 191
192 192 def load_module(self, fullname):
193 193 try:
194 194 # in case of a reload
195 195 return sys.modules[fullname]
196 196 except KeyError:
197 197 pass
198 198 mod = self.__get_module(fullname)
199 199 if isinstance(mod, MovedModule):
200 200 mod = mod._resolve()
201 201 else:
202 202 mod.__loader__ = self
203 203 sys.modules[fullname] = mod
204 204 return mod
205 205
206 206 def is_package(self, fullname):
207 207 """
208 208 Return true, if the named module is a package.
209 209
210 210 We need this method to get correct spec objects with
211 211 Python 3.4 (see PEP451)
212 212 """
213 213 return hasattr(self.__get_module(fullname), "__path__")
214 214
215 215 def get_code(self, fullname):
216 216 """Return None
217 217
218 218 Required, if is_package is implemented"""
219 219 self.__get_module(fullname) # eventually raises ImportError
220 220 return None
221 221 get_source = get_code # same as get_code
222 222
223 223 _importer = _SixMetaPathImporter(__name__)
224 224
225 225
226 226 class _MovedItems(_LazyModule):
227 227 """Lazy loading of moved objects"""
228 228 __path__ = [] # mark as package
229 229
230 230
231 231 _moved_attributes = [
232 232 MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
233 233 MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
234 234 MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
235 235 MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
236 236 MovedAttribute("intern", "__builtin__", "sys"),
237 237 MovedAttribute("map", "itertools", "builtins", "imap", "map"),
238 238 MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
239 239 MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
240 240 MovedAttribute("reduce", "__builtin__", "functools"),
241 241 MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
242 242 MovedAttribute("StringIO", "StringIO", "io"),
243 243 MovedAttribute("UserDict", "UserDict", "collections"),
244 244 MovedAttribute("UserList", "UserList", "collections"),
245 245 MovedAttribute("UserString", "UserString", "collections"),
246 246 MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
247 247 MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
248 248 MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
249 249
250 250 MovedModule("builtins", "__builtin__"),
251 251 MovedModule("configparser", "ConfigParser"),
252 252 MovedModule("copyreg", "copy_reg"),
253 253 MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
254 254 MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"),
255 255 MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
256 256 MovedModule("http_cookies", "Cookie", "http.cookies"),
257 257 MovedModule("html_entities", "htmlentitydefs", "html.entities"),
258 258 MovedModule("html_parser", "HTMLParser", "html.parser"),
259 259 MovedModule("http_client", "httplib", "http.client"),
260 260 MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
261 261 MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
262 262 MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
263 263 MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
264 264 MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
265 265 MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
266 266 MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
267 267 MovedModule("cPickle", "cPickle", "pickle"),
268 268 MovedModule("queue", "Queue"),
269 269 MovedModule("reprlib", "repr"),
270 270 MovedModule("socketserver", "SocketServer"),
271 271 MovedModule("_thread", "thread", "_thread"),
272 272 MovedModule("tkinter", "Tkinter"),
273 273 MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
274 274 MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
275 275 MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
276 276 MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
277 277 MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
278 278 MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
279 279 MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
280 280 MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
281 281 MovedModule("tkinter_colorchooser", "tkColorChooser",
282 282 "tkinter.colorchooser"),
283 283 MovedModule("tkinter_commondialog", "tkCommonDialog",
284 284 "tkinter.commondialog"),
285 285 MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
286 286 MovedModule("tkinter_font", "tkFont", "tkinter.font"),
287 287 MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
288 288 MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
289 289 "tkinter.simpledialog"),
290 290 MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
291 291 MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
292 292 MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
293 293 MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
294 294 MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
295 295 MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
296 296 MovedModule("winreg", "_winreg"),
297 297 ]
298 298 for attr in _moved_attributes:
299 299 setattr(_MovedItems, attr.name, attr)
300 300 if isinstance(attr, MovedModule):
301 301 _importer._add_module(attr, "moves." + attr.name)
302 302 del attr
303 303
304 304 _MovedItems._moved_attributes = _moved_attributes
305 305
306 306 moves = _MovedItems(__name__ + ".moves")
307 307 _importer._add_module(moves, "moves")
308 308
309 309
310 310 class Module_six_moves_urllib_parse(_LazyModule):
311 311 """Lazy loading of moved objects in six.moves.urllib_parse"""
312 312
313 313
314 314 _urllib_parse_moved_attributes = [
315 315 MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
316 316 MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
317 317 MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
318 318 MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
319 319 MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
320 320 MovedAttribute("urljoin", "urlparse", "urllib.parse"),
321 321 MovedAttribute("urlparse", "urlparse", "urllib.parse"),
322 322 MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
323 323 MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
324 324 MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
325 325 MovedAttribute("quote", "urllib", "urllib.parse"),
326 326 MovedAttribute("quote_plus", "urllib", "urllib.parse"),
327 327 MovedAttribute("unquote", "urllib", "urllib.parse"),
328 328 MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
329 329 MovedAttribute("urlencode", "urllib", "urllib.parse"),
330 330 MovedAttribute("splitquery", "urllib", "urllib.parse"),
331 331 MovedAttribute("splittag", "urllib", "urllib.parse"),
332 332 MovedAttribute("splituser", "urllib", "urllib.parse"),
333 333 MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
334 334 MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
335 335 MovedAttribute("uses_params", "urlparse", "urllib.parse"),
336 336 MovedAttribute("uses_query", "urlparse", "urllib.parse"),
337 337 MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
338 338 ]
339 339 for attr in _urllib_parse_moved_attributes:
340 340 setattr(Module_six_moves_urllib_parse, attr.name, attr)
341 341 del attr
342 342
343 343 Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
344 344
345 345 _importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
346 346 "moves.urllib_parse", "moves.urllib.parse")
347 347
348 348
349 349 class Module_six_moves_urllib_error(_LazyModule):
350 350 """Lazy loading of moved objects in six.moves.urllib_error"""
351 351
352 352
353 353 _urllib_error_moved_attributes = [
354 354 MovedAttribute("URLError", "urllib2", "urllib.error"),
355 355 MovedAttribute("HTTPError", "urllib2", "urllib.error"),
356 356 MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
357 357 ]
358 358 for attr in _urllib_error_moved_attributes:
359 359 setattr(Module_six_moves_urllib_error, attr.name, attr)
360 360 del attr
361 361
362 362 Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
363 363
364 364 _importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
365 365 "moves.urllib_error", "moves.urllib.error")
366 366
367 367
368 368 class Module_six_moves_urllib_request(_LazyModule):
369 369 """Lazy loading of moved objects in six.moves.urllib_request"""
370 370
371 371
372 372 _urllib_request_moved_attributes = [
373 373 MovedAttribute("urlopen", "urllib2", "urllib.request"),
374 374 MovedAttribute("install_opener", "urllib2", "urllib.request"),
375 375 MovedAttribute("build_opener", "urllib2", "urllib.request"),
376 376 MovedAttribute("pathname2url", "urllib", "urllib.request"),
377 377 MovedAttribute("url2pathname", "urllib", "urllib.request"),
378 378 MovedAttribute("getproxies", "urllib", "urllib.request"),
379 379 MovedAttribute("Request", "urllib2", "urllib.request"),
380 380 MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
381 381 MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
382 382 MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
383 383 MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
384 384 MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
385 385 MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
386 386 MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
387 387 MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
388 388 MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
389 389 MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
390 390 MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
391 391 MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
392 392 MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
393 393 MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
394 394 MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
395 395 MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
396 396 MovedAttribute("FileHandler", "urllib2", "urllib.request"),
397 397 MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
398 398 MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
399 399 MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
400 400 MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
401 401 MovedAttribute("urlretrieve", "urllib", "urllib.request"),
402 402 MovedAttribute("urlcleanup", "urllib", "urllib.request"),
403 403 MovedAttribute("URLopener", "urllib", "urllib.request"),
404 404 MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
405 405 MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
406 406 ]
407 407 for attr in _urllib_request_moved_attributes:
408 408 setattr(Module_six_moves_urllib_request, attr.name, attr)
409 409 del attr
410 410
411 411 Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
412 412
413 413 _importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
414 414 "moves.urllib_request", "moves.urllib.request")
415 415
416 416
417 417 class Module_six_moves_urllib_response(_LazyModule):
418 418 """Lazy loading of moved objects in six.moves.urllib_response"""
419 419
420 420
421 421 _urllib_response_moved_attributes = [
422 422 MovedAttribute("addbase", "urllib", "urllib.response"),
423 423 MovedAttribute("addclosehook", "urllib", "urllib.response"),
424 424 MovedAttribute("addinfo", "urllib", "urllib.response"),
425 425 MovedAttribute("addinfourl", "urllib", "urllib.response"),
426 426 ]
427 427 for attr in _urllib_response_moved_attributes:
428 428 setattr(Module_six_moves_urllib_response, attr.name, attr)
429 429 del attr
430 430
431 431 Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
432 432
433 433 _importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
434 434 "moves.urllib_response", "moves.urllib.response")
435 435
436 436
437 437 class Module_six_moves_urllib_robotparser(_LazyModule):
438 438 """Lazy loading of moved objects in six.moves.urllib_robotparser"""
439 439
440 440
441 441 _urllib_robotparser_moved_attributes = [
442 442 MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
443 443 ]
444 444 for attr in _urllib_robotparser_moved_attributes:
445 445 setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
446 446 del attr
447 447
448 448 Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
449 449
450 450 _importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
451 451 "moves.urllib_robotparser", "moves.urllib.robotparser")
452 452
453 453
454 454 class Module_six_moves_urllib(types.ModuleType):
455 455 """Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
456 456 __path__ = [] # mark as package
457 457 parse = _importer._get_module("moves.urllib_parse")
458 458 error = _importer._get_module("moves.urllib_error")
459 459 request = _importer._get_module("moves.urllib_request")
460 460 response = _importer._get_module("moves.urllib_response")
461 461 robotparser = _importer._get_module("moves.urllib_robotparser")
462 462
463 463 def __dir__(self):
464 464 return ['parse', 'error', 'request', 'response', 'robotparser']
465 465
466 466 _importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
467 467 "moves.urllib")
468 468
469 469
470 470 def add_move(move):
471 471 """Add an item to six.moves."""
472 472 setattr(_MovedItems, move.name, move)
473 473
474 474
475 475 def remove_move(name):
476 476 """Remove item from six.moves."""
477 477 try:
478 478 delattr(_MovedItems, name)
479 479 except AttributeError:
480 480 try:
481 481 del moves.__dict__[name]
482 482 except KeyError:
483 483 raise AttributeError("no such move, %r" % (name,))
484 484
485 485
486 486 if PY3:
487 487 _meth_func = "__func__"
488 488 _meth_self = "__self__"
489 489
490 490 _func_closure = "__closure__"
491 491 _func_code = "__code__"
492 492 _func_defaults = "__defaults__"
493 493 _func_globals = "__globals__"
494 494 else:
495 495 _meth_func = "im_func"
496 496 _meth_self = "im_self"
497 497
498 498 _func_closure = "func_closure"
499 499 _func_code = "func_code"
500 500 _func_defaults = "func_defaults"
501 501 _func_globals = "func_globals"
502 502
503 503
504 504 try:
505 505 advance_iterator = next
506 506 except NameError:
507 507 def advance_iterator(it):
508 508 return it.next()
509 509 next = advance_iterator
510 510
511 511
512 512 try:
513 513 callable = callable
514 514 except NameError:
515 515 def callable(obj):
516 516 return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
517 517
518 518
519 519 if PY3:
520 520 def get_unbound_function(unbound):
521 521 return unbound
522 522
523 523 create_bound_method = types.MethodType
524 524
525 525 Iterator = object
526 526 else:
527 527 def get_unbound_function(unbound):
528 528 return unbound.im_func
529 529
530 530 def create_bound_method(func, obj):
531 531 return types.MethodType(func, obj, obj.__class__)
532 532
533 533 class Iterator(object):
534 534
535 535 def next(self):
536 536 return type(self).__next__(self)
537 537
538 538 callable = callable
539 539 _add_doc(get_unbound_function,
540 540 """Get the function out of a possibly unbound function""")
541 541
542 542
543 543 get_method_function = operator.attrgetter(_meth_func)
544 544 get_method_self = operator.attrgetter(_meth_self)
545 545 get_function_closure = operator.attrgetter(_func_closure)
546 546 get_function_code = operator.attrgetter(_func_code)
547 547 get_function_defaults = operator.attrgetter(_func_defaults)
548 548 get_function_globals = operator.attrgetter(_func_globals)
549 549
550 550
551 551 if PY3:
552 552 def iterkeys(d, **kw):
553 553 return iter(d.keys(**kw))
554 554
555 555 def itervalues(d, **kw):
556 556 return iter(d.values(**kw))
557 557
558 558 def iteritems(d, **kw):
559 559 return iter(d.items(**kw))
560 560
561 561 def iterlists(d, **kw):
562 562 return iter(d.lists(**kw))
563 563
564 564 viewkeys = operator.methodcaller("keys")
565 565
566 566 viewvalues = operator.methodcaller("values")
567 567
568 568 viewitems = operator.methodcaller("items")
569 569 else:
570 570 def iterkeys(d, **kw):
571 571 return iter(d.iterkeys(**kw))
572 572
573 573 def itervalues(d, **kw):
574 574 return iter(d.itervalues(**kw))
575 575
576 576 def iteritems(d, **kw):
577 577 return iter(d.iteritems(**kw))
578 578
579 579 def iterlists(d, **kw):
580 580 return iter(d.iterlists(**kw))
581 581
582 582 viewkeys = operator.methodcaller("viewkeys")
583 583
584 584 viewvalues = operator.methodcaller("viewvalues")
585 585
586 586 viewitems = operator.methodcaller("viewitems")
587 587
588 588 _add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
589 589 _add_doc(itervalues, "Return an iterator over the values of a dictionary.")
590 590 _add_doc(iteritems,
591 591 "Return an iterator over the (key, value) pairs of a dictionary.")
592 592 _add_doc(iterlists,
593 593 "Return an iterator over the (key, [values]) pairs of a dictionary.")
594 594
595 595
596 596 if PY3:
597 597 def b(s):
598 598 return s.encode("latin-1")
599 599 def u(s):
600 600 return s
601 601 unichr = chr
602 602 if sys.version_info[1] <= 1:
603 603 def int2byte(i):
604 604 return bytes((i,))
605 605 else:
606 606 # This is about 2x faster than the implementation above on 3.2+
607 607 int2byte = operator.methodcaller("to_bytes", 1, "big")
608 608 byte2int = operator.itemgetter(0)
609 609 indexbytes = operator.getitem
610 610 iterbytes = iter
611 611 import io
612 612 StringIO = io.StringIO
613 613 BytesIO = io.BytesIO
614 614 _assertCountEqual = "assertCountEqual"
615 615 _assertRaisesRegex = "assertRaisesRegex"
616 616 _assertRegex = "assertRegex"
617 617 else:
618 618 def b(s):
619 619 return s
620 620 # Workaround for standalone backslash
621 621 def u(s):
622 622 return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
623 623 unichr = unichr
624 624 int2byte = chr
625 625 def byte2int(bs):
626 626 return ord(bs[0])
627 627 def indexbytes(buf, i):
628 628 return ord(buf[i])
629 629 iterbytes = functools.partial(itertools.imap, ord)
630 630 from io import StringIO
631 631 StringIO = BytesIO = StringIO.StringIO
632 632 _assertCountEqual = "assertItemsEqual"
633 633 _assertRaisesRegex = "assertRaisesRegexp"
634 634 _assertRegex = "assertRegexpMatches"
635 635 _add_doc(b, """Byte literal""")
636 636 _add_doc(u, """Text literal""")
637 637
638 638
639 639 def assertCountEqual(self, *args, **kwargs):
640 640 return getattr(self, _assertCountEqual)(*args, **kwargs)
641 641
642 642
643 643 def assertRaisesRegex(self, *args, **kwargs):
644 644 return getattr(self, _assertRaisesRegex)(*args, **kwargs)
645 645
646 646
647 647 def assertRegex(self, *args, **kwargs):
648 648 return getattr(self, _assertRegex)(*args, **kwargs)
649 649
650 650
651 651 if PY3:
652 652 exec_ = getattr(moves.builtins, "exec")
653 653
654 654
655 655 def reraise(tp, value, tb=None):
656 656 if value is None:
657 657 value = tp()
658 658 if value.__traceback__ is not tb:
659 659 raise value.with_traceback(tb)
660 660 raise value
661 661
662 662 else:
663 663 def exec_(_code_, _globs_=None, _locs_=None):
664 664 """Execute code in a namespace."""
665 665 if _globs_ is None:
666 666 frame = sys._getframe(1)
667 667 _globs_ = frame.f_globals
668 668 if _locs_ is None:
669 669 _locs_ = frame.f_locals
670 670 del frame
671 671 elif _locs_ is None:
672 672 _locs_ = _globs_
673 673 exec("""exec _code_ in _globs_, _locs_""")
674 674
675 675
676 676 exec_("""def reraise(tp, value, tb=None):
677 677 raise tp, value, tb
678 678 """)
679 679
680 680
681 681 if sys.version_info[:2] == (3, 2):
682 682 exec_("""def raise_from(value, from_value):
683 683 if from_value is None:
684 684 raise value
685 685 raise value from from_value
686 686 """)
687 687 elif sys.version_info[:2] > (3, 2):
688 688 exec_("""def raise_from(value, from_value):
689 689 raise value from from_value
690 690 """)
691 691 else:
692 692 def raise_from(value, from_value):
693 693 raise value
694 694
695 695
696 696 print_ = getattr(moves.builtins, "print", None)
697 697 if print_ is None:
698 698 def print_(*args, **kwargs):
699 699 """The new-style print function for Python 2.4 and 2.5."""
700 700 fp = kwargs.pop("file", sys.stdout)
701 701 if fp is None:
702 702 return
703 703 def write(data):
704 704 if not isinstance(data, str):
705 705 data = str(data)
706 706 # If the file has an encoding, encode unicode with it.
707 707 if (isinstance(fp, file) and
708 708 isinstance(data, unicode) and
709 709 fp.encoding is not None):
710 710 errors = getattr(fp, "errors", None)
711 711 if errors is None:
712 712 errors = "strict"
713 713 data = data.encode(fp.encoding, errors)
714 714 fp.write(data)
715 715 want_unicode = False
716 716 sep = kwargs.pop("sep", None)
717 717 if sep is not None:
718 718 if isinstance(sep, unicode):
719 719 want_unicode = True
720 720 elif not isinstance(sep, str):
721 721 raise TypeError("sep must be None or a string")
722 722 end = kwargs.pop("end", None)
723 723 if end is not None:
724 724 if isinstance(end, unicode):
725 725 want_unicode = True
726 726 elif not isinstance(end, str):
727 727 raise TypeError("end must be None or a string")
728 728 if kwargs:
729 729 raise TypeError("invalid keyword arguments to print()")
730 730 if not want_unicode:
731 731 for arg in args:
732 732 if isinstance(arg, unicode):
733 733 want_unicode = True
734 734 break
735 735 if want_unicode:
736 736 newline = unicode("\n")
737 737 space = unicode(" ")
738 738 else:
739 739 newline = "\n"
740 740 space = " "
741 741 if sep is None:
742 742 sep = space
743 743 if end is None:
744 744 end = newline
745 745 for i, arg in enumerate(args):
746 746 if i:
747 747 write(sep)
748 748 write(arg)
749 749 write(end)
750 750 if sys.version_info[:2] < (3, 3):
751 751 _print = print_
752 752 def print_(*args, **kwargs):
753 753 fp = kwargs.get("file", sys.stdout)
754 754 flush = kwargs.pop("flush", False)
755 755 _print(*args, **kwargs)
756 756 if flush and fp is not None:
757 757 fp.flush()
758 758
759 759 _add_doc(reraise, """Reraise an exception.""")
760 760
761 761 if sys.version_info[0:2] < (3, 4):
762 762 def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
763 763 updated=functools.WRAPPER_UPDATES):
764 764 def wrapper(f):
765 765 f = functools.wraps(wrapped, assigned, updated)(f)
766 766 f.__wrapped__ = wrapped
767 767 return f
768 768 return wrapper
769 769 else:
770 770 wraps = functools.wraps
771 771
772 772 def with_metaclass(meta, *bases):
773 773 """Create a base class with a metaclass."""
774 774 # This requires a bit of explanation: the basic idea is to make a dummy
775 775 # metaclass for one level of class instantiation that replaces itself with
776 776 # the actual metaclass.
777 777 class metaclass(meta):
778 778 def __new__(cls, name, this_bases, d):
779 779 return meta(name, bases, d)
780 780 return type.__new__(metaclass, 'temporary_class', (), {})
781 781
782 782
783 783 def add_metaclass(metaclass):
784 784 """Class decorator for creating a class with a metaclass."""
785 785 def wrapper(cls):
786 786 orig_vars = cls.__dict__.copy()
787 787 slots = orig_vars.get('__slots__')
788 788 if slots is not None:
789 789 if isinstance(slots, str):
790 790 slots = [slots]
791 791 for slots_var in slots:
792 792 orig_vars.pop(slots_var)
793 793 orig_vars.pop('__dict__', None)
794 794 orig_vars.pop('__weakref__', None)
795 795 return metaclass(cls.__name__, cls.__bases__, orig_vars)
796 796 return wrapper
797 797
798 798
799 799 def python_2_unicode_compatible(klass):
800 800 """
801 801 A decorator that defines __unicode__ and __str__ methods under Python 2.
802 802 Under Python 3 it does nothing.
803 803
804 804 To support Python 2 and 3 with a single code base, define a __str__ method
805 805 returning text and apply this decorator to the class.
806 806 """
807 807 if PY2:
808 808 if '__str__' not in klass.__dict__:
809 809 raise ValueError("@python_2_unicode_compatible cannot be applied "
810 810 "to %s because it doesn't define __str__()." %
811 811 klass.__name__)
812 812 klass.__unicode__ = klass.__str__
813 813 klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
814 814 return klass
815 815
816 816
817 817 # Complete the moves implementation.
818 818 # This code is at the end of this module to speed up module loading.
819 819 # Turn this module into a package.
820 820 __path__ = [] # required for PEP 302 and PEP 451
821 821 __package__ = __name__ # see PEP 366 @ReservedAssignment
822 822 if globals().get("__spec__") is not None:
823 823 __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable
824 824 # Remove other six meta path importers, since they cause problems. This can
825 825 # happen if six is removed from sys.modules and then reloaded. (Setuptools does
826 826 # this for some reason.)
827 827 if sys.meta_path:
828 828 for i, importer in enumerate(sys.meta_path):
829 829 # Here's some real nastiness: Another "instance" of the six module might
830 830 # be floating around. Therefore, we can't use isinstance() to check for
831 831 # the six meta path importer, since the other six instance will have
832 832 # inserted an importer with different class.
833 833 if (type(importer).__name__ == "_SixMetaPathImporter" and
834 834 importer.name == __name__):
835 835 del sys.meta_path[i]
836 836 break
837 837 del i, importer
838 838 # Finally, add the importer to the meta path import hook.
839 839 sys.meta_path.append(_importer)
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now