- 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?
How can I create a new build system to run python from project root?
Terminus: How to run python with customized rules?
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.