Attempt to find or guess the path to the Arduino binary .

def get_arduino_command():
    """Attempt to find or guess the path to the Arduino binary."""
    global arduino_cmd
    if not arduino_cmd:
        if platform.system() == "Darwin":
            arduino_cmd_guesses = ["/Applications/Arduino.app/Contents/MacOS/Arduino"]
        elif platform.system() == "Windows":
            arduino_cmd_guesses = [
                "c:\Program Files\Arduino\Arduino_debug.exe",
                "c:\Program Files\Arduino\Arduino.exe",
                "c:\Program Files (x86)\Arduino\Arduino_debug.exe",
                "c:\Program Files (x86)\Arduino\Arduino.exe"
            ]
        else:
            arduino_cmd_guesses = []

        for guess in arduino_cmd_guesses:
            if os.path.exists(guess):
                logging.info("Found Arduino command at %s", guess)
                arduino_cmd = guess
                break
        else:
            logging.info("Couldn't find Arduino command; hoping it's on the path!")
            arduino_cmd = "arduino"
    return arduino_cmd


Attempt to guess a port name that we might find an Arduino on .

def guess_port_name():
    """Attempt to guess a port name that we might find an Arduino on."""
    portname = None
    if platform.system() == "Windows":
        import _winreg as winreg
        key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM")
        # We'll guess it's the last COM port.
        for i in itertools.count():
            try:
                portname = winreg.EnumValue(key, i)[1]
            except WindowsError:
                break
    else:
        # We'll guess it's the first non-bluetooth tty. or cu. prefixed device
        ttys = [filename for filename in os.listdir("/dev")
                if (filename.startswith("tty.") or filename.startswith("cu."))
                and not "luetooth" in filename]
        ttys.sort(key=lambda k:(k.startswith("cu."), k))
        if ttys:
            portname = "/dev/" + ttys[0]
    logging.info("Guessing port name as %s", portname)
    return portname


def do_HEAD(self):
        """Send response headers"""
        if self.path != "/":
            return SimpleHTTPServer.SimpleHTTPRequestHandler.do_HEAD(self)
        self.send_response(200)
        self.send_header("content-type", "text/html;charset=utf-8")
        self.send_header('Access-Control-Allow-Origin', '*')
        self.end_headers()

def do_HEAD ( self ) : if self . path != "/" : return SimpleHTTPServer . SimpleHTTPRequestHandler . do_HEAD ( self ) self . send_response ( 200 ) self . send_header ( "content-type" , "text/html;charset=utf-8" ) self . send_header ( 'Access-Control-Allow-Origin' , '*' ) self . end_headers ( )


def do_GET(self):
        """Send page text"""
        if self.path != "/":
            return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
        else:
            self.send_response(302)
            self.send_header("Location", "/blockly/apps/blocklyduino/index.html")
            self.end_headers()

def do_GET ( self ) : if self . path != "/" : return SimpleHTTPServer . SimpleHTTPRequestHandler . do_GET ( self ) else : self . send_response ( 302 ) self . send_header ( "Location" , "/blockly/apps/blocklyduino/index.html" ) self . end_headers ( )


def do_POST(self):
        """Save new page text and display it"""
        if self.path != "/":
            return SimpleHTTPServer.SimpleHTTPRequestHandler.do_POST(self)

        options, args = parser.parse_args()

        length = int(self.headers.getheader('content-length'))
        if length:
            text = self.rfile.read(length)

            print "sketch to upload: " + text

            dirname = tempfile.mkdtemp()
            sketchname = os.path.join(dirname, os.path.basename(dirname)) + ".ino"
            f = open(sketchname, "wb")
            f.write(text + "\n")
            f.close()

            print "created sketch at %s" % (sketchname,)

            # invoke arduino to build/upload
            compile_args = [
                options.cmd or get_arduino_command(),
                "--upload",
                "--port",
                options.port or guess_port_name(),
            ]
            if options.board:
                compile_args.extend([
                    "--board",
                    options.board
                ])
            compile_args.append(sketchname)

            print "Uploading with %s" % (" ".join(compile_args))
            rc = subprocess.call(compile_args)

            if not rc == 0:
                print "arduino --upload returned " + `rc`
                self.send_response(400)
            else:
                self.send_response(200)
                self.send_header('Access-Control-Allow-Origin', '*')
                self.end_headers()
        else:
            self.send_response(400)

