Sublime Forum

FileNotFoundError: reading JSON file

#1

My plugin needs to load a JSON file, but because ST3 uses .sublime-package zip files to store packages; my plugin can not be able to locate the JSON file and throws FileNotFoundError instead.

Here’s a piece of my code trying to load the file.

curr_path = os.path.dirname(os.path.abspath(__file__))
json_path = os.path.join(curr_path, 'data.json')

with open(json_path) as json_file:
	color_data = json.load(json_file)

Any advice to work around this?

0 Likes

#2

Use the sublime.load_resource api. It expects a resource path.

3 Likes

#3

Thansk for your response FichteFoll.

I found and read the documentation. I also searched for sublime.load_resource on GitHub to see how people using it and found this repo as an example.

Now, when I try to use the code below, it raises me an IOError("resource not found") error. Any idea?

import sublime

sublime.load_resource("Packages/DeclareThatColor/libs/hexcode/data.json")

# also tried this one
sublime.load_resource("Packages\\DeclareThatColor\\libs\\hexcode\\data.json")

# the following might not make sense according to the documentation,
# but I still tried just in case
sublime.load_resource(json_path)
sublime.load_resource(os.path.join("Packages", __package__, "data.json"))

BTW here’s the GitHub link to my plugin if you’re interested in the folder structure of the project.

0 Likes

#4

Note that resource paths always use a forward slash, so

sublime.load_resource("/".join(["Packages, __package__, "libs", "hexcode", "data.json"))
sublime.load_resource("Packages/{}/libs/hexcode/data.json".format(__package__))

would be the way to go. If that doesn’t work for some reason, check whether you have a typo in there or “hexcode” is in your list of ignored packages (there’s a bug with that currently).

Please also check the output of sublime.find_resources("data.json") in the console.

0 Likes

#5

FYI, if you have to do a lot of manipulation of resource paths, then sublime_lib.ResourcePath may be useful:

from sublime_lib import ResourcePath

data_path = ResourcePath('Packages', __package__, 'libs/hexcode/data.json')

# or:

package_path = ResourcePath('Packages', __package__)
data_path = package_path / 'libs/hexcode/data.json'

json = data_path.read_text()

It’s probably overkill for just the example you gave, but if you’re doing a lot of it then ResourcePath could be handy.

0 Likes

#6

To anyone that might experience this same issue, the following are the solutions that worked for me.

First, make sure if the resource you’re trying to load do exists. As pointed by FichteFoll this can be done by running the code below to the console:

sublime.find_resources("data.json")

The returned list must include the path to your resource file, in my case Packages/DeclareThatColor/libs/hexcode/data.json.

Now into the solution.

If on the module level function call load_resource inside plugin_loaded:

def plugin_loaded():
    sublime.load_resource("Packages/DeclareThatColor/libs/hexcode/data.json")

Otherwise, use a callback to load the JSON and pass that to set_timeout function.

load_data_delay = 1000  # milliseconds

def _load_color_data():
    sublime.load_resource("Packages/DeclareThatColor/libs/hexcode/data.json")

sublime.set_timeout(_load_color_data, load_data_delay)

For some reason, using ResourcePath as suggested by ThomSmith gives me an ImportError: No module named 'sublime_lib' error. :confused:

0 Likes

#7

I’m not sure what this “otherwise” refers to here. If you’re trying to load a resource before the API is ready, it will fail.

You need to install the module first by declaring it as a dependency. See the instructions in the README for details.

0 Likes

#8

Sorry, I should have been clear.

In my case, using plugin_loaded in DeclareThatColor/declare_that_color.py works — this is where my commands located.

But I want to load the JSON file in DeclareThatColor/libs/hexcode/hexname.py. However, when I define plugin_loaded function here, it’s not being called. What worked though is the second solution I posted above.


You need to install the module first by declaring it as a dependency. See the instructions in the README for details.

I seee, thanks! :slight_smile:

0 Likes

#9

Well, that’s to be expected then. Still, don’t rely on timeouts when there are workable solutions, e.g. a cached function that only fetches the resource when it’s first needed.

1 Like

#10

a cached function that only fetches the resource when it’s first needed.

Does this pertain to using lru_cache?

0 Likes

#11

Both lru_cache or a module-global variable are valid solutions.

1 Like

#12

e.g.,

_hexcode_data = None
def get_hexcode_data():
  global _hexcode_data
  if _hexcode_data is None:
    _hexcode_data = sublime.load_resource("Packages/DeclareThatColor/libs/hexcode/data.json")
  return _hexcode_data

Sublime will only call plugin_loaded on modules at the top level of the package. In fact, these are the only modules that Sublime will load directly (without you needing to explicitly import them). If you import a module from a subdirectory, Sublime will not call plugin_loaded on that module.

1 Like

#13

Also, load_resource returns a string; to convert it to dict use decode_value.

My actual code is this.

package_path = os.path.join(*__package__.split('.'))
color_data_path = os.path.join('Packages', package_path, 'data.json')
color_data = None

def _load_color_data():
    global color_data

    color_data_file = sublime.load_resource(color_data_path)
    color_data = sublime.decode_value(color_data_file)

Sublime will only call plugin_loaded on modules at the top level of the package. In fact, these are the only modules that Sublime will load directly (without you needing to explicitly import them). If you import a module from a subdirectory, Sublime will not call plugin_loaded on that module.

For me as a first-timer to write an ST plugin, this was not obvious from the documentation.

Thanks ThomSmith.

0 Likes