Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/future/standard_library/__init__.py: 28%

270 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:53 +0000

1""" 

2Python 3 reorganized the standard library (PEP 3108). This module exposes 

3several standard library modules to Python 2 under their new Python 3 

4names. 

5 

6It is designed to be used as follows:: 

7 

8 from future import standard_library 

9 standard_library.install_aliases() 

10 

11And then these normal Py3 imports work on both Py3 and Py2:: 

12 

13 import builtins 

14 import copyreg 

15 import queue 

16 import reprlib 

17 import socketserver 

18 import winreg # on Windows only 

19 import test.support 

20 import html, html.parser, html.entites 

21 import http, http.client, http.server 

22 import http.cookies, http.cookiejar 

23 import urllib.parse, urllib.request, urllib.response, urllib.error, urllib.robotparser 

24 import xmlrpc.client, xmlrpc.server 

25 

26 import _thread 

27 import _dummy_thread 

28 import _markupbase 

29 

30 from itertools import filterfalse, zip_longest 

31 from sys import intern 

32 from collections import UserDict, UserList, UserString 

33 from collections import OrderedDict, Counter, ChainMap # even on Py2.6 

34 from subprocess import getoutput, getstatusoutput 

35 from subprocess import check_output # even on Py2.6 

36 

37(The renamed modules and functions are still available under their old 

38names on Python 2.) 

39 

40This is a cleaner alternative to this idiom (see 

41http://docs.pythonsprints.com/python3_porting/py-porting.html):: 

42 

43 try: 

44 import queue 

45 except ImportError: 

46 import Queue as queue 

47 

48 

49Limitations 

50----------- 

51We don't currently support these modules, but would like to:: 

52 

53 import dbm 

54 import dbm.dumb 

55 import dbm.gnu 

56 import collections.abc # on Py33 

57 import pickle # should (optionally) bring in cPickle on Python 2 

58 

59""" 

60 

61from __future__ import absolute_import, division, print_function 

62 

63import sys 

64import logging 

65import imp 

66import contextlib 

67import types 

68import copy 

69import os 

70 

71# Make a dedicated logger; leave the root logger to be configured 

72# by the application. 

73flog = logging.getLogger('future_stdlib') 

74_formatter = logging.Formatter(logging.BASIC_FORMAT) 

75_handler = logging.StreamHandler() 

76_handler.setFormatter(_formatter) 

77flog.addHandler(_handler) 

78flog.setLevel(logging.WARN) 

79 

80from future.utils import PY2, PY3 

81 

82# The modules that are defined under the same names on Py3 but with 

83# different contents in a significant way (e.g. submodules) are: 

84# pickle (fast one) 

85# dbm 

86# urllib 

87# test 

88# email 

89 

90REPLACED_MODULES = set(['test', 'urllib', 'pickle', 'dbm']) # add email and dbm when we support it 

91 

92# The following module names are not present in Python 2.x, so they cause no 

93# potential clashes between the old and new names: 

94# http 

95# html 

96# tkinter 

97# xmlrpc 

98# Keys: Py2 / real module names 

99# Values: Py3 / simulated module names 

