Skip to content

Commit c0bd416

Browse files
committed
utf-8
1 parent 6e48015 commit c0bd416

5 files changed

Lines changed: 948 additions & 6 deletions

File tree

j2py

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
""" j2py -> Java to Python compiler script.
4+
5+
This is all very ordinary. We import the package bits, open and read
6+
a file, translate it, and write it out.
7+
8+
"""
9+
import sys
10+
from argparse import ArgumentParser, ArgumentTypeError
11+
from collections import defaultdict
12+
from logging import _levelNames as logLevels, exception, warning, info, basicConfig
13+
from os import path, makedirs
14+
from time import time
15+
16+
from java2python.compiler import Module, buildAST, transformAST
17+
from java2python.config import Config
18+
from java2python.lib import escapes
19+
20+
import codecs
21+
version = '0.5.1'
22+
23+
24+
def logLevel(value):
25+
""" Returns a valid logging level or raises and exception. """
26+
msg = 'invalid loglevel: %r'
27+
try:
28+
lvl = int(value)
29+
except (ValueError, ):
30+
name = value.upper()
31+
if name not in logLevels:
32+
raise ArgumentTypeError(msg % value)
33+
lvl = logLevels[name]
34+
else:
35+
if lvl not in logLevels:
36+
raise ArgumentTypeError(msg % value)
37+
return lvl
38+
39+
40+
def configFromDir(inname, dirname):
41+
""" Returns a file name from the given config directory. """
42+
name = path.join(dirname, path.basename(path.splitext(inname)[0]))
43+
return '%s.py' % path.abspath(name)
44+
45+
46+
def runMain(options):
47+
""" Runs our main function with profiling if indicated by options. """
48+
if options.profile:
49+
import cProfile, pstats
50+
prof = cProfile.Profile()
51+
prof.runcall(runOneOrMany, options)
52+
stats = pstats.Stats(prof, stream=sys.stderr)
53+
stats.strip_dirs().sort_stats('cumulative')
54+
stats.print_stats().print_callers()
55+
return 0
56+
else:
57+
return runOneOrMany(options)
58+
59+
def runOneOrMany(options):
60+
""" Runs our main transformer with each of the input files. """
61+
infile, outfile = options.inputfile, options.outputfile
62+
63+
if infile and not isinstance(infile, file) and path.isdir(infile):
64+
if outfile and not isinstance(outfile, file) and not path.isdir(outfile):
65+
warning('Must specify output directory or stdout when using input directory.')
66+
return 2
67+
def walker(arg, dirname, files):
68+
for name in [name for name in files if name.endswith('.java')]:
69+
fullname = path.join(dirname, name)
70+
options.inputfile = fullname
71+
info('opening %s', fullname)
72+
if outfile and outfile != '-' and not isinstance(outfile, file):
73+
full = path.abspath(path.join(outfile, fullname))
74+
head, tail = path.split(full)
75+
tail = path.splitext(tail)[0] + '.py'
76+
if not path.exists(head):
77+
makedirs(head)
78+
options.outputfile = path.join(head, tail)
79+
runTransform(options)
80+
path.walk(infile, walker, None)
81+
return 0
82+
else:
83+
return runTransform(options)
84+
85+
86+
def runTransform(options):
87+
""" Compile the indicated java source with the given options. """
88+
timed = defaultdict(time)
89+
timed['overall']
90+
91+
filein = fileout = filedefault = '-'
92+
if options.inputfile and not isinstance(options.inputfile, file):
93+
filein = options.inputfile
94+
if options.outputfile and not isinstance(options.outputfile, file):
95+
fileout = options.outputfile
96+
elif fileout != filedefault:
97+
fileout = '%s.py' % (path.splitext(filein)[0])
98+
99+
configs = options.configs
100+
if options.configdirs and not isinstance(filein, file):
101+
for configdir in options.configdirs:
102+
dirname = configFromDir(filein, configdir)
103+
if path.exists(dirname):
104+
configs.insert(0, dirname)
105+
if options.includedefaults:
106+
configs.insert(0, 'java2python.config.default')
107+
108+
try:
109+
if filein != '-':
110+
source = codecs.open(filein,"r","utf-8").read()
111+
else:
112+
source = sys.stdin.read()
113+
except (IOError, ), exc:
114+
code, msg = exc.args[0:2]
115+
print 'IOError: %s.' % (msg, )
116+
return code
117+
118+
timed['comp']
119+
try:
120+
tree = buildAST(source)
121+
except (Exception, ), exc:
122+
exception('exception while parsing')
123+
return 1
124+
timed['comp_finish']
125+
126+
config = Config(configs)
127+
timed['xform']
128+
transformAST(tree, config)
129+
timed['xform_finish']
130+
131+
timed['visit']
132+
module = Module(config)
133+
module.sourceFilename = path.abspath(filein) if filein != '-' else None
134+
module.name = path.splitext(path.basename(filein))[0] if filein != '-' else '<stdin>'
135+
module.walk(tree)
136+
timed['visit_finish']
137+
138+
timed['encode']
139+
source = module.__unicode__()
140+
timed['encode_finish']
141+
timed['overall_finish']
142+
143+
if options.lexertokens:
144+
for idx, tok in enumerate(tree.parser.input.tokens):
145+
print >> sys.stderr, '{0} {1}'.format(idx, tok)
146+
print >> sys.stderr
147+
148+
if options.javaast:
149+
tree.dump(sys.stderr)
150+
print >> sys.stderr
151+
152+
if options.pytree:
153+
module.dumpRepr(sys.stderr)
154+
print >> sys.stderr
155+
156+
if not options.skipsource:
157+
if fileout == filedefault:
158+
output = sys.stdout
159+
else:
160+
output = open(fileout, 'w')
161+
module.name = path.splitext(filein)[0] if filein != '-' else '<stdin>'
162+
print >> output, source
163+
164+
if not options.skipcompile:
165+
try:
166+
compile(source, '<string>', 'exec')
167+
except (SyntaxError, ), ex:
168+
warning('Generated source has invalid syntax. %s', ex)
169+
else:
170+
info('Generated source has valid syntax.')
171+
172+
info('Parse: %.4f seconds', timed['comp_finish'] - timed['comp'])
173+
info('Visit: %.4f seconds', timed['visit_finish'] - timed['visit'])
174+
info('Transform: %.4f seconds', timed['xform_finish'] - timed['xform'])
175+
info('Encode: %.4f seconds', timed['encode_finish'] - timed['encode'])
176+
info('Total: %.4f seconds', timed['overall_finish'] - timed['overall'])
177+
return 0
178+
179+
180+
def isWindows():
181+
""" True if running on Windows. """
182+
return sys.platform.startswith('win')
183+
184+
185+
def configLogging(loglevel):
186+
""" Configure the logging package. """
187+
fmt = '# %(levelname)s %(funcName)s: %(message)s'
188+
basicConfig(level=loglevel, format=fmt)
189+
190+
191+
def configColors(nocolor):
192+
""" Configure the color escapes. """
193+
if isWindows() or nocolor:
194+
escapes.clear()
195+
196+
197+
def configScript(argv):
198+
""" Return an options object from the given argument sequence. """
199+
parser = ArgumentParser(
200+
description='Translate Java source code to Python.',
201+
epilog='Refer to https://github.com/natural/java2python for docs and support.'
202+
)
203+
204+
add = parser.add_argument
205+
add(dest='inputfile', nargs='?',
206+
help='Read from INPUT. May use - for stdin (default).',
207+
metavar='INPUT', default=None)
208+
add(dest='outputfile', nargs='?',
209+
help='Write to OUTPUT. May use - for stdout (default).',
210+
metavar='OUTPUT', default=None)
211+
add('-c', '--config', dest='configs',
212+
help='Use CONFIG file or module. May be repeated.',
213+
metavar='CONFIG', default=[], action='append')
214+
add('-d', '--config-dir', dest='configdirs',
215+
help='Use DIR to match input filename with config filename.',
216+
metavar='DIR', default=[], action='append')
217+
add('-f', '--profile', dest='profile',
218+
help='Profile execution and print results to stderr.',
219+
default=False, action='store_true')
220+
add('-j', '--java-ast', dest='javaast',
221+
help='Print java source AST tree to stderr.',
222+
default=False, action='store_true')
223+
add('-k', '--skip-compile', dest='skipcompile',
224+
help='Skip compile check on translated source.',
225+
default=False, action='store_true')
226+
add('-l', '--log-level', dest='loglevel',
227+
help='Set log level by name or value.',
228+
default='WARN', type=logLevel)
229+
add('-n', '--no-defaults', dest='includedefaults',
230+
help='Ignore default configuration module.',
231+
default=True, action='store_false')
232+
add('-p', '--python-tree', dest='pytree',
233+
help='Print python object tree to stderr.',
234+
default=False, action='store_true')
235+
add('-r', '--no-color', dest='nocolor',
236+
help='Disable color output.' +\
237+
(' No effect on Win OS.' if isWindows() else ''),
238+
default=False, action='store_true')
239+
add('-s', '--skip-source', dest='skipsource',
240+
help='Skip writing translated source; useful when printing trees',
241+
default=False, action='store_true')
242+
add('-t', '--lexer-tokens', dest='lexertokens',
243+
help='Print lexer tokens to stderr.',
244+
default=False, action='store_true')
245+
add('-v', '--version', action='version', version='%(prog)s ' + version)
246+
247+
ns = parser.parse_args(argv)
248+
if ns.inputfile == '-':
249+
ns.inputfile = sys.stdin
250+
if ns.outputfile == '-':
251+
ns.outputfile = sys.stdout
252+
253+
configColors(ns.nocolor)
254+
configLogging(ns.loglevel)
255+
return ns
256+
257+
258+
if __name__ == '__main__':
259+
sys.exit(runMain(configScript(sys.argv[1:])))