def
do_POST
(
self
)
:
if
self
.
path
!=
"/"
:
return
SimpleHTTPServer
.
SimpleHTTPRequestHandler
.
do_POST
(
self
)
options
,
args
=
parser
.
parse_args
(
)
length
=
int
(
self
.
headers
.
getheader
(
'content-length'
)
)
if
length
:
text
=
self
.
rfile
.
read
(
length
)
print
"sketch to upload: "
+
text
dirname
=
tempfile
.
mkdtemp
(
)
sketchname
=
os
.
path
.
join
(
dirname
,
os
.
path
.
basename
(
dirname
)
)
+
".ino"
f
=
open
(
sketchname
,
"wb"
)
f
.
write
(
text
+
"\n"
)
f
.
close
(
)
print
"created sketch at %s"
%
(
sketchname
,
)
# invoke arduino to build/upload
compile_args
=
[
options
.
cmd
or
get_arduino_command
(
)
,
"--upload"
,
"--port"
,
options
.
port
or
guess_port_name
(
)
,
]
if
options
.
board
:
compile_args
.
extend
(
[
"--board"
,
options
.
board
]
)
compile_args
.
append
(
sketchname
)
print
"Uploading with %s"
%
(
" "
.
join
(
compile_args
)
)
rc
=
subprocess
.
call
(
compile_args
)
if
not
rc
==
0
:
print
"arduino --upload returned "
+
`rc`
self
.
send_response
(
400
)
else
:
self
.
send_response
(
200
)
self
.
send_header
(
'Access-Control-Allow-Origin'
,
'*'
)
self
.
end_headers
(
)
else
:
self
.
send_response
(
400
)


Import a file with full path specification . Allows one to import from any directory something __import__ does not do .

def import_path(fullpath):
  """Import a file with full path specification.
  Allows one to import from any directory, something __import__ does not do.

  Args:
      fullpath:  Path and filename of import.

  Returns:
      An imported module.
  """
  path, filename = os.path.split(fullpath)
  filename, ext = os.path.splitext(filename)
  sys.path.append(path)
  module = __import__(filename)
  reload(module)  # Might be out of date.
  del sys.path[-1]
  return module

def
import_path
(
fullpath
)
:
path
,
filename
=
os
.
path
.
split
(
fullpath
)
filename
,
ext
=
os
.
path
.
splitext
(
filename
)
sys
.
path
.
append
(
path
)
module
=
__import__
(
filename
)
reload
(
module
)
# Might be out of date.
del
sys
.
path
[
1
]
return
module


Generate . js files defining Blockly core and language messages .

