Sublime Text With Python

I've been using Sublime Text for a long time and have generally been very happy with it, but I never took the time to explore some of its more advanced features. I finally sat down and decided to get it working exactly right for a python project and I found that while there are many excellent resources, there's no clear "This is precisely how to do this" type of page. I figured I could write one of those, explain what the configuration does, and hopefully that would help someone else.

Specifically, this post will describe how to set up a Python project in Sublime Text with a lot of the features of an "800lb Gorilla IDE" but without all the same memory overhead. This will be accomplished with the following series of Sublime Text plug-ins:

The following is an example configuration file for a small python project I've been working on. It is saved as <my-project>.sublime-project in the root directory of the project:

{
    "folders":
    [
        {
            "path": ".",
            "name": "Robotnik",
            "folder_exclude_patterns": ["__pycache__", "*.egg-info", ".pytest_cache"],
        }
    ],
    "settings":
    {
        "SublimeLinter.linters.pylint.executable":   "~/.virtualenvs/$project_base_name/bin/pylint",
        "SublimeLinter.linters.pylint.python":       "~/.virtualenvs/$project_base_name/bin/python",
        "SublimeLinter.linters.pylint.args":         ["--disable=C0111"],
        "SublimeLinter.linters.flake8.executable":   "~/.virtualenvs/$project_base_name/bin/flake8",
        "SublimeLinter.linters.flake8.python":       "~/.virtualenvs/$project_base_name/bin/python",
        "SublimeLinter.linters.mypy.executable":     "~/.virtualenvs/$project_base_name/bin/mypy",
        "SublimeLinter.linters.mypy.python":         "~/.virtualenvs/$project_base_name/bin/python",
        "SublimeLinter.linters.mypy.args":           ["--ignore-missing-imports", "--strict", "--allow-untyped-decorators"],

        "python_virtualenv":                         "~/.virtualenvs/$project_base_name",
        "python_interpreter":                        "~/.virtualenvs/$project_base_name/bin/python",
        "python_package_paths":
        [
            "",
            "~/.virtualenvs/$project_base_name/lib/python37.zip",
            "~/.virtualenvs/$project_base_name/lib/python3.7",
            "~/.virtualenvs/$project_base_name/lib/python3.7/lib-dynload",
            "~/.virtualenvs/$project_base_name/lib/python3.7/site-packages",
            "/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7"
        ],
    },
    "build_systems":
    [
        {
            "name": "All Unit Tests (With Coverage)",
            "shell_cmd": "~/.virtualenvs/$project_base_name/bin/pip install -e $project_path; ~/.virtualenvs/$project_base_name/bin/pytest -s -vv --cov-report=term-missing --cov-branch --cov=robotnik $project_path/tests/unit"
        },
        {
            "name": "All Tests (via Vagrant)",
            "shell_cmd": "VAGRANT_CWD=$project_path/tests/integration/virtual-testing-env vagrant ssh -c 'pip install -e /develop/robotnik; pytest -s -vv --cov-report=term-missing --cov-branch --cov=robotnik /develop/robotnik/tests'"
        }
    ]
}

There are several very cool things here:

  • First and foremost, you can see how pylint, flake8, and mypy are configured. Together these provide everything from type-checking to style validation. To make those plugins work, you will need to install them in your virtualenv: pip install flake8 pylint mypy
  • Jedi is configured by giving it python_virtualenv, python_interpreter, and python_package_paths
  • The way to get python_package_paths is to execute the following: python -c "import sys; print(sys.path);"
  • Note that the build systems run a battery of pytest tests. The second of which actually executes the integration tests on a vagrant box running on the same machine as part of the project.