100RENAMES = { 

101 # 'cStringIO': 'io', # there's a new io module in Python 2.6 

102 # that provides StringIO and BytesIO 

103 # 'StringIO': 'io', # ditto 

104 # 'cPickle': 'pickle', 

105 '__builtin__': 'builtins', 

106 'copy_reg': 'copyreg', 

107 'Queue': 'queue', 

108 'future.moves.socketserver': 'socketserver', 

109 'ConfigParser': 'configparser', 

110 'repr': 'reprlib', 

111 # 'FileDialog': 'tkinter.filedialog', 

112 # 'tkFileDialog': 'tkinter.filedialog', 

113 # 'SimpleDialog': 'tkinter.simpledialog', 

114 # 'tkSimpleDialog': 'tkinter.simpledialog', 

115 # 'tkColorChooser': 'tkinter.colorchooser', 

116 # 'tkCommonDialog': 'tkinter.commondialog', 

117 # 'Dialog': 'tkinter.dialog', 

118 # 'Tkdnd': 'tkinter.dnd', 

119 # 'tkFont': 'tkinter.font', 

120 # 'tkMessageBox': 'tkinter.messagebox', 

121 # 'ScrolledText': 'tkinter.scrolledtext', 

122 # 'Tkconstants': 'tkinter.constants', 

123 # 'Tix': 'tkinter.tix', 

124 # 'ttk': 'tkinter.ttk', 

125 # 'Tkinter': 'tkinter', 

126 '_winreg': 'winreg', 

127 'thread': '_thread', 

128 'dummy_thread': '_dummy_thread', 

129 # 'anydbm': 'dbm', # causes infinite import loop 

130 # 'whichdb': 'dbm', # causes infinite import loop 

131 # anydbm and whichdb are handled by fix_imports2 

132 # 'dbhash': 'dbm.bsd', 

133 # 'dumbdbm': 'dbm.dumb', 

134 # 'dbm': 'dbm.ndbm', 

135 # 'gdbm': 'dbm.gnu', 

136 'future.moves.xmlrpc': 'xmlrpc', 

137 # 'future.backports.email': 'email', # for use by urllib 

138 # 'DocXMLRPCServer': 'xmlrpc.server', 

139 # 'SimpleXMLRPCServer': 'xmlrpc.server', 

140 # 'httplib': 'http.client', 

141 # 'htmlentitydefs' : 'html.entities', 

142 # 'HTMLParser' : 'html.parser', 

143 # 'Cookie': 'http.cookies', 

144 # 'cookielib': 'http.cookiejar', 

145 # 'BaseHTTPServer': 'http.server', 

146 # 'SimpleHTTPServer': 'http.server', 

147 # 'CGIHTTPServer': 'http.server', 

148 # 'future.backports.test': 'test', # primarily for renaming test_support to support 

149 # 'commands': 'subprocess', 

150 # 'urlparse' : 'urllib.parse', 

151 # 'robotparser' : 'urllib.robotparser', 

152 # 'abc': 'collections.abc', # for Py33 

153 # 'future.utils.six.moves.html': 'html', 

154 # 'future.utils.six.moves.http': 'http', 

155 'future.moves.html': 'html', 

156 'future.moves.http': 'http', 

157 # 'future.backports.urllib': 'urllib', 

158 # 'future.utils.six.moves.urllib': 'urllib', 

159 'future.moves._markupbase': '_markupbase', 

160 } 

161 

162 

163# It is complicated and apparently brittle to mess around with the 

164# ``sys.modules`` cache in order to support "import urllib" meaning two 

165# different things (Py2.7 urllib and backported Py3.3-like urllib) in different 

166# contexts. So we require explicit imports for these modules. 

167assert len(set(RENAMES.values()) & set(REPLACED_MODULES)) == 0 

168 

169 

170# Harmless renames that we can insert. 

171# These modules need names from elsewhere being added to them: 

172# subprocess: should provide getoutput and other fns from commands 

173# module but these fns are missing: getstatus, mk2arg, 

174# mkarg 

175# re: needs an ASCII constant that works compatibly with Py3 

176 

177# etc: see lib2to3/fixes/fix_imports.py 

178 

179# (New module name, new object name, old module name, old object name) 

180MOVES = [('collections', 'UserList', 'UserList', 'UserList'), 

181 ('collections', 'UserDict', 'UserDict', 'UserDict'), 

182 ('collections', 'UserString','UserString', 'UserString'), 

183 ('collections', 'ChainMap', 'future.backports.misc', 'ChainMap'), 

184 ('itertools', 'filterfalse','itertools', 'ifilterfalse'), 

185 ('itertools', 'zip_longest','itertools', 'izip_longest'), 

186 ('sys', 'intern','__builtin__', 'intern'), 

187 # The re module has no ASCII flag in Py2, but this is the default. 

188 # Set re.ASCII to a zero constant. stat.ST_MODE just happens to be one 

189 # (and it exists on Py2.6+). 

190 ('re', 'ASCII','stat', 'ST_MODE'), 

191 ('base64', 'encodebytes','base64', 'encodestring'), 

192 ('base64', 'decodebytes','base64', 'decodestring'), 

193 ('subprocess', 'getoutput', 'commands', 'getoutput'), 

194 ('subprocess', 'getstatusoutput', 'commands', 'getstatusoutput'), 

195 ('subprocess', 'check_output', 'future.backports.misc', 'check_output'), 

196 ('math', 'ceil', 'future.backports.misc', 'ceil'), 

197 ('collections', 'OrderedDict', 'future.backports.misc', 'OrderedDict'), 

198 ('collections', 'Counter', 'future.backports.misc', 'Counter'), 

199 ('collections', 'ChainMap', 'future.backports.misc', 'ChainMap'), 

200 ('itertools', 'count', 'future.backports.misc', 'count'), 

201 ('reprlib', 'recursive_repr', 'future.backports.misc', 'recursive_repr'), 

202 ('functools', 'cmp_to_key', 'future.backports.misc', 'cmp_to_key'), 

203 

204# This is no use, since "import urllib.request" etc. still fails: 

205# ('urllib', 'error', 'future.moves.urllib', 'error'), 

206# ('urllib', 'parse', 'future.moves.urllib', 'parse'), 

207# ('urllib', 'request', 'future.moves.urllib', 'request'), 

208# ('urllib', 'response', 'future.moves.urllib', 'response'), 

209# ('urllib', 'robotparser', 'future.moves.urllib', 'robotparser'), 

210 ] 

