Skip to content

Commit

Permalink
refactor: __build_app function clean-up and make all modules access…
Browse files Browse the repository at this point in the history
…ible (#1240)

This is both a refactor and a fix for the `Modulename.html = True`
default that was removed with viur-core 3.6.

The error `AttributeError: 'Index' object has no attribute
'<modulename>'` is fixed with this improvement, and the workaround of
`conf.main_app.vi.modulename` is not neccessary anymore.

Belongs to #1229, replaces #1232.
  • Loading branch information
phorward authored Aug 21, 2024
1 parent cb8d386 commit 9493f81
Showing 1 changed file with 29 additions and 18 deletions.
47 changes: 29 additions & 18 deletions src/viur/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def setDefaultDomainLanguage(domain: str, lang: str):
conf.i18n.domain_language_mapping[host] = lang.lower()


def buildApp(modules: ModuleType | object, renderers: ModuleType | object, default: str = None) -> Module:
def __build_app(modules: ModuleType | object, renderers: ModuleType | object, default: str = None) -> Module:
"""
Creates the application-context for the current instance.
Expand Down Expand Up @@ -118,12 +118,6 @@ def buildApp(modules: ModuleType | object, renderers: ModuleType | object, defau
renderers[key][subkey] = render
del renderers_root

# instanciate root module
if hasattr(modules, "index"):
root = modules.index("index", "")
else:
root = Module("index", "")

# assign ViUR system modules
from viur.core.modules.moduleconf import ModuleConf # noqa: E402 # import works only here because circular imports
from viur.core.modules.script import Script # noqa: E402 # import works only here because circular imports
Expand All @@ -135,20 +129,29 @@ def buildApp(modules: ModuleType | object, renderers: ModuleType | object, defau
modules._translation = Translation
modules.script = Script

# create module mappings
# Resolver defines the URL mapping
resolver = {}

# Index is mapping all module instances for global access
index = (modules.index if hasattr(modules, "index") else Module)("index", "")
index.register(resolver, renderers[default]["default"](parent=index))

for module_name, module_cls in vars(modules).items(): # iterate over all modules
if module_name == "index":
continue # ignore index, as it has been processed before!

if module_name in renderers:
raise NameError(f"Cannot name module {module_name!r}, as it is a reserved render's name")

if not ( # we define the cases we want to use and then negate them all
(inspect.isclass(module_cls) and issubclass(module_cls, Module) # is a normal Module class
and not issubclass(module_cls, InstancedModule)) # but not a "instantiable" Module
or isinstance(module_cls, InstancedModule) # is an already instanced Module
):
continue

if module_name == "index":
root.register(resolver, renderers[default]["default"](parent=root))
continue
# remember module_instance for default renderer.
module_instance = default_module_instance = None

for render_name, render in renderers.items(): # look, if a particular renderer should be built
# Only continue when module_cls is configured for this render
Expand All @@ -163,15 +166,15 @@ def buildApp(modules: ModuleType | object, renderers: ModuleType | object, defau

# Attach the module-specific or the default render
if render_name == default: # default or render (sub)namespace?
setattr(root, module_name, module_instance)
default_module_instance = module_instance
target = resolver
else:
if getattr(root, render_name, True) is True:
if getattr(index, render_name, True) is True:
# Render is not build yet, or it is just the simple marker that a given render should be build
setattr(root, render_name, Module(render_name, "/" + render_name))
setattr(index, render_name, Module(render_name, "/" + render_name))

# Attach the module to the given renderer node
setattr(getattr(root, render_name), module_name, module_instance)
setattr(getattr(index, render_name), module_name, module_instance)
target = resolver.setdefault(render_name, {})

module_instance.register(target, render.get(module_name, render["default"])(parent=module_instance))
Expand All @@ -180,8 +183,14 @@ def buildApp(modules: ModuleType | object, renderers: ModuleType | object, defau
if "_postProcessAppObj" in render: # todo: This is ugly!
render["_postProcessAppObj"](target)

conf.main_resolver = resolver
# Ugly solution, but there is no better way to do it in ViUR 3:
# Allow that any module can be accessed by `conf.main_app.<modulename>`,
# either with default render or the last created render.
# This behavior does NOT influence the routing.
if default_module_instance or module_instance:
setattr(index, module_name, default_module_instance or module_instance)

# fixme: Below is also ugly...
if default in renderers and hasattr(renderers[default]["default"], "renderEmail"):
conf.emailRenderer = renderers[default]["default"]().renderEmail
elif "html" in renderers:
Expand All @@ -192,7 +201,8 @@ def buildApp(modules: ModuleType | object, renderers: ModuleType | object, defau
import pprint
logging.debug(pprint.pformat(resolver))

return root
conf.main_resolver = resolver
conf.main_app = index


def setup(modules: ModuleType | object, render: ModuleType | object = None, default: str = "html"):
Expand All @@ -215,7 +225,8 @@ def setup(modules: ModuleType | object, render: ModuleType | object = None, de
if not render:
import viur.core.render
render = viur.core.render
conf.main_app = buildApp(modules, render, default)

__build_app(modules, render, default)

# Send warning email in case trace is activated in a cloud environment
if ((conf.debug.trace
Expand Down

0 comments on commit 9493f81

Please sign in to comment.