Skip to content

Commit

Permalink
Define a base class for all navigation item classes
Browse files Browse the repository at this point in the history
  • Loading branch information
oprypin committed Jun 18, 2023
1 parent c459cd2 commit 01be507
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 65 deletions.
6 changes: 3 additions & 3 deletions docs/dev-guide/themes.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ on the homepage:
show_root_full_path: false
heading_level: 5

::: mkdocs.structure.pages.Page.parent
::: mkdocs.structure.StructureItem.parent
options:
show_root_full_path: false
heading_level: 5
Expand Down Expand Up @@ -479,7 +479,7 @@ The following attributes are available on `section` objects:
show_root_full_path: false
heading_level: 5

::: mkdocs.structure.nav.Section.parent
::: mkdocs.structure.StructureItem.parent
options:
show_root_full_path: false
heading_level: 5
Expand Down Expand Up @@ -533,7 +533,7 @@ The following attributes are available on `link` objects:
show_root_full_path: false
heading_level: 5

::: mkdocs.structure.nav.Link.parent
::: mkdocs.structure.StructureItem.parent
options:
show_root_full_path: false
heading_level: 5
Expand Down
36 changes: 36 additions & 0 deletions mkdocs/structure/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from __future__ import annotations

import abc
from typing import TYPE_CHECKING, Iterable

if TYPE_CHECKING:
from mkdocs.structure.nav import Section


class StructureItem(metaclass=abc.ABCMeta):
"""An item in MkDocs structure - see concrete subclasses Section, Page or Link."""

@abc.abstractmethod
def __init__(self):
...

parent: Section | None = None
"""The immediate parent of the item in the site navigation. `None` if it's at the top level."""

@property
def is_top_level(self) -> bool:
return self.parent is None

title: str | None
is_section: bool = False
is_page: bool = False
is_link: bool = False

@property
def ancestors(self) -> Iterable[StructureItem]:
if self.parent is None:
return []
return [self.parent, *self.parent.ancestors]

def _indent_print(self, depth=0):
return (' ' * depth) + repr(self)
48 changes: 13 additions & 35 deletions mkdocs/structure/nav.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import TYPE_CHECKING, Iterator, TypeVar
from urllib.parse import urlsplit

from mkdocs.structure import StructureItem
from mkdocs.structure.pages import Page
from mkdocs.utils import nest_paths

Expand All @@ -16,7 +17,7 @@


class Navigation:
def __init__(self, items: list[Page | Section | Link], pages: list[Page]) -> None:
def __init__(self, items: list, pages: list[Page]) -> None:
self.items = items # Nested List with full navigation of Sections, Pages, and Links.
self.pages = pages # Flat List of subset of Pages in nav, in order.

Expand All @@ -32,34 +33,30 @@ def __init__(self, items: list[Page | Section | Link], pages: list[Page]) -> Non
pages: list[Page]
"""A flat list of all [page][mkdocs.structure.pages.Page] objects contained in the navigation."""

def __repr__(self):
def __str__(self) -> str:
return '\n'.join(item._indent_print() for item in self)

def __iter__(self) -> Iterator[Page | Section | Link]:
def __iter__(self) -> Iterator:
return iter(self.items)

def __len__(self) -> int:
return len(self.items)


class Section:
def __init__(self, title: str, children: list[Page | Section | Link]) -> None:
class Section(StructureItem):
def __init__(self, title: str, children: list[StructureItem]) -> None:
self.title = title
self.children = children

self.parent = None
self.active = False

def __repr__(self):
return f"Section(title='{self.title}')"
return f"Section(title={self.title!r})"

title: str
"""The title of the section."""

parent: Section | None
"""The immediate parent of the section or `None` if the section is at the top level."""

children: list[Page | Section | Link]
children: list[StructureItem]
"""An iterable of all child navigation objects. Children may include nested sections, pages and links."""

@property
Expand Down Expand Up @@ -87,28 +84,21 @@ def active(self, value: bool):
is_link: bool = False
"""Indicates that the navigation object is a "link" object. Always `False` for section objects."""

@property
def ancestors(self):
if self.parent is None:
return []
return [self.parent] + self.parent.ancestors

def _indent_print(self, depth=0):
ret = ['{}{}'.format(' ' * depth, repr(self))]
def _indent_print(self, depth: int = 0):
ret = [super()._indent_print(depth)]
for item in self.children:
ret.append(item._indent_print(depth + 1))
return '\n'.join(ret)


class Link:
class Link(StructureItem):
def __init__(self, title: str, url: str):
self.title = title
self.url = url
self.parent = None