211 

212 

213# A minimal example of an import hook: 

214# class WarnOnImport(object): 

215# def __init__(self, *args): 

216# self.module_names = args 

217# 

218# def find_module(self, fullname, path=None): 

219# if fullname in self.module_names: 

220# self.path = path 

221# return self 

222# return None 

223# 

224# def load_module(self, name): 

225# if name in sys.modules: 

226# return sys.modules[name] 

227# module_info = imp.find_module(name, self.path) 

228# module = imp.load_module(name, *module_info) 

229# sys.modules[name] = module 

230# flog.warning("Imported deprecated module %s", name) 

231# return module 

232 

233 

234class RenameImport(object): 

235 """ 

236 A class for import hooks mapping Py3 module names etc. to the Py2 equivalents. 

237 """ 

238 # Different RenameImport classes are created when importing this module from 

239 # different source files. This causes isinstance(hook, RenameImport) checks 

240 # to produce inconsistent results. We add this RENAMER attribute here so 

241 # remove_hooks() and install_hooks() can find instances of these classes 

242 # easily: 

243 RENAMER = True 

244 

245 def __init__(self, old_to_new): 

246 ''' 

247 Pass in a dictionary-like object mapping from old names to new 

248 names. E.g. {'ConfigParser': 'configparser', 'cPickle': 'pickle'} 

249 ''' 

250 self.old_to_new = old_to_new 

251 both = set(old_to_new.keys()) & set(old_to_new.values()) 

252 assert (len(both) == 0 and 

253 len(set(old_to_new.values())) == len(old_to_new.values())), \ 

254 'Ambiguity in renaming (handler not implemented)' 

255 self.new_to_old = dict((new, old) for (old, new) in old_to_new.items()) 

256 

257 def find_module(self, fullname, path=None): 

258 # Handles hierarchical importing: package.module.module2 

259 new_base_names = set([s.split('.')[0] for s in self.new_to_old]) 

260 # Before v0.12: Was: if fullname in set(self.old_to_new) | new_base_names: 

261 if fullname in new_base_names: 

262 return self 

263 return None 

264 

265 def load_module(self, name): 

266 path = None 

267 if name in sys.modules: 

268 return sys.modules[name] 

269 elif name in self.new_to_old: 

270 # New name. Look up the corresponding old (Py2) name: 

271 oldname = self.new_to_old[name] 

272 module = self._find_and_load_module(oldname) 

273 # module.__future_module__ = True 

274 else: 

275 module = self._find_and_load_module(name) 

276 # In any case, make it available under the requested (Py3) name 

277 sys.modules[name] = module 

278 return module 

279 

280 def _find_and_load_module(self, name, path=None): 

281 """ 

282 Finds and loads it. But if there's a . in the name, handles it 

283 properly. 

284 """ 

285 bits = name.split('.') 

286 while len(bits) > 1: 

287 # Treat the first bit as a package 

288 packagename = bits.pop(0) 

289 package = self._find_and_load_module(packagename, path) 

290 try: 

291 path = package.__path__ 

292 except AttributeError: 

293 # This could be e.g. moves. 

294 flog.debug('Package {0} has no __path__.'.format(package)) 

295 if name in sys.modules: 

296 return sys.modules[name] 

297 flog.debug('What to do here?') 

298 

299 name = bits[0] 

300 module_info = imp.find_module(name, path) 

301 return imp.load_module(name, *module_info) 

302 

303 

304class hooks(object): 

