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()