Sublime Forum

An odd problem about "sublime.load_settings"

#1

Test.sublime-settings

{
	"test1" : "1",
	"test2" : "2"
}

Test.py

import sublime, sublime_plugin
class Test:
    def __init__(self):
        # initialize
        self.setting = sublime.load_settings('Test.sublime-settings')
        print(self.setting.get('test1', 'aa'))
Test()

When I open sublime3(odd things happen), log is

reloading plugin Test.Test
None

but when I resave Test.py in sublime3(this is correct), log is

reloading plugin Test.Test
1

why? I Don’t understand.

0 Likes

#2

Potentially because when a plugin loads, the classes get created right away, but the API isn’t ready to do anything until plugin_loaded is called. When you cause the plugin to be reloaded it works because by that point the plugin API is ready.

1 Like

#3

It’s not that simple, I’m afraid. If the API wasn’t ready at the time sublime.load_settings was called, the return value would be None and the following line fail with an AttributeError since None has no attribute get.

This issues used to be a problem quite a while ago when the settings files weren’t loaded fast enough or so it seemed, so it would be an extension of the “API not ready” problem. I don’t think this was ever solved and instead it just slowly went away until it wasn’t seen frequently enough (or at all) anymore. I seem to remember @facelessuser also having problems with this.

It would be nice if this could be somewhat reliably reproduced and an issue filed on the github Issues repo because I strongly believe this to be a bug.

2 Likes

#4

Yes, this still happens to me on occasions even now. I wait for them to occur, and then I patch them, usually by abandoning what I’m doing.

0 Likes

#5

Ah, that’s good to know. I suspected that might be the case, but lacking a machine to check on I thought perhaps it would still return a settings object but without the ability to retreive a setting.

I can’t say I’ve run across this before, although I did notice some weirdness at one point with ApplicationCommand and how sublime.find_resources won’t find resources if invoked from it’s __init__ method. However in that case it just never finds resources even if you force reload the module after the API is ready so it may not be related.

0 Likes

#6

@OdatNurd @FichteFoll
I change the code of Test.py to fllowing:

import sublime, sublime_plugin
class Test:
    def __init__(self):
        # initialize
        self.setting = sublime.load_settings('Test.sublime-settings')
        print(type(self.setting))
        self.setting.set('test1', 'change')
        print(self.setting.get('test1', 'aa'))
Test()

When I open sublime3, it log fllowing:

reloading plugin Test.Test
<class 'sublime.Settings'>
None

self.setting is a object of class ‘sublime.Settings’, but it can’t be set, and all value is None.

0 Likes

#7

This seems to be the same issue as: Core$1508 Sublime Text cannot call the plugins forwards as on_selection_modified(1) before the plugin goes through the forward plugin_loaded(0)

The Sublime Text API, is only ready after all plugins being loaded, which can take some time, if the user has too much plugins. Then until the API is ready, you cannot call it, that is why calling API on __init__ does not work when Sublime Text is starting. The __init__ is called right away when Sublime Text finds the plugin, but the API is only ready some time later, when plugin_loaded() is called.

0 Likes

#8

If that is the case, when should I initialize my plugin data?

0 Likes

#9

In the forward plugin_loaded(), until your plugin’s plugin_loaded() forward has not being called by Sublime Text, you cannot attempt to use Sublime Text API. This is explained on the Sublime Text API documentation:

  1. https://www.sublimetext.com/docs/3/api_reference.html#plugin_lifecycle
0 Likes

#10

I’m aware of that; I said as much above in my first post in the thread. However, consider this sample plugin:

import sublime
import sublime_plugin

loaded = False

def plugin_loaded():
    global loaded
    loaded = True


class TestAppCommand(sublime_plugin.ApplicationCommand):
    def __init__(self):
        global loaded
        print("Was loaded: %s" % loaded)

After a restart of Sublime, the console says this:

plugins loaded
Was loaded: True
Package Control: Skipping automatic upgrade, last run at 2017-08-06 09:52:43, next run at 2017-08-06 10:52:43 or after

This would seem to indicate that at the time the __init__ method is called, plugin_loaded has indeed been called.

I also submit this:

import sublime
import sublime_plugin
from threading import get_ident as tid

def test(place):
    s = sublime.load_settings("Preferences.sublime-settings")
    r = sublime.find_resources("*.txt")
    try:
        t = sublime.load_resource('Packages/Language - English/README_en_GB.txt')
    except:
        t = None

    print("=====================")
    print("Test: %s (%s)" % (place, tid()))
    print("Settings: %s (%s)" % (s, s.get("rulers")))
    print("Found: %s" % len(r))
    print("Loaded resource: %s" % bool(t is not None))
    print("=====================")

def plugin_loaded():
    test("plugin_loaded")

def plugin_unloaded():
    print("Testing plugin_unloaded")

class TestAppCommand(sublime_plugin.ApplicationCommand):
    def __init__(self):
        super().__init__()
        test("TestAppCommand.__init__")

class TestWindowCommand(sublime_plugin.WindowCommand):
    def __init__(self, window):
        super().__init__(window)
        test("TestWindowCommand.__init__")

On startup, the console displays this:

=====================
Test: plugin_loaded (25616)
Settings: <sublime.Settings object at 0x000002F2001E2EB8> ([110, 80])
Found: 122
Loaded resource: True
=====================
=====================
Test: TestAppCommand.__init__ (25616)
Settings: <sublime.Settings object at 0x000002F27F0F4CC0> (None)
Found: 0
Loaded resource: False
=====================
=====================
Test: TestWindowCommand.__init__ (25616)
Settings: <sublime.Settings object at 0x000002F2002F4A20> ([110, 80])
Found: 122
Loaded resource: True
=====================

All are called from what appears to be the same thread ID, so presumably the console output shows you the output of the calls to test in the same order as they were made.

plugin_loaded gets called first, and the API is available, then the ApplicationCommand is created, but it can’t access the API, then the WindowCommand is created, and it can.

Next, with the plugin still loaded, save the file to cause it to be reloaded:

reloading plugin User.test
Testing plugin_unloaded
=====================
Test: plugin_loaded (25616)
Settings: <sublime.Settings object at 0x000002F2002FBB70> ([110, 80])
Found: 122
Loaded resource: True
=====================
=====================
Test: TestAppCommand.__init__ (25616)
Settings: <sublime.Settings object at 0x000002F27F2A7F60> (None)
Found: 0
Loaded resource: False
=====================

Same call order as before, only the WindowCommand is not instantiated again, which I guess makes sense because they’re only created for a new window. However, even now the ApplicationCommand cannot access the API.

Then, create a new window:

=====================
Test: TestWindowCommand.__init__ (25616)
Settings: <sublime.Settings object at 0x000002F20032FDA0> ([110, 80])
Found: 122
Loaded resource: True
=====================

No call to plugin_loaded, but the created window causes a new instance of the WindowCommand, which can access the API just fine.

Maybe this is a bug or maybe it’s by design, but it certainly seems odd. Presuming there’s not some flaw in my test code, which there undoubtedly is,

5 Likes

#11

Thanks, it’s useful to me.

0 Likes