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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

272 statements  

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.entities 

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 from multiprocessing import SimpleQueue 

37 

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

39names on Python 2.) 

40 

41This is a cleaner alternative to this idiom (see 

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

43 

44 try: 

45 import queue 

46 except ImportError: 

47 import Queue as queue 

48 

49 

50Limitations 

51----------- 

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

53 

54 import dbm 

55 import dbm.dumb 

56 import dbm.gnu 

57 import collections.abc # on Py33 

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

59 

60""" 

61 

62from __future__ import absolute_import, division, print_function 

63 

64import sys 

65import logging 

66# imp was deprecated in python 3.6 

67if sys.version_info >= (3, 6): 

68 import importlib as imp 

69else: 

70 import imp 

71import contextlib 

72import copy 

73import os 

74 

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

76# by the application. 

77flog = logging.getLogger('future_stdlib') 

78_formatter = logging.Formatter(logging.BASIC_FORMAT) 

79_handler = logging.StreamHandler() 

80_handler.setFormatter(_formatter) 

81flog.addHandler(_handler) 

82flog.setLevel(logging.WARN) 

83 

84from future.utils import PY2, PY3 

85 

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

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

88# pickle (fast one) 

89# dbm 

90# urllib 

91# test 

92# email 

93 

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

95 

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

97# potential clashes between the old and new names: 

98# http 

99# html 

100# tkinter 

101# xmlrpc 

102# Keys: Py2 / real module names 

103# Values: Py3 / simulated module names 

104RENAMES = { 

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

106 # that provides StringIO and BytesIO 

107 # 'StringIO': 'io', # ditto 

108 # 'cPickle': 'pickle', 

109 '__builtin__': 'builtins', 

110 'copy_reg': 'copyreg', 

111 'Queue': 'queue', 

112 'future.moves.socketserver': 'socketserver', 

113 'ConfigParser': 'configparser', 

114 'repr': 'reprlib', 

115 'multiprocessing.queues': 'multiprocessing', 

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

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

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

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

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

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

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

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

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

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

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

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

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

129 # 'ttk': 'tkinter.ttk', 

130 # 'Tkinter': 'tkinter', 

131 '_winreg': 'winreg', 

132 'thread': '_thread', 

133 'dummy_thread': '_dummy_thread' if sys.version_info < (3, 9) else '_thread', 

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

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

136 # anydbm and whichdb are handled by fix_imports2 

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

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

139 # 'dbm': 'dbm.ndbm', 

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

141 'future.moves.xmlrpc': 'xmlrpc', 

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

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

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

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

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

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

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

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

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

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

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

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

154 # 'commands': 'subprocess', 

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

156 # 'robotparser' : 'urllib.robotparser', 

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

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

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

160 'future.moves.html': 'html', 

161 'future.moves.http': 'http', 

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

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

164 'future.moves._markupbase': '_markupbase', 

165 } 

166 

167 

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

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

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

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

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

173 

174 

175# Harmless renames that we can insert. 

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

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

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

179# mkarg 

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

181 

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

183 

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

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

186 ('collections', 'UserDict', 'UserDict', 'UserDict'), 

187 ('collections', 'UserString','UserString', 'UserString'), 

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

189 ('itertools', 'filterfalse','itertools', 'ifilterfalse'), 

190 ('itertools', 'zip_longest','itertools', 'izip_longest'), 

191 ('sys', 'intern','__builtin__', 'intern'), 

192 ('multiprocessing', 'SimpleQueue', 'multiprocessing.queues', 'SimpleQueue'), 

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

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

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

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

197 ('base64', 'encodebytes','base64', 'encodestring'), 

198 ('base64', 'decodebytes','base64', 'decodestring'), 

199 ('subprocess', 'getoutput', 'commands', 'getoutput'), 

200 ('subprocess', 'getstatusoutput', 'commands', 'getstatusoutput'), 

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

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

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

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

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

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

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

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

209 

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

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

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

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

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

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

216 ] 

217 

218 

219# A minimal example of an import hook: 

220# class WarnOnImport(object): 

221# def __init__(self, *args): 

222# self.module_names = args 

223# 

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

225# if fullname in self.module_names: 

226# self.path = path 

227# return self 

228# return None 

229# 

230# def load_module(self, name): 

231# if name in sys.modules: 

232# return sys.modules[name] 

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

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

235# sys.modules[name] = module 

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

237# return module 

238 

239 

240class RenameImport(object): 

241 """ 

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

243 """ 

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

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

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

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

248 # easily: 

249 RENAMER = True 

250 

251 def __init__(self, old_to_new): 

252 ''' 

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

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

255 ''' 

256 self.old_to_new = old_to_new 

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

258 assert (len(both) == 0 and 

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

260 'Ambiguity in renaming (handler not implemented)' 

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

262 

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

264 # Handles hierarchical importing: package.module.module2 

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

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

267 if fullname in new_base_names: 

268 return self 

269 return None 

270 

271 def load_module(self, name): 

272 path = None 

273 if name in sys.modules: 

274 return sys.modules[name] 

275 elif name in self.new_to_old: 

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

277 oldname = self.new_to_old[name] 

278 module = self._find_and_load_module(oldname) 

279 # module.__future_module__ = True 

280 else: 

281 module = self._find_and_load_module(name) 

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

283 sys.modules[name] = module 

284 return module 

285 

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

287 """ 

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

289 properly. 

290 """ 

291 bits = name.split('.') 

292 while len(bits) > 1: 

293 # Treat the first bit as a package 

294 packagename = bits.pop(0) 

295 package = self._find_and_load_module(packagename, path) 

296 try: 

297 path = package.__path__ 

298 except AttributeError: 

299 # This could be e.g. moves. 

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

301 if name in sys.modules: 

302 return sys.modules[name] 

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

304 

305 name = bits[0] 

306 module_info = imp.find_module(name, path) 

307 return imp.load_module(name, *module_info) 

308 

309 

310class hooks(object): 

311 """ 

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

313 after the 'with' block. 

314 

315 Use like this: 

316 

317 >>> from future import standard_library 

318 >>> with standard_library.hooks(): 

319 ... import http.client 

320 >>> import requests 

321 

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

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

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

325 imported modules (like requests). 

326 """ 

327 def __enter__(self): 

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

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

330 self.hooks_were_installed = detect_hooks() 

331 # self.scrubbed = scrub_py2_sys_modules() 

332 install_hooks() 

333 return self 

334 

335 def __exit__(self, *args): 

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

337 # restore_sys_modules(self.scrubbed) 

338 if not self.hooks_were_installed: 

339 remove_hooks() 

340 # scrub_future_sys_modules() 

341 

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

343# builtin modules names: 

344if PY2: 

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

346 

347 

348def is_py2_stdlib_module(m): 

349 """ 

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

351 This may not be reliable on all systems. 

352 """ 

353 if PY3: 

354 return False 

355 if not 'stdlib_path' in is_py2_stdlib_module.__dict__: 

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

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

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

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

360 # ignore it. 

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

362 'library: %s' % stdlib_paths) 

363 # Choose the first one arbitrarily 

364 is_py2_stdlib_module.stdlib_path = stdlib_paths[0] 

365 

366 if m.__name__ in sys.builtin_module_names: 

367 return True 

368 

369 if hasattr(m, '__file__'): 

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

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

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

373 return True 

374 

375 return False 

376 

377 

378def scrub_py2_sys_modules(): 

379 """ 

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

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

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

383 

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

385 with ambiguous names anyway ...) 