java2python/compiler/template.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919

2020
from java2python.lang import tokens
2121
from java2python.lib import FS, colors
22-
23-
22+
from java2python.utils import *;
2423
class Factory(object):
2524
""" Factory -> creates pre-configured callables for new block instances.
2625
@@ -73,7 +72,7 @@ def __init__(cls, name, bases, namespace):
7372
except (AttributeError, ):
7473
pass
7574

76-
75+
@python_2_unicode_compatible
7776
class Base(object):
7877
""" Base -> base class for formatting Python output.
7978
@@ -290,7 +289,7 @@ def wrapper(*a, **b):
290289
yield value
291290
return wrapper
292291

293-
292+
@python_2_unicode_compatible
294293
class Expression(Base):
295294
""" Expression -> formatting for Python expressions. """
296295

@@ -321,7 +320,12 @@ def __repr__(self):
321320

322321
def __str__(self):
323322
""" Returns the Python source code representation of this template. """
324-
return self.fs.format(left=self.left, right=self.right) + self.tail
323+
#print "=========="
324+
fs=unicode(self.fs)
325+
#print fs,":",self.left,",",self.right
326+
#print type(fs),type(self.left),type(self.right)
327+
r=fs.format(left=self.left, right=self.right)
328+
return r + self.tail
325329

326330
def dump(self, fd, level=0):
327331
""" Writes the Python source code for this template to the given file. """

java2python/compiler/visitor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
from java2python.lang import tokens
2121
from java2python.lib import FS
22-
22+
from java2python.utils import *
2323

2424
class Memo(object):
2525
""" Memo -> AST walking luggage. """

java2python/utils/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import six
2+
def python_2_unicode_compatible(klass):
3+
"""
4+
A decorator that defines __unicode__ and __str__ methods under Python 2.
5+
Under Python 3 it does nothing.
6+
7+
To support Python 2 and 3 with a single code base, define a __str__ method
8+
returning text and apply this decorator to the class.
9+
"""
10+
if six.PY2:
11+
if '__str__' not in klass.__dict__:
12+
raise ValueError("@python_2_unicode_compatible cannot be applied "
13+
"to %s because it doesn't define __str__()." %
14+
klass.__name__)
15+
klass.__unicode__ = klass.__str__
16+
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
17+
return klass

0 commit comments

Comments
 (0)