Sublime Forum

[Question] Proper use of global variables

#1

So things are actually tracking along pretty well in plugin development land. I am almost done with my first “big” command, which extracts information about text around the point. All the extraction part is complete and I’m sure there’s ways to break it because the language I’m parsing has a lot of optional things, it’s at least a good first cut and modestly robust.

The idea is that this is a type of special copy command, and then I will made various pasting commands that pastes the information back into the text file in different formats. So I created a class that holds this interface information and the command I created reads into it. However currently the variable that’s got the instantiated class is local to the copy command.

Is it permitted to declare this variable to have global scope? I need to pass this information to the next command and because I’m working within the API I can’t just hand it over in the command invocation because it’s not part of the view. If declaring it global is alright, are there any caveats associated with it (naming, etc)? I think it’s reasonable safe. There’s only ever one copy of this thing, I don’t need to behave as part of a kill-ring stack or anything. I just need to get it from one command (the copy one) to the next (the paste-as-x set of commands.)

Thanks! I’ve been very pleased with how this is rolling along. Starting to get a real handle on regular expressions too as that was required for parsing the text properly.

1 Like

#2

In Python nothing is truly “global” in the traditional sense of the word, in that it doesn’t escape outside of the confines of the current file (module) without someone taking explicit action to try and get at it. Even then, the name would be qualified with the name of the module that it came from, and the module would be qualified by the name of the package that the module is in.

So basically there are none of the usual concerns about collisions with other similarly named variables in other places unless someone goes out of their way to try and break things.

For the specific case of Sublime plugins, when your plugin modules are loaded by sublime it invokes the dir function on the loaded module to find all of the symbols it contains and ignores everything that’s not a subclass of one of the special plugin classes (i.e. ApplicationCommand, WindowCommand, TextCommand, EventListener and ViewEventListener).

As such, so long as your helper classes are not subclasses of one of those classes, you pretty much have free reign to do as you please from within your own plugin files.

If you’re not familiar with how this sort of thing works in Python as opposed to other languages, the syntax and semantics might be a little “weird” initially, but I think that’s about the only caveat. The only other thing I can think to point out is that you have to be aware of the ramifications of using the global, but it sounds like you are.

Symbols with a single leading underscore (e.g. _myValue) and those with two leading underscores and no more than one trailing underscore (e.g. __myValue) have some special semantics, but for your use case here that doesn’t really matter. This page talks about that a little.

A simplistic example of doing something like this, based on what you outlined above:

import sublime
import sublime_plugin

_myGlobal = None

class MyClass():
    def __init__(self, value):
        self.value = value

class MyCopyCommand(sublime_plugin.WindowCommand):
    def run(self):
        global _myGlobal

        self.window.run_command("copy")
        _myGlobal = MyClass(sublime.get_clipboard())

class MyPasteCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        sublime.set_clipboard(_myGlobal.value)
        self.view.run_command("paste")

    def is_enabled(self):
        return _myGlobal is not None

The important takeaway is that the “global” is really just a top level symbol in the module.

Anything that wants to read the value (in this case everything in MyPasteCommand) can just do so with no special syntax. When the access happens, because there’s no local variable or parameter with that name, it automatically finds what it wants from the outer scope.

When it comes to actually writing to the value you need to use the special global keyword to tell the interpreter that inside of this scope, all references to _myGlobal actually mean the one from the outer scope. Without that, the assignment in MyCopyCommand.run() would create a local variable with that value, which would immediately go out of scope.

2 Likes

#3

Excellent, and thank you. I had not realized that global still had scope to the local module and yes my concern was that it might truly be global to all of ST. This makes it much cleaner. I’d tried to find some examples but I hadn’t found anything that really seemed to use global.

For example, in kill_ring.py, this is actually not dissimilar to what I need to do (i.e. have data scraped from the current file buffer persist over commands). It starts with a class, and then in the module assigns “kill_ring = KillRing()”. At this point, if I’m reading it correctly, it just defined a wide-scope instantiation of class KillRing.

However then the following YankCommand() class utilizes “kill_ring” without ever declaring it global scope. But, I just realized that it’s never actually assigning it fresh, it’s simply using it, so we don’t need to declare the wider scope explicitly. So I guess that’s a nuance that I just picked up on. I had been expecting to that explicit global keyword.

Thanks again. This is really starting to roll along. I spent some time tweaking a few things because I’d really like the language lexicon handling classes to be as relatively Sublime agnostic as possible and I had some cross over stuff like some reference points. I should be able to start breaking off the lexicon classes into their own module and then keep the commanding structures that need to understand the API in the fundamental module. Now I just need to dig back into the API for the text insertion portions because I won’t be able to cheese it with a snippet this time. :smile:

1 Like

#4

Out of curiosity, is leading with an initial _ the common practice for global instances? My Python book and other docs says that double __ is frequently reserved for special things (like “init”) but I don’t know that I’d seen anything about a single underscore prefix. I’d like to conform to whatever the expected standard for Python is where reasonable.

0 Likes

#5

_ is implicit for protected and __ for private members. However __func__ is used for built-in functions. If you create a function or variable you only want to use in your module then you should prefix it with _ (using __ does not always work for ST plugins).

0 Likes

#6

Oh okay, it has meaning further than just global. I can’t imagine anyone else wanting to use it, but I suppose it couldn’t hurt to make it protected.

0 Likes