386 """ 

387 if PY3: 

388 return {} 

389 scrubbed = {} 

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

391 if not modulename in sys.modules: 

392 continue 

393 

394 module = sys.modules[modulename] 

395 

396 if is_py2_stdlib_module(module): 

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

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

399 del sys.modules[modulename] 

400 return scrubbed 

401 

402 

403def scrub_future_sys_modules(): 

404 """ 

405 Deprecated. 

406 """ 

407 return {} 

408 

409class suspend_hooks(object): 

410 """ 

411 Acts as a context manager. Use like this: 

412 

413 >>> from future import standard_library 

414 >>> standard_library.install_hooks() 

415 >>> import http.client 

416 >>> # ... 

417 >>> with standard_library.suspend_hooks(): 

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

419 

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

421 the context is left. 

422 """ 

423 def __enter__(self): 

424 self.hooks_were_installed = detect_hooks() 

425 remove_hooks() 

426 # self.scrubbed = scrub_future_sys_modules() 

427 return self 

428 

429 def __exit__(self, *args): 

430 if self.hooks_were_installed: 

431 install_hooks() 

432 # restore_sys_modules(self.scrubbed) 

433 

434 

435def restore_sys_modules(scrubbed): 

436 """ 

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

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

439 """ 

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

441 if len(clash) != 0: 

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

443 first = list(clash)[0] 

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

445 .format(first)) 

446 sys.modules.update(scrubbed) 

447 

448 

449def install_aliases(): 

450 """ 

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

452 aliases for better Py3 compatibility. 

453 """ 

454 if PY3: 

455 return 

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

457 # return 

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

459 __import__(newmodname) 

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

461 # top-level package: 

462 newmod = sys.modules[newmodname] 

463 # newmod.__future_module__ = True 

464 

465 __import__(oldmodname) 

466 oldmod = sys.modules[oldmodname] 

467 

468 obj = getattr(oldmod, oldobjname) 

469 setattr(newmod, newobjname, obj) 

470 

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

472 import urllib 

473 from future.backports.urllib import request 

474 from future.backports.urllib import response 

475 from future.backports.urllib import parse 

476 from future.backports.urllib import error 

477 from future.backports.urllib import robotparser 

478 urllib.request = request 

479 urllib.response = response 

480 urllib.parse = parse 

481 urllib.error = error 

482 urllib.robotparser = robotparser 

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

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

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

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

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

488 

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

490 try: 

491 import test 

492 except ImportError: 

493 pass 

494 try: 

495 from future.moves.test import support 

496 except ImportError: 

497 pass 

498 else: 

499 test.support = support 

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

501 

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

503 try: 

504 import dbm 

505 except ImportError: 

506 pass 

507 else: 

508 from future.moves.dbm import dumb 

509 dbm.dumb = dumb 

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

511 try: 

512 from future.moves.dbm import gnu 

513 except ImportError: 

514 pass 

515 else: 

516 dbm.gnu = gnu 

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

518 try: 

519 from future.moves.dbm import ndbm 

520 except ImportError: 

521 pass 

522 else: 

523 dbm.ndbm = ndbm 

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

525 

526 # install_aliases.run_already = True 

527 

528 

529def install_hooks(): 

530 """ 

531 This function installs the future.standard_library import hook into 

532 sys.meta_path. 

533 """ 

534 if PY3: 

535 return 

536 

537 install_aliases() 

538 

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

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

541 

542 # Add it unless it's there already 

543 newhook = RenameImport(RENAMES) 

544 if not detect_hooks(): 

545 sys.meta_path.append(newhook) 

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

547 

548 

549def enable_hooks(): 

550 """ 

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

552 ``future`` v1.0. 

553 """ 

554 install_hooks() 

555 

556 

557def remove_hooks(scrub_sys_modules=False): 

558 """ 

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

560 """ 

561 if PY3: 

562 return 

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

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

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

566 if hasattr(hook, 'RENAMER'): 

567 del sys.meta_path[i] 

568 

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

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

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

572 # v0.11.x. 

573 if scrub_sys_modules: 

574 scrub_future_sys_modules() 

575 

576 

577def disable_hooks(): 

578 """ 

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

580 ``future`` v1.0. 

581 """ 

582 remove_hooks() 

583 

584 

585def detect_hooks(): 

586 """ 

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

588 """ 

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

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

591 if present: 

592 flog.debug('Detected.') 

593 else: 

594 flog.debug('Not detected.') 

595 return present 

596 

597 

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

599# if not PY3: 

600# install_hooks() 

601 

602 

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

604 sys.py2_modules = {} 

605 

606def cache_py2_modules(): 

607 """ 

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

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