305 """ 

306 Acts as a context manager. Saves the state of sys.modules and restores it 

307 after the 'with' block. 

308 

309 Use like this: 

310 

311 >>> from future import standard_library 

312 >>> with standard_library.hooks(): 

313 ... import http.client 

314 >>> import requests 

315 

316 For this to work, http.client will be scrubbed from sys.modules after the 

317 'with' block. That way the modules imported in the 'with' block will 

318 continue to be accessible in the current namespace but not from any 

319 imported modules (like requests). 

320 """ 

321 def __enter__(self): 

322 # flog.debug('Entering hooks context manager') 

323 self.old_sys_modules = copy.copy(sys.modules) 

324 self.hooks_were_installed = detect_hooks() 

325 # self.scrubbed = scrub_py2_sys_modules() 

326 install_hooks() 

327 return self 

328 

329 def __exit__(self, *args): 

330 # flog.debug('Exiting hooks context manager') 

331 # restore_sys_modules(self.scrubbed) 

332 if not self.hooks_were_installed: 

333 remove_hooks() 

334 # scrub_future_sys_modules() 

335 

336# Sanity check for is_py2_stdlib_module(): We aren't replacing any 

337# builtin modules names: 

338if PY2: 

339 assert len(set(RENAMES.values()) & set(sys.builtin_module_names)) == 0 

340 

341 

342def is_py2_stdlib_module(m): 

343 """ 

344 Tries to infer whether the module m is from the Python 2 standard library. 

345 This may not be reliable on all systems. 

346 """ 

347 if PY3: 

348 return False 

349 if not 'stdlib_path' in is_py2_stdlib_module.__dict__: 

350 stdlib_files = [contextlib.__file__, os.__file__, copy.__file__] 

351 stdlib_paths = [os.path.split(f)[0] for f in stdlib_files] 

352 if not len(set(stdlib_paths)) == 1: 

353 # This seems to happen on travis-ci.org. Very strange. We'll try to 

354 # ignore it. 

355 flog.warn('Multiple locations found for the Python standard ' 

356 'library: %s' % stdlib_paths) 

357 # Choose the first one arbitrarily 

358 is_py2_stdlib_module.stdlib_path = stdlib_paths[0] 

359 

360 if m.__name__ in sys.builtin_module_names: 

361 return True 

362 

363 if hasattr(m, '__file__'): 

364 modpath = os.path.split(m.__file__) 

365 if (modpath[0].startswith(is_py2_stdlib_module.stdlib_path) and 

366 'site-packages' not in modpath[0]): 

367 return True 

368 

369 return False 

370 

371 

372def scrub_py2_sys_modules(): 

373 """ 

374 Removes any Python 2 standard library modules from ``sys.modules`` that 

375 would interfere with Py3-style imports using import hooks. Examples are 

376 modules with the same names (like urllib or email). 

377 

378 (Note that currently import hooks are disabled for modules like these 

379 with ambiguous names anyway ...) 

380 """ 

381 if PY3: 

382 return {} 

383 scrubbed = {} 

384 for modulename in REPLACED_MODULES & set(RENAMES.keys()): 

385 if not modulename in sys.modules: 

386 continue 

387 

388 module = sys.modules[modulename] 

389 

390 if is_py2_stdlib_module(module): 

391 flog.debug('Deleting (Py2) {} from sys.modules'.format(modulename)) 

392 scrubbed[modulename] = sys.modules[modulename] 

393 del sys.modules[modulename] 

394 return scrubbed 

395 

396 

397def scrub_future_sys_modules(): 

398 """ 

399 Deprecated. 

400 """ 

401 return {} 

402 

403class suspend_hooks(object): 

404 """ 

405 Acts as a context manager. Use like this: 

406 

407 >>> from future import standard_library 

408 >>> standard_library.install_hooks() 

409 >>> import http.client 

410 >>> # ... 

411 >>> with standard_library.suspend_hooks(): 

412 >>> import requests # incompatible with ``future``'s standard library hooks 

413 

414 If the hooks were disabled before the context, they are not installed when 

415 the context is left. 

416 """ 

417 def __enter__(self): 

418 self.hooks_were_installed = detect_hooks() 

419 remove_hooks() 

420 # self.scrubbed = scrub_future_sys_modules() 

421 return self 

422 

423 def __exit__(self, *args): 

424 if self.hooks_were_installed: 

