Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: third_party/google-endpoints/past/translation/__init__.py

Issue 2666783008: Add google-endpoints to third_party/. (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # -*- coding: utf-8 -*-
2 """
3 past.translation
4 ==================
5
6 The ``past.translation`` package provides an import hook for Python 3 which
7 transparently runs ``futurize`` fixers over Python 2 code on import to convert
8 print statements into functions, etc.
9
10 It is intended to assist users in migrating to Python 3.x even if some
11 dependencies still only support Python 2.x.
12
13 Usage
14 -----
15
16 Once your Py2 package is installed in the usual module search path, the import
17 hook is invoked as follows:
18
19 >>> from past import autotranslate
20 >>> autotranslate('mypackagename')
21
22 Or:
23
24 >>> autotranslate(['mypackage1', 'mypackage2'])
25
26 You can unregister the hook using::
27
28 >>> from past.translation import remove_hooks
29 >>> remove_hooks()
30
31 Author: Ed Schofield.
32 Inspired by and based on ``uprefix`` by Vinay M. Sajip.
33 """
34
35 import imp
36 import logging
37 import marshal
38 import os
39 import sys
40 import copy
41 from lib2to3.pgen2.parse import ParseError
42 from lib2to3.refactor import RefactoringTool
43
44 from libfuturize import fixes
45
46
47 logger = logging.getLogger(__name__)
48 logger.setLevel(logging.DEBUG)
49
50 myfixes = (list(fixes.libfuturize_fix_names_stage1) +
51 list(fixes.lib2to3_fix_names_stage1) +
52 list(fixes.libfuturize_fix_names_stage2) +
53 list(fixes.lib2to3_fix_names_stage2))
54
55
56 # We detect whether the code is Py2 or Py3 by applying certain lib2to3 fixers
57 # to it. If the diff is empty, it's Python 3 code.
58
59 py2_detect_fixers = [
60 # From stage 1:
61 'lib2to3.fixes.fix_apply',
62 # 'lib2to3.fixes.fix_dict', # TODO: add support for utils.viewitems() etc. and move to stage2
63 'lib2to3.fixes.fix_except',
64 'lib2to3.fixes.fix_execfile',
65 'lib2to3.fixes.fix_exitfunc',
66 'lib2to3.fixes.fix_funcattrs',
67 'lib2to3.fixes.fix_filter',
68 'lib2to3.fixes.fix_has_key',
69 'lib2to3.fixes.fix_idioms',
70 'lib2to3.fixes.fix_import', # makes any implicit relative imports explici t. (Use with ``from __future__ import absolute_import)
71 'lib2to3.fixes.fix_intern',
72 'lib2to3.fixes.fix_isinstance',
73 'lib2to3.fixes.fix_methodattrs',
74 'lib2to3.fixes.fix_ne',
75 'lib2to3.fixes.fix_numliterals', # turns 1L into 1, 0755 into 0o755
76 'lib2to3.fixes.fix_paren',
77 'lib2to3.fixes.fix_print',
78 'lib2to3.fixes.fix_raise', # uses incompatible with_traceback() method on exceptions
79 'lib2to3.fixes.fix_renames',
80 'lib2to3.fixes.fix_reduce',
81 # 'lib2to3.fixes.fix_set_literal', # this is unnecessary and breaks Py2.6 s upport
82 'lib2to3.fixes.fix_repr',
83 'lib2to3.fixes.fix_standarderror',
84 'lib2to3.fixes.fix_sys_exc',
85 'lib2to3.fixes.fix_throw',
86 'lib2to3.fixes.fix_tuple_params',
87 'lib2to3.fixes.fix_types',
88 'lib2to3.fixes.fix_ws_comma',
89 'lib2to3.fixes.fix_xreadlines',
90
91 # From stage 2:
92 'lib2to3.fixes.fix_basestring',
93 # 'lib2to3.fixes.fix_buffer', # perhaps not safe. Test this.
94 # 'lib2to3.fixes.fix_callable', # not needed in Py3.2+
95 # 'lib2to3.fixes.fix_dict', # TODO: add support for utils.viewitems() etc.
96 'lib2to3.fixes.fix_exec',
97 # 'lib2to3.fixes.fix_future', # we don't want to remove __future__ import s
98 'lib2to3.fixes.fix_getcwdu',
99 # 'lib2to3.fixes.fix_imports', # called by libfuturize.fixes.fix_future_st andard_library
100 # 'lib2to3.fixes.fix_imports2', # we don't handle this yet (dbm)
101 # 'lib2to3.fixes.fix_input',
102 # 'lib2to3.fixes.fix_itertools',
103 # 'lib2to3.fixes.fix_itertools_imports',
104 'lib2to3.fixes.fix_long',
105 # 'lib2to3.fixes.fix_map',
106 # 'lib2to3.fixes.fix_metaclass', # causes SyntaxError in Py2! Use the one fr om ``six`` instead
107 'lib2to3.fixes.fix_next',
108 'lib2to3.fixes.fix_nonzero', # TODO: add a decorator for mapping __bool_ _ to __nonzero__
109 # 'lib2to3.fixes.fix_operator', # we will need support for this by e.g. e xtending the Py2 operator module to provide those functions in Py3
110 'lib2to3.fixes.fix_raw_input',
111 # 'lib2to3.fixes.fix_unicode', # strips off the u'' prefix, which removes a potentially helpful source of information for disambiguating unicode/byte stri ngs
112 # 'lib2to3.fixes.fix_urllib',
113 'lib2to3.fixes.fix_xrange',
114 # 'lib2to3.fixes.fix_zip',
115 ]
116
117
118 class RTs:
119 """
120 A namespace for the refactoring tools. This avoids creating these at
121 the module level, which slows down the module import. (See issue #117).
122
123 There are two possible grammars: with or without the print statement.
124 Hence we have two possible refactoring tool implementations.
125 """
126 _rt = None
127 _rtp = None
128 _rt_py2_detect = None
129 _rtp_py2_detect = None
130
131 @staticmethod
132 def setup():
133 """
134 Call this before using the refactoring tools to create them on demand
135 if needed.
136 """
137 if None in [RTs._rt, RTs._rtp]:
138 RTs._rt = RefactoringTool(myfixes)
139 RTs._rtp = RefactoringTool(myfixes, {'print_function': True})
140
141
142 @staticmethod
143 def setup_detect_python2():
144 """
145 Call this before using the refactoring tools to create them on demand
146 if needed.
147 """
148 if None in [RTs._rt_py2_detect, RTs._rtp_py2_detect]:
149 RTs._rt_py2_detect = RefactoringTool(py2_detect_fixers)
150 RTs._rtp_py2_detect = RefactoringTool(py2_detect_fixers,
151 {'print_function': True})
152
153
154 # We need to find a prefix for the standard library, as we don't want to
155 # process any files there (they will already be Python 3).
156 #
157 # The following method is used by Sanjay Vinip in uprefix. This fails for
158 # ``conda`` environments:
159 # # In a non-pythonv virtualenv, sys.real_prefix points to the installed Pyt hon.
160 # # In a pythonv venv, sys.base_prefix points to the installed Python.
161 # # Outside a virtual environment, sys.prefix points to the installed Python .
162
163 # if hasattr(sys, 'real_prefix'):
164 # _syslibprefix = sys.real_prefix
165 # else:
166 # _syslibprefix = getattr(sys, 'base_prefix', sys.prefix)
167
168 # Instead, we use the portion of the path common to both the stdlib modules
169 # ``math`` and ``urllib``.
170
171 def splitall(path):
172 """
173 Split a path into all components. From Python Cookbook.
174 """
175 allparts = []
176 while True:
177 parts = os.path.split(path)
178 if parts[0] == path: # sentinel for absolute paths
179 allparts.insert(0, parts[0])
180 break
181 elif parts[1] == path: # sentinel for relative paths
182 allparts.insert(0, parts[1])
183 break
184 else:
185 path = parts[0]
186 allparts.insert(0, parts[1])
187 return allparts
188
189
190 def common_substring(s1, s2):
191 """
192 Returns the longest common substring to the two strings, starting from the
193 left.
194 """
195 chunks = []
196 path1 = splitall(s1)
197 path2 = splitall(s2)
198 for (dir1, dir2) in zip(path1, path2):
199 if dir1 != dir2:
200 break
201 chunks.append(dir1)
202 return os.path.join(*chunks)
203
204 # _stdlibprefix = common_substring(math.__file__, urllib.__file__)
205
206
207 def detect_python2(source, pathname):
208 """
209 Returns a bool indicating whether we think the code is Py2
210 """
211 RTs.setup_detect_python2()
212 try:
213 tree = RTs._rt_py2_detect.refactor_string(source, pathname)
214 except ParseError as e:
215 if e.msg != 'bad input' or e.value != '=':
216 raise
217 tree = RTs._rtp.refactor_string(source, pathname)
218
219 if source != str(tree)[:-1]: # remove added newline
220 # The above fixers made changes, so we conclude it's Python 2 code
221 logger.debug('Detected Python 2 code: {0}'.format(pathname))
222 with open('/tmp/original_code.py', 'w') as f:
223 f.write('### Original code (detected as py2): %s\n%s' %
224 (pathname, source))
225 with open('/tmp/py2_detection_code.py', 'w') as f:
226 f.write('### Code after running py3 detection (from %s)\n%s' %
227 (pathname, str(tree)[:-1]))
228 return True
229 else:
230 logger.debug('Detected Python 3 code: {0}'.format(pathname))
231 with open('/tmp/original_code.py', 'w') as f:
232 f.write('### Original code (detected as py3): %s\n%s' %
233 (pathname, source))
234 try:
235 os.remove('/tmp/futurize_code.py')
236 except OSError:
237 pass
238 return False
239
240
241 class Py2Fixer(object):
242 """
243 An import hook class that uses lib2to3 for source-to-source translation of
244 Py2 code to Py3.
245 """
246
247 # See the comments on :class:future.standard_library.RenameImport.
248 # We add this attribute here so remove_hooks() and install_hooks() can
249 # unambiguously detect whether the import hook is installed:
250 PY2FIXER = True
251
252 def __init__(self):
253 self.found = None
254 self.base_exclude_paths = ['future', 'past']
255 self.exclude_paths = copy.copy(self.base_exclude_paths)
256 self.include_paths = []
257
258 def include(self, paths):
259 """
260 Pass in a sequence of module names such as 'plotrique.plotting' that,
261 if present at the leftmost side of the full package name, would
262 specify the module to be transformed from Py2 to Py3.
263 """
264 self.include_paths += paths
265
266 def exclude(self, paths):
267 """
268 Pass in a sequence of strings such as 'mymodule' that, if
269 present at the leftmost side of the full package name, would cause
270 the module not to undergo any source transformation.
271 """
272 self.exclude_paths += paths
273
274 def find_module(self, fullname, path=None):
275 logger.debug('Running find_module: {0}...'.format(fullname))
276 if '.' in fullname:
277 parent, child = fullname.rsplit('.', 1)
278 if path is None:
279 loader = self.find_module(parent, path)
280 mod = loader.load_module(parent)
281 path = mod.__path__
282 fullname = child
283
284 # Perhaps we should try using the new importlib functionality in Python
285 # 3.3: something like this?
286 # thing = importlib.machinery.PathFinder.find_module(fullname, path)
287 try:
288 self.found = imp.find_module(fullname, path)
289 except Exception as e:
290 logger.debug('Py2Fixer could not find {0}')
291 logger.debug('Exception was: {0})'.format(fullname, e))
292 return None
293 self.kind = self.found[-1][-1]
294 if self.kind == imp.PKG_DIRECTORY:
295 self.pathname = os.path.join(self.found[1], '__init__.py')
296 elif self.kind == imp.PY_SOURCE:
297 self.pathname = self.found[1]
298 return self
299
300 def transform(self, source):
301 # This implementation uses lib2to3,
302 # you can override and use something else
303 # if that's better for you
304
305 # lib2to3 likes a newline at the end
306 RTs.setup()
307 source += '\n'
308 try:
309 tree = RTs._rt.refactor_string(source, self.pathname)
310 except ParseError as e:
311 if e.msg != 'bad input' or e.value != '=':
312 raise
313 tree = RTs._rtp.refactor_string(source, self.pathname)
314 # could optimise a bit for only doing str(tree) if
315 # getattr(tree, 'was_changed', False) returns True
316 return str(tree)[:-1] # remove added newline
317
318 def load_module(self, fullname):
319 logger.debug('Running load_module for {0}...'.format(fullname))
320 if fullname in sys.modules:
321 mod = sys.modules[fullname]
322 else:
323 if self.kind in (imp.PY_COMPILED, imp.C_EXTENSION, imp.C_BUILTIN,
324 imp.PY_FROZEN):
325 convert = False
326 # elif (self.pathname.startswith(_stdlibprefix)
327 # and 'site-packages' not in self.pathname):
328 # # We assume it's a stdlib package in this case. Is this too br ittle?
329 # # Please file a bug report at https://github.com/PythonCharmer s/python-future
330 # # if so.
331 # convert = False
332 # in theory, other paths could be configured to be excluded here too
333 elif any([fullname.startswith(path) for path in self.exclude_paths]) :
334 convert = False
335 elif any([fullname.startswith(path) for path in self.include_paths]) :
336 convert = True
337 else:
338 convert = False
339 if not convert:
340 logger.debug('Excluded {0} from translation'.format(fullname))
341 mod = imp.load_module(fullname, *self.found)
342 else:
343 logger.debug('Autoconverting {0} ...'.format(fullname))
344 mod = imp.new_module(fullname)
345 sys.modules[fullname] = mod
346
347 # required by PEP 302
348 mod.__file__ = self.pathname
349 mod.__name__ = fullname
350 mod.__loader__ = self
351
352 # This:
353 # mod.__package__ = '.'.join(fullname.split('.')[:-1])
354 # seems to result in "SystemError: Parent module '' not loaded,
355 # cannot perform relative import" for a package's __init__.py
356 # file. We use the approach below. Another option to try is the
357 # minimal load_module pattern from the PEP 302 text instead.
358
359 # Is the test in the next line more or less robust than the
360 # following one? Presumably less ...
361 # ispkg = self.pathname.endswith('__init__.py')
362
363 if self.kind == imp.PKG_DIRECTORY:
364 mod.__path__ = [ os.path.dirname(self.pathname) ]
365 mod.__package__ = fullname
366 else:
367 #else, regular module
368 mod.__path__ = []
369 mod.__package__ = fullname.rpartition('.')[0]
370
371 try:
372 cachename = imp.cache_from_source(self.pathname)
373 if not os.path.exists(cachename):
374 update_cache = True
375 else:
376 sourcetime = os.stat(self.pathname).st_mtime
377 cachetime = os.stat(cachename).st_mtime
378 update_cache = cachetime < sourcetime
379 # # Force update_cache to work around a problem with it bein g treated as Py3 code???
380 # update_cache = True
381 if not update_cache:
382 with open(cachename, 'rb') as f:
383 data = f.read()
384 try:
385 code = marshal.loads(data)
386 except Exception:
387 # pyc could be corrupt. Regenerate it
388 update_cache = True
389 if update_cache:
390 if self.found[0]:
391 source = self.found[0].read()
392 elif self.kind == imp.PKG_DIRECTORY:
393 with open(self.pathname) as f:
394 source = f.read()
395
396 if detect_python2(source, self.pathname):
397 source = self.transform(source)
398 with open('/tmp/futurized_code.py', 'w') as f:
399 f.write('### Futurized code (from %s)\n%s' %
400 (self.pathname, source))
401
402 code = compile(source, self.pathname, 'exec')
403
404 dirname = os.path.dirname(cachename)
405 if not os.path.exists(dirname):
406 os.makedirs(dirname)
407 try:
408 with open(cachename, 'wb') as f:
409 data = marshal.dumps(code)
410 f.write(data)
411 except Exception: # could be write-protected
412 pass
413 exec(code, mod.__dict__)
414 except Exception as e:
415 # must remove module from sys.modules
416 del sys.modules[fullname]
417 raise # keep it simple
418
419 if self.found[0]:
420 self.found[0].close()
421 return mod
422
423 _hook = Py2Fixer()
424
425
426 def install_hooks(include_paths=(), exclude_paths=()):
427 if isinstance(include_paths, str):
428 include_paths = (include_paths,)
429 if isinstance(exclude_paths, str):
430 exclude_paths = (exclude_paths,)
431 assert len(include_paths) + len(exclude_paths) > 0, 'Pass at least one argum ent'
432 _hook.include(include_paths)
433 _hook.exclude(exclude_paths)
434 # _hook.debug = debug
435 enable = sys.version_info[0] >= 3 # enabled for all 3.x
436 if enable and _hook not in sys.meta_path:
437 sys.meta_path.insert(0, _hook) # insert at beginning. This could be mad e a parameter
438
439 # We could return the hook when there are ways of configuring it
440 #return _hook
441
442
443 def remove_hooks():
444 if _hook in sys.meta_path:
445 sys.meta_path.remove(_hook)
446
447
448 def detect_hooks():
449 """
450 Returns True if the import hooks are installed, False if not.
451 """
452 return _hook in sys.meta_path
453 # present = any([hasattr(hook, 'PY2FIXER') for hook in sys.meta_path])
454 # return present
455
456
457 class hooks(object):
458 """
459 Acts as a context manager. Use like this:
460
461 >>> from past import translation
462 >>> with translation.hooks():
463 ... import mypy2module
464 >>> import requests # py2/3 compatible anyway
465 >>> # etc.
466 """
467 def __enter__(self):
468 self.hooks_were_installed = detect_hooks()
469 install_hooks()
470 return self
471
472 def __exit__(self, *args):
473 if not self.hooks_were_installed:
474 remove_hooks()
475
476
477 class suspend_hooks(object):
478 """
479 Acts as a context manager. Use like this:
480
481 >>> from past import translation
482 >>> translation.install_hooks()
483 >>> import http.client
484 >>> # ...
485 >>> with translation.suspend_hooks():
486 >>> import requests # or others that support Py2/3
487
488 If the hooks were disabled before the context, they are not installed when
489 the context is left.
490 """
491 def __enter__(self):
492 self.hooks_were_installed = detect_hooks()
493 remove_hooks()
494 return self
495 def __exit__(self, *args):
496 if self.hooks_were_installed:
497 install_hooks()
498
OLDNEW
« no previous file with comments | « third_party/google-endpoints/past/tests/__init__.py ('k') | third_party/google-endpoints/past/types/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698