def main():
  """Generate .js files defining Blockly core and language messages."""

  # Process command-line arguments.
  parser = argparse.ArgumentParser(description='Convert JSON files to JS.')
  parser.add_argument('--source_lang', default='en',
                      help='ISO 639-1 source language code')
  parser.add_argument('--source_lang_file',
                      default=os.path.join('json', 'en.json'),
                      help='Path to .json file for source language')
  parser.add_argument('--source_synonym_file',
                      default=os.path.join('json', 'synonyms.json'),
                      help='Path to .json file with synonym definitions')
  parser.add_argument('--output_dir', default='js/',
                      help='relative directory for output files')
  parser.add_argument('--key_file', default='keys.json',
                      help='relative path to input keys file')
  parser.add_argument('--quiet', action='store_true', default=False,
                      help='do not write anything to standard output')
  parser.add_argument('files', nargs='+', help='input files')
  args = parser.parse_args()
  if not args.output_dir.endswith(os.path.sep):
    args.output_dir += os.path.sep

  # Read in source language .json file, which provides any values missing
  # in target languages' .json files.
  source_defs = read_json_file(os.path.join(os.curdir, args.source_lang_file))
  # Make sure the source file doesn't contain a newline or carriage return.
  for key, value in source_defs.items():
    if _NEWLINE_PATTERN.search(value):
      print('ERROR: definition of {0} in {1} contained a newline character.'.
            format(key, args.source_lang_file))
      sys.exit(1)
  sorted_keys = source_defs.keys()
  sorted_keys.sort()

  # Read in synonyms file, which must be output in every language.
  synonym_defs = read_json_file(os.path.join(
      os.curdir, args.source_synonym_file))
  synonym_text = '\n'.join(['Blockly.Msg.{0} = Blockly.Msg.{1};'.format(
      key, synonym_defs[key]) for key in synonym_defs])

  # Create each output file.
  for arg_file in args.files:
    (_, filename) = os.path.split(arg_file)
    target_lang = filename[:filename.index('.')]
    if target_lang not in ('qqq', 'keys', 'synonyms'):
      target_defs = read_json_file(os.path.join(os.curdir, arg_file))

      # Verify that keys are 'ascii'
      bad_keys = [key for key in target_defs if not string_is_ascii(key)]
      if bad_keys:
        print(u'These keys in {0} contain non ascii characters: {1}'.format(
            filename, ', '.join(bad_keys)))

      # If there's a '\n' or '\r', remove it and print a warning.
      for key, value in target_defs.items():
        if _NEWLINE_PATTERN.search(value):
          print(u'WARNING: definition of {0} in {1} contained '
                'a newline character.'.
                format(key, arg_file))
          target_defs[key] = _NEWLINE_PATTERN.sub(' ', value)

      # Output file.
      outname = os.path.join(os.curdir, args.output_dir, target_lang + '.js')
      with codecs.open(outname, 'w', 'utf-8') as outfile:
        outfile.write(
            """// This file was automatically generated.  Do not modify.

'use strict';

goog.provide('Blockly.Msg.{0}');

goog.require('Blockly.Msg');

""".format(target_lang.replace('-', '.')))
        # For each key in the source language file, output the target value
        # if present; otherwise, output the source language value with a
        # warning comment.
        for key in sorted_keys:
          if key in target_defs:
            value = target_defs[key]
            comment = ''
            del target_defs[key]
          else:
            value = source_defs[key]
            comment = '  // untranslated'
          value = value.replace('"', '\\"')
          outfile.write(u'Blockly.Msg.{0} = "{1}";{2}\n'.format(
              key, value, comment))

        # Announce any keys defined only for target language.
        if target_defs:
          extra_keys = [key for key in target_defs if key not in synonym_defs]
          synonym_keys = [key for key in target_defs if key in synonym_defs]
          if not args.quiet:
            if extra_keys:
              print(u'These extra keys appeared in {0}: {1}'.format(
                  filename, ', '.join(extra_keys)))
            if synonym_keys:
              print(u'These synonym keys appeared in {0}: {1}'.format(
                  filename, ', '.join(synonym_keys)))

        outfile.write(synonym_text)

      if not args.quiet:
        print('Created {0}.'.format(outname))


Parses arguments and iterates over files .

def main():
  """Parses arguments and iterates over files.

  Raises:
    IOError: An I/O error occurred with an input or output file.
    InputError: Input JSON could not be parsed.
  """

  # Set up argument parser.
  parser = argparse.ArgumentParser(
      description='Removes duplicate key-value pairs from JSON files.')
  parser.add_argument('--suffix', default='',
                      help='optional suffix for output files; '
                      'if empty, files will be changed in place')
  parser.add_argument('files', nargs='+', help='input files')
  args = parser.parse_args()

  # Iterate over files.
  for filename in args.files:
    # Read in json using Python libraries.  This eliminates duplicates.
    print('Processing ' + filename + '...')
    try:
      with codecs.open(filename, 'r', 'utf-8') as infile:
        j = json.load(infile)
    except ValueError, e:
      print('Error reading ' + filename)
      raise InputError(file, str(e))

    # Built up output strings as an array to make output of delimiters easier.
    output = []
    for key in j:
      if key != '@metadata':
        output.append('\t"' + key + '": "' +
                      j[key].replace('\n', '\\n') + '"')

    # Output results.
    with codecs.open(filename + args.suffix, 'w', 'utf-8') as outfile:
      outfile.write('{\n')
      outfile.write(',\n'.join(output))
      outfile.write('\n}\n')


Creates a <target_lang > . xlf file for Soy .