def __repr__(self):
title = f"'{self.title}'" if (self.title is not None) else '[blank]'
return f"Link(title={title}, url='{self.url}')"
title = f"{self.title!r}" if self.title is not None else '[blank]'
return f"Link(title={title}, url={self.url!r})"

title: str
"""The title of the link. This would generally be used as the label of the link."""
Expand All @@ -117,9 +107,6 @@ def __repr__(self):
"""The URL that the link points to. The URL should always be an absolute URLs and
should not need to have `base_url` prepended."""

parent: Section | None
"""The immediate parent of the link. `None` if the link is at the top level."""

children: None = None
"""Links do not contain children and the attribute is always `None`."""

Expand All @@ -135,15 +122,6 @@ def __repr__(self):
is_link: bool = True
"""Indicates that the navigation object is a "link" object. Always `True` for link objects."""

@property
def ancestors(self):
if self.parent is None:
return []
return [self.parent] + self.parent.ancestors

def _indent_print(self, depth=0):
return '{}{}'.format(' ' * depth, repr(self))


def get_navigation(files: Files, config: MkDocsConfig) -> Navigation:
"""Build site navigation from config and files."""
Expand Down
34 changes: 7 additions & 27 deletions mkdocs/structure/pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import os
import posixpath
import warnings
from typing import TYPE_CHECKING, Any, Callable, Mapping, MutableMapping
from typing import TYPE_CHECKING, Any, Callable, MutableMapping
from urllib.parse import unquote as urlunquote
from urllib.parse import urljoin, urlsplit, urlunsplit

Expand All @@ -15,6 +15,7 @@
import markdown.treeprocessors
from markdown.util import AMP_SUBSTITUTE

from mkdocs.structure import StructureItem
from mkdocs.structure.toc import get_toc
from mkdocs.utils import get_build_date, get_markdown_title, meta, weak_property

Expand All @@ -23,7 +24,6 @@

from mkdocs.config.defaults import MkDocsConfig
from mkdocs.structure.files import File, Files
from mkdocs.structure.nav import Section
from mkdocs.structure.toc import TableOfContents

_unescape: Callable[[str], str]
Expand All @@ -36,17 +36,14 @@
log = logging.getLogger(__name__)


class Page:
def __init__(
self, title: str | None, file: File, config: MkDocsConfig | Mapping[str, Any]
) -> None:
class Page(StructureItem):
def __init__(self, title: str | None, file: File, config: MkDocsConfig) -> None:
file.page = self
self.file = file
if title is not None:
self.title = title

# Navigation attributes
self.parent = None
self.children = None
self.previous_page = None
self.next_page = None
Expand Down Expand Up @@ -74,12 +71,9 @@ def __eq__(self, other) -> bool:
)

def __repr__(self):
title = f"'{self.title}'" if (self.title is not None) else '[blank]'
title = f"{self.title!r}" if self.title is not None else '[blank]'
url = self.abs_url or self.file.url
return f"Page(title={title}, url='{url}')"

def _indent_print(self, depth=0):
return '{}{}'.format(' ' * depth, repr(self))
return f"Page(title={title}, url={url!r})"

markdown: str | None
"""The original Markdown content from the file."""
Expand Down Expand Up @@ -133,10 +127,6 @@ def active(self, value: bool):
def is_index(self) -> bool:
return self.file.name == 'index'

@property
def is_top_level(self) -> bool:
return self.parent is None

edit_url: str | None
"""The full URL to the source page in the source repository. Typically used to
provide a link to edit the source page. [base_url][] should not be used with this
Expand All @@ -157,10 +147,6 @@ def is_homepage(self) -> bool:
The value will be `None` if the current page is the last item in the site navigation
or if the current page is not included in the navigation at all."""

parent: Section | None
"""The immediate parent of the page in the site navigation. `None` if the
page is at the top level."""

children: None = None
"""Pages do not contain children and the attribute is always `None`."""

Expand All @@ -173,12 +159,6 @@ def is_homepage(self) -> bool:
is_link: bool = False
"""Indicates that the navigation object is a "link" object. Always `False` for page objects."""

@property
def ancestors(self):
if self.parent is None:
return []
return [self.parent] + self.parent.ancestors

def _set_canonical_url(self, base: str | None) -> None:
if base:
if not base.endswith('/'):
Expand Down Expand Up @@ -242,7 +222,7 @@ def _set_title(self) -> None:
)

@weak_property
def title(self) -> str | None:
def title(self) -> str | None: # type: ignore[override]
"""
Returns the title for the current page.
Expand Down

0 comments on commit 01be507

Please sign in to comment.