Sublime Forum

Window.project_data() sometimes returns int or str instead of dict

#1

Hi,

(I’ve mistakenly posted that on the devbuild thread but found that it’s a general problem, so I’m reposting here with more information).

First off, if I’m doing something illegal with project_data() I apologize and please let me know what I’m doing wrong. if not then consider this a bug report.

I reproduced this consistently on a vanilla sublime 3 3126 (with only Package Controlled installed), with the following two python scrips in my User directory (Initially I always had the issue with my test script and ‘PlainTasks’ installed, but it’s not related to that plugin. I distilled it down to the actual 10 lines needed to break this)

The issue is as follows: it seems that sometimes self.window.project_data() returns a string object instead of a dict() after I update the project data with self.window.set_project_data() (even though I make certain that a) I only do it on the main thread and b) I absolutely store a dict() object.)

code -> repo_test.py:

import sublime, sublime_plugin
import os, sys, threading
import inspect


def get_type_id_string( obj ):
    type_id_string = "id: {0}, type: {1}".format( id(obj), type(obj))
    return type_id_string


def debug_print( text ):
    callerframerecord = inspect.stack()[1]
    callerframe = callerframerecord[0]
    lineno = callerframe.f_lineno
    print( str(lineno) + ": [TID: {0}] -> ".format(threading.get_ident()) + ": {0}".format(text) )


class RepoThread( threading.Thread ):
    def __init__(self, on_finished_callback):
        threading.Thread.__init__(self)
        self.on_finished_callback = on_finished_callback

    def run( self ):
        self.item = "asdf"
        debug_print( "RepoThread.run" )

        def on_done():
            debug_print( "RepoThread.on_done" )
            self.on_finished_callback( self.item )

        sublime.set_timeout( on_done, 10 )


class RepoTestCommand(sublime_plugin.WindowCommand):
    def run(self, **kwargs):
        debug_print( "RepoTestCommand" )


        # test setup
        project_data = self.window.project_data()
        debug_print( "initial pd: {0}".format( get_type_id_string(project_data)))
        if not project_data:
            project_data = dict()
            self.window.set_project_data( project_data )
            project_data = self.window.project_data()

        project_data.pop( "settings", None )
        self.window.set_project_data( project_data )

        project_data = self.window.project_data()
        debug_print( "constructor pd: {0}".format( get_type_id_string(project_data)))

        thread = RepoThread( self.on_thread_done )
        thread.start()


    def on_thread_done( self, item ):
        project_data     = self.window.project_data()
        debug_print( "pd: way before: {0}".format( get_type_id_string(project_data)))

        project_settings = dict()
        project_data[ "settings" ] = project_settings
        debug_print( "pd: after set settings: {0}".format( get_type_id_string(project_data)))

        project_settings["solution_file"] = item
        debug_print( "pd: after update settings: {0}".format( get_type_id_string(project_data)))
        self.window.set_project_data( project_data )

        project_data     = self.window.project_data()
        debug_print( "pd: after set: {0}".format( get_type_id_string(project_data)))
        project_settings = project_data[ "settings" ]

code -> break_project_data.py:

import sublime, sublime_plugin

class BreakProjectDataCommand(sublime_plugin.TextCommand):
    pass

class BreakProjectDataListener(sublime_plugin.ViewEventListener):
    pass

output without break_project_data.py:

36: [TID: 3392] -> : RepoTestCommand
41: [TID: 3392] -> : initial pd: id: 2150516937096, type: <class ‘dict’>
51: [TID: 3392] -> : constructor pd: id: 2150517084680, type: <class ‘dict’>
25: [TID: 18976] -> : RepoThread.run
28: [TID: 3392] -> : RepoThread.on_done
59: [TID: 3392] -> : pd: way before: id: 2150517084680, type: <class ‘dict’>
63: [TID: 3392] -> : pd: after set settings: id: 2150517084680, type: <class ‘dict’>
66: [TID: 3392] -> : pd: after update settings: id: 2150517084680, type: <class ‘dict’>
70: [TID: 3392] -> : pd: after set: id: 2150495719368, type: <class ‘dict’>