def _create_xlf(target_lang):
    """Creates a <target_lang>.xlf file for Soy.

    Args:
        target_lang: The ISO 639 language code for the target language.
            This is used in the name of the file and in the metadata.

    Returns:
        A pointer to a file to which the metadata has been written.

    Raises:
        IOError: An error occurred while opening or writing the file.
    """
    filename = os.path.join(os.curdir, args.output_dir, target_lang + '.xlf')
    out_file = codecs.open(filename, 'w', 'utf-8')
    out_file.write("""<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
  <file original="SoyMsgBundle"
        datatype="x-soy-msg-bundle"
        xml:space="preserve"
        source-language="{0}"
        target-language="{1}">
    <body>""".format(args.source_lang, target_lang))
    return out_file


Closes a <target_lang > . xlf file created with create_xlf () .

def _close_xlf(xlf_file):
    """Closes a <target_lang>.xlf file created with create_xlf().

    This includes writing the terminating XML.

    Args:
        xlf_file: A pointer to a file created by _create_xlf().

    Raises:
        IOError: An error occurred while writing to or closing the file.
    """
    xlf_file.write("""
    </body>
  </file>
</xliff>
""")
    xlf_file.close()


Creates an . xlf file corresponding to the specified . json input file .

def _process_file(path_to_json, target_lang, key_dict):
    """Creates an .xlf file corresponding to the specified .json input file.

    The name of the input file must be target_lang followed by '.json'.
    The name of the output file will be target_lang followed by '.js'.

    Args:
        path_to_json: Path to the directory of xx.json files.
        target_lang: A IETF language code (RFC 4646), such as 'es' or 'pt-br'.
        key_dict: Dictionary mapping Blockly keys (e.g., Maze.turnLeft) to
            Closure keys (hash numbers).

    Raises:
        IOError: An I/O error occurred with an input or output file.
        InputError: Input JSON could not be parsed.
        KeyError: Key found in input file but not in key file.
    """
    keyfile = os.path.join(path_to_json, target_lang + '.json')
    j = read_json_file(keyfile)
    out_file = _create_xlf(target_lang)
    for key in j:
        if key != '@metadata':
            try:
                identifier = key_dict[key]
            except KeyError, e:
                print('Key "%s" is in %s but not in %s' %
                      (key, keyfile, args.key_file))
                raise e
            target = j.get(key)
            out_file.write(u"""
      <trans-unit id="{0}" datatype="html">
        <target>{1}</target>
      </trans-unit>""".format(identifier, target))
    _close_xlf(out_file)


def main():
    """Parses arguments and iterates over files."""

    # Set up argument parser.
    parser = argparse.ArgumentParser(description='Convert JSON files to JS.')
    parser.add_argument('--source_lang', default='en',
                        help='ISO 639-1 source language code')
    parser.add_argument('--output_dir', default='generated',
                        help='relative directory for output files')
    parser.add_argument('--key_file', default='json' + os.path.sep + 'keys.json',
                        help='relative path to input keys file')
    parser.add_argument('--template', default='template.soy')
    parser.add_argument('--path_to_jar',
                        default='..' + os.path.sep + 'apps' + os.path.sep
                        + '_soy',
                        help='relative path from working directory to '
                        'SoyToJsSrcCompiler.jar')
    parser.add_argument('files', nargs='+', help='input files')

    # Initialize global variables.
    global args
    args = parser.parse_args()

    # Make sure output_dir ends with slash.
    if (not args.output_dir.endswith(os.path.sep)):
      args.output_dir += os.path.sep

    # Read in keys.json, mapping descriptions (e.g., Maze.turnLeft) to
    # Closure keys (long hash numbers).
    key_file = open(args.key_file)
    key_dict = json.load(key_file)
    key_file.close()

    # Process each input file.
    print('Creating .xlf files...')
    processed_langs = []
    if len(args.files) == 1:
      # Windows does not expand globs automatically.
      args.files = glob.glob(args.files[0])
    for arg_file in args.files:
      (path_to_json, filename) = os.path.split(arg_file)
      if not filename.endswith('.json'):
        raise InputError(filename, 'filenames must end with ".json"')
      target_lang = filename[:filename.index('.')]
      if not target_lang in ('qqq', 'keys'):
        processed_langs.append(target_lang)
        _process_file(path_to_json, target_lang, key_dict)

    # Output command line for Closure compiler.
    if processed_langs:
      print('Creating .js files...')
      processed_lang_list = ','.join(processed_langs)
      subprocess.check_call([
          'java',
          '-jar', os.path.join(args.path_to_jar, 'SoyToJsSrcCompiler.jar'),
          '--locales', processed_lang_list,
          '--messageFilePathFormat', args.output_dir + '{LOCALE}.xlf',
          '--outputPathFormat', args.output_dir + '{LOCALE}.js',
          '--srcs', args.template])
      if len(processed_langs) == 1:
        print('Created ' + processed_lang_list + '.js in ' + args.output_dir)
      else:
        print('Created {' + processed_lang_list + '}.js in ' + args.output_dir)

      for lang in processed_langs:
        os.remove(args.output_dir + lang + '.xlf')
      print('Removed .xlf files.')


