Search code examples
pythonsublimetextsublime-text-plugin

Module object differences in 2 versions of Sublime Text


In a plugin I'm writing in Python for Sublime Text I've discovered a difference between Sublime Text v.2 and v.3. [I'm porting the plugin backwards to work with v.2 having written it with v.3.]

In the sublime.View class the method add_regions() takes various flags. To get a selection region outline in v.2 sublime.DRAW_OUTLINED is used, in v.3 sublime.DRAW_NO_FILL is used - it seems the developer has simply renamed DRAW_OUTLINED to DRAW_NO_FILL in v.3 as they both do the same thing.

After trying various approaches I resolved the situation when I realized that, although not documented in the API, sublime.DRAW_OUTLINED is also available in v.3 (although sublime.DRAW_NO_FILL is not available in v.2.)

However I'd quite like to know what approaches would be available to me had the developer not kept sublime.DRAW_OUTLINED in his v.3 code.

My approaches both centered around using the version number of the class.

Firstly:

sub_text_ver = sublime.version()

if sub_text_ver < 3000:
    flags = sublime.DRAW_OUTLINED | sublime.DRAW_EMPTY_AS_OVERWRITE
else:
    flags = sublime.DRAW_NO_FILL | sublime.DRAW_EMPTY_AS_OVERWRITE

# Code continues...

Secondly:

sub_text_ver = sublime.version()

if sub_text_ver < 3000:
    flags = self.get_regions_flags_sub_text_v2()
else:
    flags = self.get_regions_flags_sub_text_v3()

# Code continues to end of method and then...

def get_regions_flags_sub_text_v2(self):
    return sublime.DRAW_OUTLINED | sublime.DRAW_EMPTY_AS_OVERWRITE

def get_regions_flags_sub_text_v3(self):
    return sublime.DRAW_NO_FILL | sublime.DRAW_EMPTY_AS_OVERWRITE

Both gave me a traceback error like this:

Traceback (most recent call last):
  File "./ExpandSelectionByRegex.py", line 155, in on_change
  File "./ExpandSelectionByRegex.py", line 177, in get_regions_flags_sub_text_v3
AttributeError: 'module' object has no attribute 'DRAW_NO_FILL'

I would like to know what approaches I could have taken to sort this out. Would I have been forced to have two different files for the plugin, one for each version of Sublime Text? Could it have been resolved in one file with two different classes? Could it have been resolved in the same class? I feel that it's best to learn about these kind of things when you encounter them, rather than shelving them just because the current situation was resolved. [Clearly I am not the most experienced Python programmer.] Thanks.


Solution

  • I would suggest using try and except statements do exception handling to make sure you get the right module attribute:

    try:
        flags = sublime.DRAW_NO_FILL | sublime.DRAW_EMPTY_AS_OVERWRITE
    except AttributeError:    # DRAW_NO_FILL was previously named DRAW_OUTLINED
        flags = sublime.DRAW_OUTLINED | sublime.DRAW_EMPTY_AS_OVERWRITE
    

    This is known as the "Easier to Ask Forgiveness than Permission" (EAFP) style of programming. You write some code you know will raise an exception in some situations, and write a try/except structure to handle that exception with an alternative bit of code. EAFP style is usually preferred in Python over the "Look Before You Leap" (LBYL) style you were trying for with your if statements, though there are some situations where LBYL is easier.