425 install_hooks() 

426 # restore_sys_modules(self.scrubbed) 

427 

428 

429def restore_sys_modules(scrubbed): 

430 """ 

431 Add any previously scrubbed modules back to the sys.modules cache, 

432 but only if it's safe to do so. 

433 """ 

434 clash = set(sys.modules) & set(scrubbed) 

435 if len(clash) != 0: 

436 # If several, choose one arbitrarily to raise an exception about 

437 first = list(clash)[0] 

438 raise ImportError('future module {} clashes with Py2 module' 

439 .format(first)) 

440 sys.modules.update(scrubbed) 

441 

442 

443def install_aliases(): 

444 """ 

445 Monkey-patches the standard library in Py2.6/7 to provide 

446 aliases for better Py3 compatibility. 

447 """ 

448 if PY3: 

449 return 

450 # if hasattr(install_aliases, 'run_already'): 

451 # return 

452 for (newmodname, newobjname, oldmodname, oldobjname) in MOVES: 

453 __import__(newmodname) 

454 # We look up the module in sys.modules because __import__ just returns the 

455 # top-level package: 

456 newmod = sys.modules[newmodname] 

457 # newmod.__future_module__ = True 

458 

459 __import__(oldmodname) 

460 oldmod = sys.modules[oldmodname] 

461 

462 obj = getattr(oldmod, oldobjname) 

463 setattr(newmod, newobjname, obj) 

464 

465 # Hack for urllib so it appears to have the same structure on Py2 as on Py3 

466 import urllib 

467 from future.backports.urllib import request 

468 from future.backports.urllib import response 

469 from future.backports.urllib import parse 

470 from future.backports.urllib import error 

471 from future.backports.urllib import robotparser 

472 urllib.request = request 

473 urllib.response = response 

474 urllib.parse = parse 

475 urllib.error = error 

476 urllib.robotparser = robotparser 

477 sys.modules['urllib.request'] = request 

478 sys.modules['urllib.response'] = response 

479 sys.modules['urllib.parse'] = parse 

480 sys.modules['urllib.error'] = error 

481 sys.modules['urllib.robotparser'] = robotparser 

482 

483 # Patch the test module so it appears to have the same structure on Py2 as on Py3 

484 try: 

485 import test 

486 except ImportError: 

487 pass 

488 try: 

489 from future.moves.test import support 

490 except ImportError: 

491 pass 

492 else: 

493 test.support = support 

494 sys.modules['test.support'] = support 

495 

496 # Patch the dbm module so it appears to have the same structure on Py2 as on Py3 

497 try: 

498 import dbm 

499 except ImportError: 

500 pass 

501 else: 

502 from future.moves.dbm import dumb 

503 dbm.dumb = dumb 

504 sys.modules['dbm.dumb'] = dumb 

505 try: 

506 from future.moves.dbm import gnu 

507 except ImportError: 

508 pass 

509 else: 

510 dbm.gnu = gnu 

511 sys.modules['dbm.gnu'] = gnu 

512 try: 

513 from future.moves.dbm import ndbm 

514 except ImportError: 

515 pass 

516 else: 

517 dbm.ndbm = ndbm 

518 sys.modules['dbm.ndbm'] = ndbm 

519 

520 # install_aliases.run_already = True 

521 

522 

523def install_hooks(): 

524 """ 

525 This function installs the future.standard_library import hook into 

526 sys.meta_path. 

527 """ 

528 if PY3: 

529 return 

530 

531 install_aliases() 

532 

533 flog.debug('sys.meta_path was: {0}'.format(sys.meta_path)) 

534 flog.debug('Installing hooks ...') 

535 

536 # Add it unless it's there already 

537 newhook = RenameImport(RENAMES) 

538 if not detect_hooks(): 

539 sys.meta_path.append(newhook) 

540 flog.debug('sys.meta_path is now: {0}'.format(sys.meta_path)) 

541 

542 

543def enable_hooks(): 

544 """ 

545 Deprecated. Use install_hooks() instead. This will be removed by 

546 ``future`` v1.0. 

547 """ 

548 install_hooks() 

549 

550 

551def remove_hooks(scrub_sys_modules=False): 

552 """ 

553 This function removes the import hook from sys.meta_path. 

554 """ 

555 if PY3: 

556 return 

557 flog.debug('Uninstalling hooks ...') 

