Plain is headed towards 1.0! Subscribe for development updates →

 1import os
 2
 3from plain.internal import internalcode
 4
 5from .exceptions import PageNotFoundError
 6from .pages import Page
 7
 8
 9@internalcode
10class PagesRegistry:
11    """
12    The registry loads up all the pages at once, so we only have to do a
13    dict key lookup at runtime to get a page.
14    """
15
16    def __init__(self):
17        self._url_name_mappings = {}  # url_name -> relative_path
18        self._path_mappings = {}  # relative_path -> absolute_path
19
20    def get_page_urls(self):
21        """
22        Generate a list of real urls based on the files that exist.
23        This way, you get a concrete url reversingerror if you try
24        to refer to a page/url that isn't going to work.
25        """
26        paths = []
27
28        for relative_path in self._path_mappings.keys():
29            page = self.get_page_from_path(relative_path)
30
31            # Get all URL path objects from the page
32            paths.extend(page.get_urls())
33
34        return paths
35
36    def discover_pages(self, pages_dir):
37        for root, _, files in os.walk(pages_dir, followlinks=True):
38            for file in files:
39                relative_path = os.path.relpath(os.path.join(root, file), pages_dir)
40                absolute_path = os.path.join(root, file)
41
42                page = Page(relative_path=relative_path, absolute_path=absolute_path)
43                urls = page.get_urls()
44
45                # Some pages don't get any urls (like templates)
46                if not urls:
47                    continue
48
49                # Register the page by its file path
50                self._path_mappings[relative_path] = absolute_path
51
52                # Register all URL names to point back to this file path
53                for url_path_obj in urls:
54                    url_name = url_path_obj.name
55                    self._url_name_mappings[url_name] = relative_path
56
57    def get_page_from_name(self, url_name):
58        """Get a page by its URL name."""
59        try:
60            relative_path = self._url_name_mappings[url_name]
61            return self.get_page_from_path(relative_path)
62        except KeyError:
63            raise PageNotFoundError(f"Could not find a page for URL name {url_name}")
64
65    def get_page_from_path(self, relative_path):
66        """Get a page by its relative file path."""
67        try:
68            absolute_path = self._path_mappings[relative_path]
69            # Instantiate the page here, so we don't store a ton of cached data over time
70            # as we render all the pages
71            return Page(relative_path=relative_path, absolute_path=absolute_path)
72        except KeyError:
73            raise PageNotFoundError(f"Could not find a page for path {relative_path}")
74
75
76pages_registry = PagesRegistry()