Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to load pages from package #2556

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions dash/_pages.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import collections
import importlib
import os
import pkgutil
import re
import sys
from fnmatch import fnmatch
Expand Down Expand Up @@ -411,6 +412,15 @@ def _page_meta_tags(app):
]


def _ensure_layout_is_loaded(module_name, page_module):
if (
module_name in PAGE_REGISTRY
and not PAGE_REGISTRY[module_name]["supplied_layout"]
):
_validate.validate_pages_layout(module_name, page_module)
PAGE_REGISTRY[module_name]["layout"] = getattr(page_module, "layout")


def _import_layouts_from_pages(pages_folder):
for root, dirs, files in os.walk(pages_folder):
dirs[:] = [d for d in dirs if not d.startswith(".") and not d.startswith("_")]
Expand All @@ -428,10 +438,13 @@ def _import_layouts_from_pages(pages_folder):
page_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(page_module)
sys.modules[module_name] = page_module
_ensure_layout_is_loaded(module_name, page_module)

if (
module_name in PAGE_REGISTRY
and not PAGE_REGISTRY[module_name]["supplied_layout"]
):
_validate.validate_pages_layout(module_name, page_module)
PAGE_REGISTRY[module_name]["layout"] = getattr(page_module, "layout")

def _import_layouts_from_package(pages_package):
modules = pkgutil.walk_packages(
pages_package.__path__, prefix=pages_package.__name__ + "."
)
for module in modules:
page_module = importlib.import_module(module.name)
_ensure_layout_is_loaded(module.name, page_module)
5 changes: 5 additions & 0 deletions dash/dash.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
_page_meta_tags,
_path_to_page,
_import_layouts_from_pages,
_import_layouts_from_package,
)

# Add explicit mapping for map files
Expand Down Expand Up @@ -345,6 +346,7 @@ def __init__( # pylint: disable=too-many-statements
server=True,
assets_folder="assets",
pages_folder="pages",
pages_package=None,
use_pages=None,
assets_url_path="assets",
assets_ignore="",
Expand Down Expand Up @@ -444,6 +446,7 @@ def __init__( # pylint: disable=too-many-statements
_get_paths.CONFIG = self.config
_pages.CONFIG = self.config

self.pages_package = pages_package
self.pages_folder = str(pages_folder)
self.use_pages = (pages_folder != "pages") if use_pages is None else use_pages

Expand Down Expand Up @@ -1959,6 +1962,8 @@ def enable_pages(self):
return
if self.pages_folder:
_import_layouts_from_pages(self.config.pages_folder)
if self.pages_package:
_import_layouts_from_package(self.pages_package)

@self.server.before_request
def router():
Expand Down
4 changes: 4 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import sys

import pytest
import dash
Expand All @@ -21,6 +22,9 @@ def clear_pages_state():

def init_pages_state():
"""Clear all global state that is used by pages feature."""
for page in dash._pages.PAGE_REGISTRY.values():
if page["module"] in sys.modules:
sys.modules.pop(page["module"])
dash._pages.PAGE_REGISTRY.clear()
dash._pages.CONFIG.clear()
dash._pages.CONFIG.__dict__.clear()
9 changes: 9 additions & 0 deletions tests/unit/pages/test_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,12 @@ def test_import_layouts_from_pages(

page_entry = list(dash.page_registry.values())[0]
assert page_entry["module"] == expected_module_name


def test_import_layouts_from_package(clear_pages_state):
from . import custom_pages

_ = Dash(__package__, use_pages=True, pages_folder="", pages_package=custom_pages)
page_entries = list(dash.page_registry.values())
assert len(page_entries) == 1
assert page_entries[0]["module"] == "pages.custom_pages.page"