Read a JSON file as UTF - 8 into a dictionary discarding @metadata .

def read_json_file(filename):
  """Read a JSON file as UTF-8 into a dictionary, discarding @metadata.

  Args:
    filename: The filename, which must end ".json".

  Returns:
    The dictionary.

  Raises:
    InputError: The filename did not end with ".json" or an error occurred
        while opening or reading the file.
  """
  if not filename.endswith('.json'):
    raise InputError(filename, 'filenames must end with ".json"')
  try:
    # Read in file.
    with codecs.open(filename, 'r', 'utf-8') as infile:
      defs = json.load(infile)
    if '@metadata' in defs:
      del defs['@metadata']
    return defs
  except ValueError, e:
    print('Error reading ' + filename)
    raise InputError(filename, str(e))

def
read_json_file
(
filename
)
:
if
not
filename
.
endswith
(
'.json'
)
:
raise
InputError
(
filename
,
'filenames must end with ".json"'
)
try
:
# Read in file.
with
codecs
.
open
(
filename
,
'r'
,
'utf-8'
)
as
infile
:
defs
=
json
.
load
(
infile
)
if
'@metadata'
in
defs
:
del
defs
[
'@metadata'
]
return
defs
except
ValueError
,
e
:
print
(
'Error reading '
+
filename
)
raise
InputError
(
filename
,
str
(
e
)
)


Creates a qqq . json file with message documentation for translatewiki . net .

def _create_qqq_file(output_dir):
    """Creates a qqq.json file with message documentation for translatewiki.net.

    The file consists of key-value pairs, where the keys are message ids and
    the values are descriptions for the translators of the messages.
    What documentation exists for the format can be found at:
    http://translatewiki.net/wiki/Translating:Localisation_for_developers#Message_documentation

    The file should be closed by _close_qqq_file().

    Parameters:
        output_dir: The output directory.

    Returns:
        A pointer to a file to which a left brace and newline have been written.

    Raises:
        IOError: An error occurred while opening or writing the file.
    """
    qqq_file_name = os.path.join(os.curdir, output_dir, 'qqq.json')
    qqq_file = codecs.open(qqq_file_name, 'w', 'utf-8')
    print 'Created file: ' + qqq_file_name
    qqq_file.write('{\n')
    return qqq_file

def
_create_qqq_file
(
output_dir
)
:
qqq_file_name
=
os
.
path
.
join
(
os
.
curdir
,
output_dir
,
'qqq.json'
)
qqq_file
=
codecs
.
open
(
qqq_file_name
,
'w'
,
'utf-8'
)
print
'Created file: '
+
qqq_file_name
qqq_file
.
write
(
'{\n'
)
return
qqq_file


Closes a qqq . json file created and opened by _create_qqq_file () .

def _close_qqq_file(qqq_file):
    """Closes a qqq.json file created and opened by _create_qqq_file().

    This writes the final newlines and right brace.

    Args:
        qqq_file: A file created by _create_qqq_file().

    Raises:
        IOError: An error occurred while writing to or closing the file.
    """
    qqq_file.write('\n}\n')
    qqq_file.close()

def _close_qqq_file ( qqq_file ) : qqq_file . write ( '\n}\n' ) qqq_file . close ( )


