Sublime Forum

Accessing settings from within a multi-module ST package

#1

When handling settings in a multi-module ST package I’ve hit a problem. I’d like a single instance global of the settings class which can be shared/accessed from within all the other Python modules. As far as I can tell this is not possible within a ST package (or if I’m wrong please enlighten me!).

As an alternative I’ve used a settings class which has no instance created. Instead each method is defined with the @classmethod decorator. That way the settings class can be imported from the settings module into the other modules and the settings can be accessed easily everywhere.

I have a proof of concept project which I’ve uploaded to this GitHub repository.

The problem is in MiscModule.py which defines a single function which fails to access the setting it is supposed to handle. The settings are all accessed correctly from TestSettings.py and from TestSettingsSubClass.py.

Can anyone explain why the MiscModule.py function fails to retrieve the setting and how I can fix it?

Thanks.

0 Likes

#2

While trying to get this working I discovered that…

With the addition of a 2nd plugin_loaded() function in MiscModule.py the misc_module_done_text() now correctly retrieves the setting using Settings.done_text.

MiscModule.py now looks like this:

from .SettingsModule import Settings

def plugin_loaded():
    Settings.load_settings()

def misc_module_done_text():
    print("In: Misc.misc_module_done_text() - done_text: " + Settings.done_text)
    return Settings.done_text

Can someone explain why this is necessary please and why a plugin_loaded() function is not also necessary in TestSettingsSubClass.py?

Could it really be as simple as MiscModule.py being the nearest the beginning of the alphabet and it being loaded first?

# e.g. Console output on package installation:

reloading plugin TestSettings.MiscModule
reloading plugin TestSettings.SettingsModule
reloading plugin TestSettings.TestSettings
reloading settings Packages/TestSettings/TestSettings.sublime-settings
reloading plugin TestSettings.TestSettingsSubClass

If this is the reason, is there a way I can more elegantly handle this?

Thanks again.

0 Likes

#3

I’d like a single instance global of the settings class which can be shared/accessed from within all the other Python modules.

Why?

A Settings object is just a window into Sublime’s internal settings system. Multiple Settings objects are multiple windows into the same system. If you change settings through one, you will see those changes immediately in the others; the Settings objects are automatically kept in sync because they’re looking at the same underlying data.

A Settings object itself is extremely lightweight: all it contains is a settings_id that it passes to the internal settings API. There is no harm to creating many Settings objects that refer to the same data.

1 Like

#4

What I want to share is an instance of my settings class, not a Sublime settings object.

Do you think it’s better to create a global of my settings class in every module of the package, each of which loads all the settings and adds a callback for each setting (using add_on_change())?

The plugin I’m working on currently has 38 settings which are used in 4 Python modules holding 14 classes in total. It’s quite a substantial package and it seems rather inelegant to have the same code doing the same thing for each module rather than sharing access to a single instance.

0 Likes

#5

The issue you are faced with is caused by the way, ST loads plugins. It doesn’t handle the modules in the root folder as “normal python package”, but loads them as independend plugins.

Importing one of those modules into another one, will therefore most likely result in two instances/references of an import or the globals being defined inside. Things get even worse after ST reloads plugins for some reasons (updating, …).

I struggled with such multiple instance issues for a while when working on GitGutter as well.

My conclusion was:

  1. A package root folder should only contain independend plugins.
  2. Put all the plugin code into a sub directory and leave just one single plugin.py in the root folder in order to expose the TextCommand, … classes to ST and handle plugin initialization via plugin_loaded() and plugin_unloaded().

All sub directories are handled as ordinary python packages. ST won’t load individual files nor would it parse them for interface classes like TextCommand etc. which gives you the full power to decide what to expose and what to keep private.

The only tradeoff with such a solution is - you need to handle module reloading for that sub directory manually. Means, you need to make sure all modules are reloaded in correct import order, after your package was updated in order to avoid restarting ST.

There are basically two approaches to do that:

  1. GitGutter, GitSavvy, … use the code of AutoPackageReloader to force the reloading of all modules. pro: Module loading order is detected automatically.
    contra: It forces the main module to be reloaded, so your plugin is loaded twice.
  2. Package Control and A File Icon reload all sub modules based on a static modules load order list upon import time of the main plugin in the root folder.
    pro: very simple solution, no reloading of the main plugin required
    contra: you need to identify the correct module loading order during development time manually.

Example:

1 Like