Jithin Johnson
Jithin Johnson

Reputation: 372

Run a python snippet with every run on sublime text

I have the following snippet which I use with every code I run.

import sys
sys.stdin, sys.stdout = open('input', 'r'), open('output', 'w')

Is there any way I can make these two lines run with every .py I run.

Also on Sublime Text this is the build configuration I used to redirect the output to a file, but it is not working.

{
    "cmd": ["/home/jithin/venv/bin/python", "-u", "$file" ,">","/home/jithin/temp/hackerrank/output"],
    "file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
    "selector": "source.python"
}
    

Is there any flags like --stdout to redirect the output of the code to a file.

NB. I cannot use include any lines in the code because I am practising competitive programming, so I usually copy and paste the code from the question to ST. If there was a differemt method I wouldn't need to include the above snippet everytime. Also for pasting back I could simply use Ctrl + A and Ctrl + V.

Upvotes: 2

Views: 208

Answers (1)

OdatNurd
OdatNurd

Reputation: 22791

As far as I can see, Python has no built in way to allow you to execute arbitrary script commands along with a full on script, so the options available boil down to either manually putting the boilerplate in every file or dynamically adding it there when you run the code.

Generally speaking, my recommendation would be create a snippet that inserts this code for you so that you don't have to manually copy and paste it. To do that, you can choose Tools > Developer > New Snippet... and replace the stub with the following, then save the file as a sublime-snippet file in the location Sublime suggests:

<snippet>
    <content><![CDATA[
import sys
sys.stdin, sys.stdout = open('input', 'r'), open('output', 'w')

]]></content>
    <tabTrigger>stub</tabTrigger>
    <description>Insert IO redirect stubs for competetive coding</description>
    <scope>source.python</scope>
</snippet>

Change the tab trigger as appropriate, but now in a new Python file stubTab will insert the code for you.

It is also possible to construct a build that will automatically add the stub onto the start of every Python file that's executed, if you'd rather do that (but the snippet is much easier).

For that, there is this plugin (see this video on installing plugins if you're not sure how to use one).


import sublime
import sublime_plugin

import os
import tempfile

from Default.exec import ExecCommand


_stub = """
import sys
sys.stdin, sys.stdout = open('input', 'r'), open('output', 'w')

"""


class PythonWithRedirectExecCommand(ExecCommand):
    def run(self, **kwargs):
        view = self.window.active_view()
        if view.file_name() is None or not os.path.exists(view.file_name()):
            return self.window.status_message("Cannot build; no file associated with the current view")

        self.tmp_file = self.get_temp_file(view)

        variables = {"temp_file": self.tmp_file}
        kwargs = sublime.expand_variables(kwargs, variables)

        super().run(**kwargs)

    def get_temp_file(self, view):
        handle, name = tempfile.mkstemp(text=True, suffix=".py")
        with os.fdopen(handle, mode="wt", encoding="utf-8") as handle:
            handle.write(_stub)
            with open(view.file_name()) as source:
                handle.write(source.read())

        return name

    # NOTE: If you are using Sublime Text 3, change this line and the
    #       next use to use finished instead of on_finished.
    def on_finished(self, proc):
        super().on_finished(proc)

        # If the current build didn't finish or it was manually killed, leave.
        if proc != self.proc or proc.killed:
            return

        try:
            # If the build succeeded, delete the temporary file; we will
            # leave it alone if the build fails so that it's possible to
            # navigate to it.
            exit_code = proc.exit_code()
            if exit_code == 0 or exit_code is None:
                os.remove(self.tmp_file)
        finally:
            self.tmp_file = None

This implements a replacement build target which, when executed, will create a temporary file containing the stub content as well as the contents of the current file.

Important note: This was developed in Sublime Text ~~4~~; it will also work in Sublime Text 3, but you need to change both references to on_finished to finished if you're using ST3; otherwise the plugin won't clean up temporary files. Probably also worth noting that for the reasons outlined below, if the run fails the temporary file isn't erased.

To use it, you want a build system similar to this (modify the command as appropriate; this is a generic build):

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

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

    "working_dir": "${file_path}",

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

    "windows": {
        "cmd": ["py", "-u", "\\$temp_file"],
    },
}

The important notes here are the target and cancel, which tie the build to the command provided by the plugin, the working_dir which makes sure that the current directory is the directory the current file is in (since that is where the input and output files are expected to be), and that the \\$temp_file is used to reference the temporary output file that contains the resulting code.

As a note on this, if an error occurs, the line numbers will be slightly off in the errors because they are referencing the file that has extra content at top, and if you navigate to it you'll open the temporary file, which is not the one you should probably be editing.

So, all things told, the snippet is probably the safest fastest way to go.

Upvotes: 1

Related Questions