Creates a <lang > . json file for translatewiki . net .

def _create_lang_file(author, lang, output_dir):
    """Creates a <lang>.json file for translatewiki.net.

    The file consists of metadata, followed by key-value pairs, where the keys
    are message ids and the values are the messages in the language specified
    by the corresponding command-line argument.  The file should be closed by
    _close_lang_file().

    Args:
        author: Name and email address of contact for translators.
        lang: ISO 639-1 source language code.
        output_dir: Relative directory for output files.

    Returns:
        A pointer to a file to which the metadata has been written.

    Raises:
        IOError: An error occurred while opening or writing the file.
    """
    lang_file_name = os.path.join(os.curdir, output_dir, lang + '.json')
    lang_file = codecs.open(lang_file_name, 'w', 'utf-8')
    print 'Created file: ' + lang_file_name
    # string.format doesn't like printing braces, so break up our writes.
    lang_file.write('{\n\t"@metadata": {')
    lang_file.write("""
\t\t"author": "{0}",
\t\t"lastupdated": "{1}",
\t\t"locale": "{2}",
\t\t"messagedocumentation" : "qqq"
""".format(author, str(datetime.now()), lang))
    lang_file.write('\t},\n')
    return lang_file


Closes a <lang > . json file created with _create_lang_file () .

def _close_lang_file(lang_file):
    """Closes a <lang>.json file created with _create_lang_file().

    This also writes the terminating left brace and newline.

    Args:
        lang_file: A file opened with _create_lang_file().

    Raises:
        IOError: An error occurred while writing to or closing the file.
    """
    lang_file.write('\n}\n')
    lang_file.close()

def _close_lang_file ( lang_file ) : lang_file . write ( '\n}\n' ) lang_file . close ( )


Creates a keys . json file mapping Closure keys to Blockly keys .

def _create_key_file(output_dir):
    """Creates a keys.json file mapping Closure keys to Blockly keys.

    Args:
        output_dir: Relative directory for output files.

    Raises:
        IOError: An error occurred while creating the file.
    """
    key_file_name = os.path.join(os.curdir, output_dir, 'keys.json')
    key_file = open(key_file_name, 'w')
    key_file.write('{\n')
    print 'Created file: ' + key_file_name
    return key_file

def
_create_key_file
(
output_dir
)
:
key_file_name
=
os
.
path
.
join
(
os
.
curdir
,
output_dir
,
'keys.json'
)
key_file
=
open
(
key_file_name
,
'w'
)
key_file
.
write
(
'{\n'
)
print
'Created file: '
+
key_file_name
return
key_file


Closes a key file created and opened with _create_key_file () .

def _close_key_file(key_file):
    """Closes a key file created and opened with _create_key_file().

    Args:
        key_file: A file created by _create_key_file().

    Raises:
        IOError: An error occurred while writing to or closing the file.
    """
    key_file.write('\n}\n')
    key_file.close()

def _close_key_file ( key_file ) : key_file . write ( '\n}\n' ) key_file . close ( )


Writes the output files for the given units .

def write_files(author, lang, output_dir, units, write_key_file):
    """Writes the output files for the given units.

    There are three possible output files:
    * lang_file: JSON file mapping meanings (e.g., Maze.turnLeft) to the
      English text.  The base name of the language file is specified by the
      "lang" command-line argument.
    * key_file: JSON file mapping meanings to Soy-generated keys (long hash
      codes).  This is only output if the parameter write_key_file is True.
    * qqq_file: JSON file mapping meanings to descriptions.

    Args:
        author: Name and email address of contact for translators.
        lang: ISO 639-1 source language code.
        output_dir: Relative directory for output files.
        units: A list of dictionaries with entries for 'meaning', 'source',
            'description', and 'keys' (the last only if write_key_file is true),
            in the order desired in the output files.
        write_key_file: Whether to output a keys.json file.

    Raises:
        IOError: An error occurs opening, writing to, or closing a file.
        KeyError: An expected key is missing from units.
    """
    lang_file = _create_lang_file(author, lang, output_dir)
    qqq_file = _create_qqq_file(output_dir)
    if write_key_file:
      key_file = _create_key_file(output_dir)
    first_entry = True
    for unit in units:
        if not first_entry:
            lang_file.write(',\n')
            if write_key_file:
              key_file.write(',\n')
            qqq_file.write(',\n')
        lang_file.write(u'\t"{0}": "{1}"'.format(
            unit['meaning'],
            unit['source'].replace('"', "'")))
        if write_key_file:
          key_file.write('"{0}": "{1}"'.format(unit['meaning'], unit['key']))
        qqq_file.write(u'\t"{0}": "{1}"'.format(
            unit['meaning'],
            unit['description'].replace('"', "'").replace(
                '{lb}', '{').replace('{rb}', '}')))
        first_entry = False
    _close_lang_file(lang_file)
    if write_key_file:
      _close_key_file(key_file)
    _close_qqq_file(qqq_file)