558 # Loop backwards, so deleting items keeps the ordering: 

559 for i, hook in list(enumerate(sys.meta_path))[::-1]: 

560 if hasattr(hook, 'RENAMER'): 

561 del sys.meta_path[i] 

562 

563 # Explicit is better than implicit. In the future the interface should 

564 # probably change so that scrubbing the import hooks requires a separate 

565 # function call. Left as is for now for backward compatibility with 

566 # v0.11.x. 

567 if scrub_sys_modules: 

568 scrub_future_sys_modules() 

569 

570 

571def disable_hooks(): 

572 """ 

573 Deprecated. Use remove_hooks() instead. This will be removed by 

574 ``future`` v1.0. 

575 """ 

576 remove_hooks() 

577 

578 

579def detect_hooks(): 

580 """ 

581 Returns True if the import hooks are installed, False if not. 

582 """ 

583 flog.debug('Detecting hooks ...') 

584 present = any([hasattr(hook, 'RENAMER') for hook in sys.meta_path]) 

585 if present: 

586 flog.debug('Detected.') 

587 else: 

588 flog.debug('Not detected.') 

589 return present 

590 

591 

592# As of v0.12, this no longer happens implicitly: 

593# if not PY3: 

594# install_hooks() 

595 

596 

597if not hasattr(sys, 'py2_modules'): 

598 sys.py2_modules = {} 

599 

600def cache_py2_modules(): 

601 """ 

602 Currently this function is unneeded, as we are not attempting to provide import hooks 

603 for modules with ambiguous names: email, urllib, pickle. 

604 """ 

605 if len(sys.py2_modules) != 0: 

606 return 

607 assert not detect_hooks() 

608 import urllib 

609 sys.py2_modules['urllib'] = urllib 

610 

611 import email 

612 sys.py2_modules['email'] = email 

613 

614 import pickle 

615 sys.py2_modules['pickle'] = pickle 

616 

617 # Not all Python installations have test module. (Anaconda doesn't, for example.) 

618 # try: 

619 # import test 

620 # except ImportError: 

621 # sys.py2_modules['test'] = None 

622 # sys.py2_modules['test'] = test 

623 

624 # import dbm 

625 # sys.py2_modules['dbm'] = dbm 

626 

627 

628def import_(module_name, backport=False): 

629 """ 

630 Pass a (potentially dotted) module name of a Python 3 standard library 

631 module. This function imports the module compatibly on Py2 and Py3 and 

632 returns the top-level module. 

633 

634 Example use: 

635 >>> http = import_('http.client') 

636 >>> http = import_('http.server') 

637 >>> urllib = import_('urllib.request') 

638 

639 Then: 

640 >>> conn = http.client.HTTPConnection(...) 

641 >>> response = urllib.request.urlopen('http://mywebsite.com') 

642 >>> # etc. 

643 

644 Use as follows: 

645 >>> package_name = import_(module_name) 

646 

647 On Py3, equivalent to this: 

648 

649 >>> import module_name 

650 

651 On Py2, equivalent to this if backport=False: 

652 

653 >>> from future.moves import module_name 

654 

655 or to this if backport=True: 

656 

657 >>> from future.backports import module_name 

658 

659 except that it also handles dotted module names such as ``http.client`` 

660 The effect then is like this: 

661 

662 >>> from future.backports import module 

663 >>> from future.backports.module import submodule 

664 >>> module.submodule = submodule 

665 

666 Note that this would be a SyntaxError in Python: 

667 

668 >>> from future.backports import http.client 

669 

670 """ 

671 # Python 2.6 doesn't have importlib in the stdlib, so it requires 

672 # the backported ``importlib`` package from PyPI as a dependency to use 

673 # this function: 

674 import importlib 

675 

676 if PY3: 

677 return __import__(module_name) 

678 else: 

679 # client.blah = blah 

680 # Then http.client = client 

681 # etc. 

682 if backport: 

683 prefix = 'future.backports' 

684 else: 

685 prefix = 'future.moves' 

686 parts = prefix.split('.') + module_name.split('.') 

687 

688 modules = [] 

689 for i, part in enumerate(parts): 

690 sofar = '.'.join(parts[:i+1]) 

691 modules.append(importlib.import_module(sofar)) 

692 for i, part in reversed(list(enumerate(parts))): 

693 if i == 0: 

694 break 

695 setattr(modules[i-1], part, modules[i]) 

