Sublime Forum

How can I create a new build system to run python from project root?

#1
  • For example, I have a project like “project_path/package/sample.py”.
  • I want build when I editing “sample.py”, from the project root, like
    project_path> python -m package.sample.
    (in order to make the python import path correct)
  • I’ve tried create a build file, but is that possible to get “package.sample”? I’m stuck here.
    I can get the project path, and the file name, but I don’t know how to combine it.
  • Or I have to send the path to a bat file?
0 Likes

Terminus: How to run python with customized rules?
#2

The first thing that comes to mind is that variables in sublime-build files support regex substitution in the same manner as snippets, so that might be a possibility. However that might be kind of complicated since it would require a couple of different expressions (remove path, remove extension, replace path separators with a period). I played with it a bit but couldn’t come up with anything workable.

A second alternative would be to have your build execute an external script instead, as you mentioned. The benefit there is that you can manipulate things as you like.

You can also use a custom build target to do the same thing. An example of that would be the following plugin code:

import sublime
import sublime_plugin

import os

from Default.exec import ExecCommand


class RelativePythonExecCommand(ExecCommand):
    def run(self, **kwargs):
        # Get the standard list of build variables and construct a
        # relative path to the current file based on the first open
        # folder
        var_list = self.window.extract_variables()
        rel_name = os.path.relpath(var_list["file"], var_list["folder"])

        # Remove the extension and replace path separators with periods
        rel_name = os.path.splitext(rel_name)[0].replace(os.path.sep, '.')

        # Set up a new variable and then expand it in the keyword args
        var_list["module_name"] = rel_name
        kwargs = sublime.expand_variables(kwargs, var_list)

        # Run the build
        super().run(**kwargs)

Once it’s in place (see this video if you’re not sure how to do that) you could use a build system like this to do what you want (only tested on Linux, but it should work on all platforms):

{
    "target": "relative_python_exec",
    "cancel": {"kill": true},

    "shell_cmd": "python -um \"\\${module_name}\"",
    "working_dir": "${folder}",

    "file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
    "selector": "source.python",

    "env": {"PYTHONIOENCODING": "utf-8"},
}

The target and cancel keys tell Sublime to use our custom command to run the build (and how to cancel it), and make sure to set working_dir (which the default Python build system doesn’t do) to make sure that the build starts at the correct location.

The custom command creates a new variable named module_name that has the text that you want; in order to expand it the build needs to quote the $ in its name so that Sublime knows to not expand it for us automatically. Since the file is JSON, \$ would be considered an (invalid) JSON character escape, so the variable is listed as \\${module_name} instead.

One thing to note is that it’s important that your project be open in the side bar; it won’t work if the first folder in the side bar isn’t your project folder, or for any python files that are outside of that folder (because the relative path will be calculated wrong).

If you’re new to custom targets, this video playlist covers build systems in general, and there are videos included in it that cover things like custom builds, how to cancel them, and the advanced custom target template used here to simplify the code.

5 Likes

#3

Thank you very much for your detailed reply.
I need a little time to try it.:ghost:

0 Likes

#4

Yes, it works!
You’re so good, love u dude!

1 Like