def
write_files
(
author
,
lang
,
output_dir
,
units
,
write_key_file
)
:
lang_file
=
_create_lang_file
(
author
,
lang
,
output_dir
)
qqq_file
=
_create_qqq_file
(
output_dir
)
if
write_key_file
:
key_file
=
_create_key_file
(
output_dir
)
first_entry
=
True
for
unit
in
units
:
if
not
first_entry
:
lang_file
.
write
(
',\n'
)
if
write_key_file
:
key_file
.
write
(
',\n'
)
qqq_file
.
write
(
',\n'
)
lang_file
.
write
(
u'\t"{0}": "{1}"'
.
format
(
unit
[
'meaning'
]
,
unit
[
'source'
]
.
replace
(
'"'
,
"'"
)
)
)
if
write_key_file
:
key_file
.
write
(
'"{0}": "{1}"'
.
format
(
unit
[
'meaning'
]
,
unit
[
'key'
]
)
)
qqq_file
.
write
(
u'\t"{0}": "{1}"'
.
format
(
unit
[
'meaning'
]
,
unit
[
'description'
]
.
replace
(
'"'
,
"'"
)
.
replace
(
'{lb}'
,
'{'
)
.
replace
(
'{rb}'
,
'}'
)
)
)
first_entry
=
False
_close_lang_file
(
lang_file
)
if
write_key_file
:
_close_key_file
(
key_file
)
_close_qqq_file
(
qqq_file
)


Converts a trans - unit XML node into a more convenient dictionary format .

def _parse_trans_unit(trans_unit):
    """Converts a trans-unit XML node into a more convenient dictionary format.

    Args:
        trans_unit: An XML representation of a .xlf translation unit.

    Returns:
        A dictionary with useful information about the translation unit.
        The returned dictionary is guaranteed to have an entry for 'key' and
        may have entries for 'source', 'target', 'description', and 'meaning'
        if present in the argument.

    Raises:
        InputError: A required field was not present.
    """

    def get_value(tag_name):
        elts = trans_unit.getElementsByTagName(tag_name)
        if not elts:
            return None
        elif len(elts) == 1:
            return ''.join([child.toxml() for child in elts[0].childNodes])
        else:
            raise InputError('', 'Unable to extract ' + tag_name)

    result = {}
    key = trans_unit.getAttribute('id')
    if not key:
        raise InputError('', 'id attribute not found')
    result['key'] = key

    # Get source and target, if present.
    try:
        result['source'] = get_value('source')
        result['target'] = get_value('target')
    except InputError, e:
        raise InputError(key, e.msg)

    # Get notes, using the from value as key and the data as value.
    notes = trans_unit.getElementsByTagName('note')
    for note in notes:
        from_value = note.getAttribute('from')
        if from_value and len(note.childNodes) == 1:
            result[from_value] = note.childNodes[0].data
        else:
            raise InputError(key, 'Unable to extract ' + from_value)

    return result


Builds list of translation units from input file .

