4
$\begingroup$

I practiced using text data as a module as below. And it works very well.

# function.py

def function():
    print('hello')
import bpy
f = bpy.data.texts['function.py'].as_module()

f.function()
>> 'hello'

But I want to use the from import statement. But I couldn't find a way.

Can text data be recalled to the from import statement as a module and used?

$\endgroup$

1 Answer 1

12
$\begingroup$

Add to sys.modules

Among other ways to make fake modules, can simply add it to sys.modules

>>> import sys
>>> sys.modules["function"] = bpy.data.texts['function.py'].as_module()
>>> from function import function
>>> function()
hello

Have a good link re fake modules and namespaces, will look for it.

https://stackoverflow.com/questions/5122465/can-i-fake-a-package-or-at-least-a-module-in-python-for-testing-purposes/27476659#27476659

Will add this link here too, https://dev.to/dangerontheranger/dependency-injection-with-import-hooks-in-python-3-5hap in can make a fake namespace that can be imported as if a regular module.

An example using above (stripped of comments to make shorter) making the function method appear to come from module my_addon.utils

import importlib.abc
import importlib.machinery
import sys
import types


class DependencyInjectorFinder(importlib.abc.MetaPathFinder):
    def __init__(self, loader):
        self._loader = loader
    def find_spec(self, fullname, path, target=None):

        if self._loader.provides(fullname):
            return self._gen_spec(fullname)
    def _gen_spec(self, fullname):
        spec = importlib.machinery.ModuleSpec(fullname, self._loader)
        return spec

    
class DependencyInjectorLoader(importlib.abc.Loader):
    _COMMON_PREFIX = "my_addon."
    def __init__(self):
        self._services = {}
        self._dummy_module = types.ModuleType(self._COMMON_PREFIX[:-1])

        self._dummy_module.__path__ = []
    def provide(self, service_name, module):
        self._services[service_name] = module
    def provides(self, fullname):
        if self._truncate_name(fullname) in self._services:
            return True
        else:
            return self._COMMON_PREFIX.startswith(fullname)
    def create_module(self, spec):

        service_name = self._truncate_name(spec.name)
        if service_name not in self._services:
            return self._dummy_module
        module = self._services[service_name]
        return module
    def exec_module(self, module):

        pass
    def _truncate_name(self, fullname):
        return fullname[len(self._COMMON_PREFIX):] 


class DependencyInjector:

    def __init__(self):
        self._loader = DependencyInjectorLoader()
        self._finder = DependencyInjectorFinder(self._loader)
    def install(self):
        sys.meta_path.append(self._finder)
    def provide(self, service_name, module):
        self._loader.provide(service_name, module)


if __name__ == "__main__":
    import bpy
    injector = DependencyInjector()

    injector.provide("utils", bpy.data.texts["functions.py"].as_module())
    injector.install()

    import my_addon.utils as utils
    utils.function()

output

hello
$\endgroup$
4
  • 1
    $\begingroup$ This might be the most convoluted Hello World I've ever seen :) $\endgroup$
    – Gorgious
    Commented Feb 23, 2021 at 7:17
  • $\begingroup$ Yep. Ummed and ahhed whether to include. 2nd part prob not required here. Found it handy as a use case for from my_addon.libs import matplotlib to inject some dummy for the case of the real one not installed. In this case OP can develop a script in blend file text editor to look like from my_addon.utils import function Thanks for the feedback, it's appreciated. $\endgroup$
    – batFINGER
    Commented Feb 23, 2021 at 8:27
  • $\begingroup$ In any case I really liked it, I don't understand most of it but it seems really powerful and I'll gladly dive deep into it :) $\endgroup$
    – Gorgious
    Commented Feb 23, 2021 at 9:05
  • $\begingroup$ Thank you! This solved it. $\endgroup$
    – J.Nada
    Commented Aug 10, 2021 at 21:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .