If you are okay with overriding builtins.__import__
and a simple regex to convert the print statements that don't have paren then you can do the following. Note that this doesn't actually change any file, just when you import them it'll read the code into a string, tweak that string, then send the fixed code to the compiler/importer
import re
import sys
if sys.version_info >= (3, 0):
import lib2to3
from lib2to3 import main, refactor
import os
import types
import builtins
import sys
import importlib
cache = {}
prevImport = builtins.__import__
def customImport(path, *args, **kwargs):
#print (path, args, kwargs)
try:
return fimport(path + ".py")
except:
return prevImport(path, *args, **kwargs)
def reload(filename):
fimport(filename.__file__, forceReload=True)
def fimport(filename, forceReload=False):
filename = os.path.abspath(filename)
modulePath = os.path.splitext(os.path.basename(filename))[0]
if filename in cache and not forceReload:
execval, modifyTime, module = cache[filename]
if modifyTime == os.path.getmtime(filename):
return module
f = open(filename)
text = f.read() + "\n"
p = re.compile("print")
res = []
curI = 0
for m in p.finditer(text):
i = m.start()
res.append(text[curI:i])
curI = i
pieceTmp = text[i:].split("\n")[0]
piece = text[i:].split("\n")[0].split("#")[0]
pieceAfter = piece[len('print'):].strip()
if pieceAfter[0] != '(':
resLine = "print" + "(" + pieceAfter + ")" + "\n"
res.append(resLine)
else:
res.append(pieceTmp)
curI += len(pieceTmp)+1
text = "".join(res)
f.close()
'''
# this code can run lib2to3 if you want but just for replacing prints that is not needed
#fixes = sorted(lib2to3.refactor.get_fixers_from_package('lib2to3.fixes'))
fixes = ['lib2to3.fixes.fix_print']
rt = lib2to3.main.StdoutRefactoringTool(fixes, {}, [], False, False)
res = str(rt.refactor_string(text, name=modulePath))
'''
res = text
res = compile(res, '<string>', 'exec')
module = types.ModuleType(modulePath)
module.__file__ = filename
cache[filename] = (res, os.path.getmtime(filename), module)
exec(res, module.__dict__)
return module
builtins.__import__ = customImport
importlib.reload = reload
If you save this code to, say, pastimport.py, then lets say I have some file named juniper.py:
def wow(a):
print a
Now if I want to call juniper.py from python3, I can just do
import pastimport
import juniper
juniper.wow("bean")
And it'll run :)
This could probably be faster and be more like the typical imports with caching and logging and stuff but I don't understand exactly how and when the pyc files are generated yet. There might also be edge cases with c plugins and such I'm not sure. So feel free to suggest improvements, but at least this is a proof of concept. I think you should be able to actually tweak the interpreter inputs and the values of the current file but I'm fiddling with that right now.
Also technically this lets you import any python2 file (2to3 fixes xrange, print, etc.) and if those files import other python2 files they'll be converted too since this overrides the import everyone uses. You can also implement arbitrary operator overloading, require static typing, actually require braces, and technically even import code from other languages or change python altogether with this single import. But I digress