def _process_file(filename):
    """Builds list of translation units from input file.

    Each translation unit in the input file includes:
    - an id (opaquely generated by Soy)
    - the Blockly name for the message
    - the text in the source language (generally English)
    - a description for the translator

    The Soy and Blockly ids are joined with a hyphen and serve as the
    keys in both output files.  The value is the corresponding text (in the
    <lang>.json file) or the description (in the qqq.json file).

    Args:
        filename: The name of an .xlf file produced by Closure.

    Raises:
        IOError: An I/O error occurred with an input or output file.
        InputError: The input file could not be parsed or lacked required
            fields.

    Returns:
        A list of dictionaries produced by parse_trans_unit().
    """
    try:
        results = []  # list of dictionaries (return value)
        names = []    # list of names of encountered keys (local variable)
        try:
            parsed_xml = minidom.parse(filename)
        except IOError:
            # Don't get caught by below handler
            raise
        except Exception, e:
            print
            raise InputError(filename, str(e))

        # Make sure needed fields are present and non-empty.
        for trans_unit in parsed_xml.getElementsByTagName('trans-unit'):
            unit = _parse_trans_unit(trans_unit)
            for key in ['description', 'meaning', 'source']:
                if not key in unit or not unit[key]:
                    raise InputError(filename + ':' + unit['key'],
                                     key + ' not found')
            if unit['description'].lower() == 'ibid':
              if unit['meaning'] not in names:
                # If the term has not already been described, the use of 'ibid'
                # is an error.
                raise InputError(
                    filename,
                    'First encountered definition of: ' + unit['meaning']
                    + ' has definition: ' + unit['description']
                    + '.  This error can occur if the definition was not'
                    + ' provided on the first appearance of the message'
                    + ' or if the source (English-language) messages differ.')
              else:
                # If term has already been described, 'ibid' was used correctly,
                # and we output nothing.
                pass
            else:
              if unit['meaning'] in names:
                raise InputError(filename,
                                 'Second definition of: ' + unit['meaning'])
              names.append(unit['meaning'])
              results.append(unit)

        return results
    except IOError, e:
        print 'Error with file {0}: {1}'.format(filename, e.strerror)
        sys.exit(1)


Sorts the translation units by their definition order in the template .

def sort_units(units, templates):
    """Sorts the translation units by their definition order in the template.

    Args:
        units: A list of dictionaries produced by parse_trans_unit()
            that have a non-empty value for the key 'meaning'.
        templates: A string containing the Soy templates in which each of
            the units' meanings is defined.

    Returns:
        A new list of translation units, sorted by the order in which
        their meaning is defined in the templates.

    Raises:
        InputError: If a meaning definition cannot be found in the
            templates.
    """
    def key_function(unit):
        match = re.search(
            '\\smeaning\\s*=\\s*"{0}"\\s'.format(unit['meaning']),
            templates)
        if match:
            return match.start()
        else:
            raise InputError(args.templates,
                             'msg definition for meaning not found: ' +
                             unit['meaning'])
    return sorted(units, key=key_function)


Parses arguments and processes the specified file .

def main():
    """Parses arguments and processes the specified file.

    Raises:
        IOError: An I/O error occurred with an input or output file.
        InputError: Input files lacked required fields.
    """
    # Set up argument parser.
    parser = argparse.ArgumentParser(description='Create translation files.')
    parser.add_argument(
        '--author',
        default='Ellen Spertus <ellen.spertus@gmail.com>',
        help='name and email address of contact for translators')
    parser.add_argument('--lang', default='en',
                        help='ISO 639-1 source language code')
    parser.add_argument('--output_dir', default='json',
                        help='relative directory for output files')
    parser.add_argument('--xlf', help='file containing xlf definitions')
    parser.add_argument('--templates', default=['template.soy'], nargs='+',
                        help='relative path to Soy templates, comma or space '
                        'separated (used for ordering messages)')
    global args
    args = parser.parse_args()

    # Make sure output_dir ends with slash.
    if (not args.output_dir.endswith(os.path.sep)):
      args.output_dir += os.path.sep

    # Process the input file, and sort the entries.
    units = _process_file(args.xlf)
    files = []
    for arg in args.templates:
      for filename in arg.split(','):
        filename = filename.strip();
        if filename:
          with open(filename) as myfile:
            files.append(' '.join(line.strip() for line in myfile))
    sorted_units = sort_units(units, ' '.join(files))

    # Write the output files.
    write_files(args.author, args.lang, args.output_dir, sorted_units, True)

    # Delete the input .xlf file.
    os.remove(args.xlf)
    print('Removed ' + args.xlf)