output with break_project_data.py:

36: [TID: 3392] -> : RepoTestCommand
41: [TID: 3392] -> : initial pd: id: 2150516939592, type: <class ‘dict’>
51: [TID: 3392] -> : constructor pd: id: 510181184, type: <class ‘int’>
25: [TID: 13376] -> : RepoThread.run
28: [TID: 3392] -> : RepoThread.on_done
59: [TID: 3392] -> : pd: way before: id: 2150516955912, type: <class ‘dict’>
63: [TID: 3392] -> : pd: after set settings: id: 2150516955912, type: <class ‘dict’>
66: [TID: 3392] -> : pd: after update settings: id: 2150516955912, type: <class ‘dict’>
70: [TID: 3392] -> : pd: after set: id: 510181184, type: <class ‘int’>
Traceback (most recent call last):
File “C:\Users\broom\Downloads\st\Data\Packages\User\repotest.py”, line 29, in on_done
self.on_finished_callback( self.item )
File “C:\Users\broom\Downloads\st\Data\Packages\User\repotest.py”, line 71, in on_thread_done
project_settings = project_data[ “settings” ]
TypeError: ‘int’ object is not subscriptable

as you can see, if I have break_project_data.py in my User folder, eventually the project_data object that I get back is an ‘int’ instead of a ‘dict’. This is not what the documentation says and also I only ever store a dict back into the project_data. Also, the other script involved to trigger the bug doesn’t touch the project_data at all, so I’m not sure what’s going on here.

0 Likes

#2

So this issue has been ignored for well over a year now.
I still face this issue so is there any way to debug why this is happening? Are there any debug setting I can use, can we get the symbol files for sublime text…
do you care to at least have a look at this? (Yes, I do have a paid license for sublime text if you’re hesitant because of that)

0 Likes

#3

I wonder if the problem is related to the fact that you’re a) setting a mutable dict as the project data, b) mutating that dict, and c) setting the same dict as the project data.

1 Like

#4

It may possibly be related to this, but I’ve also noticed in the past that there is something wonky about adding project data to a window in which the API in general starts behaving in weird ways.

Consider this simple example, which adds project data to the window and adds a settings to it:

import sublime
import sublime_plugin

class ExampleCommand(sublime_plugin.ApplicationCommand):
	def run(self, break_things=False):
		if break_things:
			sublime.active_window().set_project_data({"settings": {"broken": True}})

		print("=> '%s'" % sublime.packages_path())

After adding this plugin, run the command from the console with no arguments; I get results such as the following:

>>> sublime.run_command("example")
=> '/tmp/bob/d/sublime_text_3_3184/Data/Packages'

Now, run the command again, but with the appropriate argument to break things:

>>> sublime.run_command("example", {"break_things": True})
=> ''

Suddenly, there’s no packages path any longer. But not really, because it works if you do it a second time in the same window:

>>> sublime.run_command("example", {"break_things": True})
=> ''
>>> sublime.run_command("example", {"break_things": True})
=> '/tmp/bob/d/sublime_text_3_3184/Data/Packages'

This isn’t exactly the same symptom as what you’ve outlined above; but this particular situation smacks of something inherently insidious happening behind the scenes in a way that (I presume) was not intended. So it doesn’t seem like much of a stretch to posit that they may somehow be related to each other since they touch the same area and they cause the API to not do what you expect.

3 Likes

#5

@wbond : could you (or someone else maintaining ST3) please take a look at this and let me/us know if there is anything we can do to fix that ourselves?
I try to implement what I need as plugins instead of posting feature requests, but well if the plugin API is broken (at least I think it is given we haven’t had any official word in since mid 2017) that’s kind of hard…

0 Likes

#6

The best place for bug reports is https://github.com/SublimeTextIssues/Core/issues.

That said, I will add it to my “short list” of things to check out. Hopefully I’ll have time to check that out in the next couple of weeks.

2 Likes

#7

Thanks a lot! (I didn’t know about the ST github… I’ll file the bug there).
cheers!

1 Like