696 

697 # Return the next-most top-level module after future.backports / future.moves: 

698 return modules[2] 

699 

700 

701def from_import(module_name, *symbol_names, **kwargs): 

702 """ 

703 Example use: 

704 >>> HTTPConnection = from_import('http.client', 'HTTPConnection') 

705 >>> HTTPServer = from_import('http.server', 'HTTPServer') 

706 >>> urlopen, urlparse = from_import('urllib.request', 'urlopen', 'urlparse') 

707 

708 Equivalent to this on Py3: 

709 

710 >>> from module_name import symbol_names[0], symbol_names[1], ... 

711 

712 and this on Py2: 

713 

714 >>> from future.moves.module_name import symbol_names[0], ... 

715 

716 or: 

717 

718 >>> from future.backports.module_name import symbol_names[0], ... 

719 

720 except that it also handles dotted module names such as ``http.client``. 

721 """ 

722 

723 if PY3: 

724 return __import__(module_name) 

725 else: 

726 if 'backport' in kwargs and bool(kwargs['backport']): 

727 prefix = 'future.backports' 

728 else: 

729 prefix = 'future.moves' 

730 parts = prefix.split('.') + module_name.split('.') 

731 module = importlib.import_module(prefix + '.' + module_name) 

732 output = [getattr(module, name) for name in symbol_names] 

733 if len(output) == 1: 

734 return output[0] 

735 else: 

736 return output 

737 

738 

739class exclude_local_folder_imports(object): 

740 """ 

741 A context-manager that prevents standard library modules like configparser 

742 from being imported from the local python-future source folder on Py3. 

743 

744 (This was need prior to v0.16.0 because the presence of a configparser 

745 folder would otherwise have prevented setuptools from running on Py3. Maybe 

746 it's not needed any more?) 

747 """ 

748 def __init__(self, *args): 

749 assert len(args) > 0 

750 self.module_names = args 

751 # Disallow dotted module names like http.client: 

752 if any(['.' in m for m in self.module_names]): 

753 raise NotImplementedError('Dotted module names are not supported') 

754 

755 def __enter__(self): 

756 self.old_sys_path = copy.copy(sys.path) 

757 self.old_sys_modules = copy.copy(sys.modules) 

758 if sys.version_info[0] < 3: 

759 return 

760 # The presence of all these indicates we've found our source folder, 

761 # because `builtins` won't have been installed in site-packages by setup.py: 

762 FUTURE_SOURCE_SUBFOLDERS = ['future', 'past', 'libfuturize', 'libpasteurize', 'builtins'] 

763 

764 # Look for the future source folder: 

765 for folder in self.old_sys_path: 

766 if all([os.path.exists(os.path.join(folder, subfolder)) 

767 for subfolder in FUTURE_SOURCE_SUBFOLDERS]): 

768 # Found it. Remove it. 

769 sys.path.remove(folder) 

770 

771 # Ensure we import the system module: 

772 for m in self.module_names: 

773 # Delete the module and any submodules from sys.modules: 

774 # for key in list(sys.modules): 

775 # if key == m or key.startswith(m + '.'): 

776 # try: 

777 # del sys.modules[key] 

778 # except KeyError: 

779 # pass 

780 try: 

781 module = __import__(m, level=0) 

782 except ImportError: 

783 # There's a problem importing the system module. E.g. the 

784 # winreg module is not available except on Windows. 

785 pass 

786 

787 def __exit__(self, *args): 

788 # Restore sys.path and sys.modules: 

789 sys.path = self.old_sys_path 

790 for m in set(self.old_sys_modules.keys()) - set(sys.modules.keys()): 

791 sys.modules[m] = self.old_sys_modules[m] 

792 

793TOP_LEVEL_MODULES = ['builtins', 

794 'copyreg', 

795 'html', 

796 'http', 

797 'queue', 

798 'reprlib', 

799 'socketserver', 

800 'test', 

801 'tkinter', 

802 'winreg', 

803 'xmlrpc', 

804 '_dummy_thread', 

805 '_markupbase', 

806 '_thread', 

807 ] 

808 

809def import_top_level_modules(): 

810 with exclude_local_folder_imports(*TOP_LEVEL_MODULES): 

811 for m in TOP_LEVEL_MODULES: 

812 try: 

813 __import__(m) 

814 except ImportError: # e.g. winreg 

815 pass