610 """ 

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

612 return 

613 assert not detect_hooks() 

614 import urllib 

615 sys.py2_modules['urllib'] = urllib 

616 

617 import email 

618 sys.py2_modules['email'] = email 

619 

620 import pickle 

621 sys.py2_modules['pickle'] = pickle 

622 

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

624 # try: 

625 # import test 

626 # except ImportError: 

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

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

629 

630 # import dbm 

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

632 

633 

634def import_(module_name, backport=False): 

635 """ 

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

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

638 returns the top-level module. 

639 

640 Example use: 

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

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

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

644 

645 Then: 

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

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

648 >>> # etc. 

649 

650 Use as follows: 

651 >>> package_name = import_(module_name) 

652 

653 On Py3, equivalent to this: 

654 

655 >>> import module_name 

656 

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

658 

659 >>> from future.moves import module_name 

660 

661 or to this if backport=True: 

662 

663 >>> from future.backports import module_name 

664 

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

666 The effect then is like this: 

667 

668 >>> from future.backports import module 

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

670 >>> module.submodule = submodule 

671 

672 Note that this would be a SyntaxError in Python: 

673 

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

675 

676 """ 

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

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

679 # this function: 

680 import importlib 

681 

682 if PY3: 

683 return __import__(module_name) 

684 else: 

685 # client.blah = blah 

686 # Then http.client = client 

687 # etc. 

688 if backport: 

689 prefix = 'future.backports' 

690 else: 

691 prefix = 'future.moves' 

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

693 

694 modules = [] 

695 for i, part in enumerate(parts): 

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

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

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

699 if i == 0: 

700 break 

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

702 

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

704 return modules[2] 

705 

706 

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

708 """ 

709 Example use: 

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

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

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

713 

714 Equivalent to this on Py3: 

715 

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

717 

718 and this on Py2: 

719 

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

721 

722 or: 

723 

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

725 

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

727 """ 

728 

729 if PY3: 

730 return __import__(module_name) 

731 else: 

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

733 prefix = 'future.backports' 

734 else: 

735 prefix = 'future.moves' 

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

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

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

739 if len(output) == 1: 

740 return output[0] 

741 else: 

742 return output 

743 

744 

745class exclude_local_folder_imports(object): 

746 """ 

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

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

749 

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

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

752 it's not needed any more?) 

753 """ 

754 def __init__(self, *args): 

755 assert len(args) > 0 

756 self.module_names = args 

757 # Disallow dotted module names like http.client: 

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

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

760 

761 def __enter__(self): 

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

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

764 if sys.version_info[0] < 3: 

765 return 

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

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

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

769 

770 # Look for the future source folder: 

771 for folder in self.old_sys_path: 

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

773 for subfolder in FUTURE_SOURCE_SUBFOLDERS]): 

774 # Found it. Remove it. 

775 sys.path.remove(folder) 

776 

777 # Ensure we import the system module: 

778 for m in self.module_names: 

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

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

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

782 # try: 

783 # del sys.modules[key] 

784 # except KeyError: 

785 # pass 

786 try: 

787 module = __import__(m, level=0) 

788 except ImportError: 

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

790 # winreg module is not available except on Windows. 

791 pass 

792 

793 def __exit__(self, *args): 

794 # Restore sys.path and sys.modules: 

795 sys.path = self.old_sys_path 

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

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

798 

799TOP_LEVEL_MODULES = ['builtins', 

800 'copyreg', 

801 'html', 

802 'http', 

803 'queue', 

804 'reprlib', 

805 'socketserver', 

806 'test', 

807 'tkinter', 

808 'winreg', 

809 'xmlrpc', 

810 '_dummy_thread', 

811 '_markupbase', 

812 '_thread', 

813 ] 

814 

815def import_top_level_modules(): 

816 with exclude_local_folder_imports(*TOP_LEVEL_MODULES): 

817 for m in TOP_LEVEL_MODULES: 

818 try: 

819 __import__(m) 

820 except ImportError: # e.g. winreg 

821 pass