Search code examples
pythonsetuptoolspython-babel

Extract messages programmatically with PyBabel


Right now, I’m extracting messages using

pybabel extract -F babel.cfg -o messages.pot .

This walks through all my Python files and extracts messages correctly. However, I call this through subprocess.call(), which is pretty ugly, given PyBbel is also written in Python.

I took a look into PyBabel, and it uses setuptools comands to do its work. I could copy the extract_messages.run() method to my Python script, but it doesn’t feel too elegant. Is there a better way to do it? There are tons of articles on how to create new setuptools commands, but no one writes about invoking them…


Solution

  • Maybe this is what you are looking for: How do i run the python 'sdist' command from within a python automated script without using subprocess?

    I'm going to show a few alternatives to run the Babel Python code, without creating a new subprocess, from higher to lower level.

    This is kind of a hack, taken from the answers linked above:

    from setuptools.dist import Distribution
    from babel.messages.frontend import extract_messages
    
    dist = Distribution({'name': 'my-project', 'version': '1.0.0'}) # etc.
    dist.script_name = 'setup.py'
    cmd = extract_messages(dist)
    cmd.ensure_finalized()
    cmd.run()  # TODO: error handling
    

    pylabel script actually does something like this:

    from babel.messages.frontend import main
    
    sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
    sys.exit(main())
    

    But you can avoid sending the commands over the sys.argv and actually call the CommandInterface python code from babel.

    This is my favorite way to call it:

    from babel.messages.frontend import CommandLineInterface
    
    CommandLineInterface().run(['pybabel','extract','-F','babel.cfg','-k','lazy_gettext','-o','messages.pot','sample_project'])
    CommandLineInterface().run(['pybabel','init','-i','messages.pot','-d','translations','-l','en'])
    CommandLineInterface().run(['pybabel','compile','-d','translations'])
    CommandLineInterface().run(['pybabel','update','-d','translations'])
    

    That's the closest you can get to the low level code, unless you want to start copy/pasting and customizing the python code. Again, this is a 100% python solution, and it doesn't call a new process.

    Good luck