Skip to content

Commit

Permalink
More type hints.
Browse files Browse the repository at this point in the history
  • Loading branch information
idlesign committed Apr 12, 2022
1 parent a610944 commit 673485b
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 56 deletions.
18 changes: 10 additions & 8 deletions sitetree/sitetreeapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@
from .settings import (
ALIAS_TRUNK, ALIAS_THIS_CHILDREN, ALIAS_THIS_SIBLINGS, ALIAS_THIS_PARENT_SIBLINGS, ALIAS_THIS_ANCESTOR_CHILDREN,
UNRESOLVED_ITEM_MARKER, RAISE_ITEMS_ERRORS_ON_DEBUG, CACHE_TIMEOUT, CACHE_NAME, DYNAMIC_ONLY, ADMIN_APP_NAME,
SITETREE_CLS)
SITETREE_CLS,
)
from .utils import get_tree_model, get_tree_item_model, import_app_sitetree_module, generate_id_for

if False: # pragma: nocover
from django.contrib.auth.models import User # noqa
from .models import TreeItemBase, TreeBase

TypeDynamicTrees = Dict[str, Union[Dict[str, List['TreeBase']], List['TreeBase']]]
TypeStrExpr = Union[str, FilterExpression]

MODEL_TREE_CLASS = get_tree_model()
MODEL_TREE_ITEM_CLASS = get_tree_item_model()
Expand Down Expand Up @@ -750,7 +752,7 @@ def init_tree(

return tree_alias, sitetree_items

def get_current_page_title(self, tree_alias: str, context: Context) -> str:
def get_current_page_title(self, tree_alias: TypeStrExpr, context: Context) -> str:
"""Returns resolved from sitetree title for current page.
:param tree_alias:
Expand All @@ -759,7 +761,7 @@ def get_current_page_title(self, tree_alias: str, context: Context) -> str:
"""
return self.get_current_page_attr('title_resolved', tree_alias, context)

def get_current_page_attr(self, attr_name: str, tree_alias: str, context: Context) -> str:
def get_current_page_attr(self, attr_name: str, tree_alias: TypeStrExpr, context: Context) -> str:
"""Returns an arbitrary attribute of a sitetree item resolved as current for current page.
:param attr_name:
Expand Down Expand Up @@ -795,7 +797,7 @@ def get_ancestor_level(self, current_item: 'TreeItemBase', depth: int = 1) -> 'T

return self.get_ancestor_level(current_item.parent, depth=depth-1)

def menu(self, tree_alias: str, tree_branches: str, context: Context) -> List['TreeItemBase']:
def menu(self, tree_alias: TypeStrExpr, tree_branches: TypeStrExpr, context: Context) -> List['TreeItemBase']:
"""Builds and returns menu structure for 'sitetree_menu' tag.
:param tree_alias:
Expand Down Expand Up @@ -928,7 +930,7 @@ def get_permissions(self, user: 'User', item: 'TreeItemBase') -> set:
"""
return user.get_all_permissions()

def breadcrumbs(self, tree_alias: str, context: Context) -> List['TreeItemBase']:
def breadcrumbs(self, tree_alias: TypeStrExpr, context: Context) -> List['TreeItemBase']:
"""Builds and returns breadcrumb trail structure for 'sitetree_breadcrumbs' tag.
:param tree_alias:
Expand Down Expand Up @@ -969,7 +971,7 @@ def climb(base_item):

return items

def tree(self, tree_alias: str, context: Context) -> List['TreeItemBase']:
def tree(self, tree_alias: TypeStrExpr, context: Context) -> List['TreeItemBase']:
"""Builds and returns tree structure for 'sitetree_tree' tag.
:param tree_alias:
Expand All @@ -989,7 +991,7 @@ def tree(self, tree_alias: str, context: Context) -> List['TreeItemBase']:

def children(
self,
parent_item: 'TreeItemBase',
parent_item: Union[str, 'TreeItemBase'],
navigation_type: str,
use_template: str,
context: Context
Expand Down Expand Up @@ -1116,7 +1118,7 @@ def tree_climber(self, tree_alias: str, base_item: 'TreeItemBase'):

def resolve_var(
self,
varname: Union[str, 'TreeItemBase', FilterExpression],
varname: Union[TypeStrExpr, 'TreeItemBase'],
context: Context = None
) -> Any:
"""Resolves name as a variable in a given context.
Expand Down
123 changes: 75 additions & 48 deletions sitetree/templatetags/sitetree.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
from typing import Optional, List

from django import template
from django.template.base import FilterExpression
from django.template import Context
from django.template.base import FilterExpression, Parser, Token
from django.template.loader import get_template

from ..sitetreeapp import get_sitetree
from ..sitetreeapp import get_sitetree, TypeStrExpr

if False: # pragma: nocover
from ..models import TreeItemBase # noqa


register = template.Library()


@register.tag
def sitetree_tree(parser, token):
def sitetree_tree(parser: Parser, token: Token) -> 'sitetree_treeNode':
"""Parses sitetree tag parameters.
Two notation types are possible:
Expand All @@ -29,13 +36,13 @@ def sitetree_tree(parser, token):
if tokens_num in (3, 5):
tree_alias = parser.compile_filter(tokens[2])
return sitetree_treeNode(tree_alias, use_template)
else:
raise template.TemplateSyntaxError(
'%r tag requires two arguments. E.g. {%% sitetree_tree from "mytree" %%}.' % tokens[0])

raise template.TemplateSyntaxError(
'%r tag requires two arguments. E.g. {%% sitetree_tree from "mytree" %%}.' % tokens[0])


@register.tag
def sitetree_children(parser, token):
def sitetree_children(parser: Parser, token: Token) -> 'sitetree_childrenNode':
"""Parses sitetree_children tag parameters.
Six arguments:
Expand All @@ -59,14 +66,14 @@ def sitetree_children(parser, token):
tree_item = tokens[2]
navigation_type = tokens[4]
return sitetree_childrenNode(tree_item, navigation_type, use_template)
else:
raise template.TemplateSyntaxError(
'%r tag requires six arguments. '
'E.g. {%% sitetree_children of someitem for menu template "sitetree/mychildren.html" %%}.' % tokens[0])

raise template.TemplateSyntaxError(
'%r tag requires six arguments. '
'E.g. {%% sitetree_children of someitem for menu template "sitetree/mychildren.html" %%}.' % tokens[0])


@register.tag
def sitetree_breadcrumbs(parser, token):
def sitetree_breadcrumbs(parser: Parser, token: Token) -> 'sitetree_breadcrumbsNode':
"""Parses sitetree_breadcrumbs tag parameters.
Two notation types are possible:
Expand All @@ -87,13 +94,13 @@ def sitetree_breadcrumbs(parser, token):
if tokens_num == 3:
tree_alias = parser.compile_filter(tokens[2])
return sitetree_breadcrumbsNode(tree_alias, use_template)
else:
raise template.TemplateSyntaxError(
'%r tag requires two arguments. E.g. {%% sitetree_breadcrumbs from "mytree" %%}.' % tokens[0])

raise template.TemplateSyntaxError(
'%r tag requires two arguments. E.g. {%% sitetree_breadcrumbs from "mytree" %%}.' % tokens[0])


@register.tag
def sitetree_menu(parser, token):
def sitetree_menu(parser: Parser, token: Token) -> 'sitetree_menuNode':
"""Parses sitetree_menu tag parameters.
{% sitetree_menu from "mytree" include "trunk,1,level3" %}
Expand All @@ -119,14 +126,14 @@ def sitetree_menu(parser, token):
tree_alias = parser.compile_filter(tokens[2])
tree_branches = parser.compile_filter(tokens[4])
return sitetree_menuNode(tree_alias, tree_branches, use_template)
else:
raise template.TemplateSyntaxError(
'%r tag requires four arguments. '
'E.g. {%% sitetree_menu from "mytree" include "trunk,1,level3" %%}.' % tokens[0])

raise template.TemplateSyntaxError(
'%r tag requires four arguments. '
'E.g. {%% sitetree_menu from "mytree" include "trunk,1,level3" %%}.' % tokens[0])


@register.tag
def sitetree_url(parser, token):
def sitetree_url(parser: Parser, token: Token) -> 'sitetree_urlNode':
"""This tag is much the same as Django built-in 'url' tag.
The difference is that after 'for' it should get TreeItem object.
Expand All @@ -135,77 +142,91 @@ def sitetree_url(parser, token):


@register.tag
def sitetree_page_title(parser, token):
def sitetree_page_title(parser: Parser, token: Token) -> 'sitetree_page_titleNode':
"""Renders a title for current page, resolved against sitetree item representing current URL."""
return sitetree_page_titleNode.for_tag(parser, token, 'from', 'sitetree_page_title from "mytree"')


@register.tag
def sitetree_page_description(parser, token):
def sitetree_page_description(parser: Parser, token: Token) -> 'sitetree_page_descriptionNode':
"""Renders a description for the current page, resolved against sitetree item representing current URL."""
return sitetree_page_descriptionNode.for_tag(parser, token, 'from', 'sitetree_page_description from "mytree"')


@register.tag
def sitetree_page_hint(parser, token):
def sitetree_page_hint(parser: Parser, token: Token) -> 'sitetree_page_hintNode':
"""Renders a hint for the current page, resolved against sitetree item representing current URL."""
return sitetree_page_hintNode.for_tag(parser, token, 'from', 'sitetree_page_hint from "mytree"')


class sitetree_treeNode(template.Node):
"""Renders tree items from specified site tree."""

def __init__(self, tree_alias, use_template):
def __init__(self, tree_alias: FilterExpression, use_template: Optional[FilterExpression]):
self.use_template = use_template
self.tree_alias = tree_alias

def render(self, context):
tree_items = get_sitetree().tree(self.tree_alias, context)
def render(self, context: Context) -> str:
tree_items = get_sitetree().tree(tree_alias=self.tree_alias, context=context)
return render(context, tree_items, self.use_template or 'sitetree/tree.html')


class sitetree_childrenNode(template.Node):
"""Renders tree items under specified parent site tree item."""

def __init__(self, tree_item, navigation_type, use_template):
def __init__(self, tree_item: str, navigation_type: str, use_template: Optional[FilterExpression]):
self.use_template = use_template
self.tree_item = tree_item
self.navigation_type = navigation_type

def render(self, context):
return get_sitetree().children(self.tree_item, self.navigation_type, self.use_template.resolve(context), context)
def render(self, context: Context) -> str:
return get_sitetree().children(
parent_item=self.tree_item,
navigation_type=self.navigation_type,
use_template=self.use_template.resolve(context),
context=context
)


class sitetree_breadcrumbsNode(template.Node):
"""Renders breadcrumb trail items from specified site tree."""

def __init__(self, tree_alias, use_template):
def __init__(self, tree_alias: FilterExpression, use_template: Optional[FilterExpression]):
self.use_template = use_template
self.tree_alias = tree_alias

def render(self, context):
tree_items = get_sitetree().breadcrumbs(self.tree_alias, context)
def render(self, context: Context) -> str:
tree_items = get_sitetree().breadcrumbs(tree_alias=self.tree_alias, context=context)
return render(context, tree_items, self.use_template or 'sitetree/breadcrumbs.html')


class sitetree_menuNode(template.Node):
"""Renders specified site tree menu items."""

def __init__(self, tree_alias, tree_branches, use_template):
def __init__(
self,
tree_alias: FilterExpression,
tree_branches: FilterExpression,
use_template: Optional[FilterExpression]
):
self.use_template = use_template
self.tree_alias = tree_alias
self.tree_branches = tree_branches

def render(self, context):
tree_items = get_sitetree().menu(self.tree_alias, self.tree_branches, context)
def render(self, context: Context) -> str:
tree_items = get_sitetree().menu(
tree_alias=self.tree_alias,
tree_branches=self.tree_branches,
context=context
)
return render(context, tree_items, self.use_template or 'sitetree/menu.html')


class SimpleNode(template.Node):
"""Simple node with `as` clause support."""

@classmethod
def for_tag(cls, parser, token, preposition, error_hint):
def for_tag(cls, parser: Parser, token: Token, preposition: str, error_hint: str) -> 'SimpleNode':
"""Node constructor to be used in tags."""
tokens = token.split_contents()

Expand All @@ -218,25 +239,25 @@ def for_tag(cls, parser, token, preposition, error_hint):
f'{tokens[0]} tag requires at least two arguments. E.g. {{% {error_hint} %}}.')

@classmethod
def get_as_var(cls, tokens):
def get_as_var(cls, tokens: List[str]) -> Optional[str]:
"""Returns context variable from `as` template tag clause if any.
Modifies tokens inplace.
:param tokens:
:rtype: None|str
"""
as_var = None
if tokens[-2] == 'as':
as_var = tokens[-1]
tokens[-2:] = []
return as_var

def __init__(self, item, as_var):
def __init__(self, item: FilterExpression, as_var: Optional[str]):
self.item = item
self.as_var = as_var

def get_value(self, context):
def get_value(self, context: Context):
"""Should return a computed value to be used in render()."""

def render(self, context) -> str:
Expand All @@ -250,32 +271,36 @@ def render(self, context) -> str:
class sitetree_urlNode(SimpleNode):
"""Resolves and renders specified url."""

def get_value(self, context):
def get_value(self, context: Context):
return get_sitetree().url(self.item, context)


class sitetree_page_titleNode(SimpleNode):
"""Renders a page title from the specified site tree."""

def get_value(self, context):
def get_value(self, context: Context):
return get_sitetree().get_current_page_title(self.item, context)


class sitetree_page_descriptionNode(SimpleNode):
"""Renders a page description from the specified site tree."""

def get_value(self, context):
return get_sitetree().get_current_page_attr('description', self.item, context)
def get_value(self, context: Context):
return get_sitetree().get_current_page_attr(
attr_name='description',
tree_alias=self.item,
context=context
)


class sitetree_page_hintNode(SimpleNode):
"""Renders a page hint from the specified site tree."""

def get_value(self, context):
def get_value(self, context: Context):
return get_sitetree().get_current_page_attr('hint', self.item, context)


def detect_clause(parser, clause_name, tokens):
def detect_clause(parser: Parser, clause_name: str, tokens: List[str]):
"""Helper function detects a certain clause in tag tokens list.
Returns its value.
Expand All @@ -284,12 +309,14 @@ def detect_clause(parser, clause_name, tokens):
t_index = tokens.index(clause_name)
clause_value = parser.compile_filter(tokens[t_index + 1])
del tokens[t_index:t_index + 2]

else:
clause_value = None

return clause_value


def render(context, tree_items, use_template):
def render(context: Context, tree_items: List['TreeItemBase'], use_template: TypeStrExpr):
"""Render helper is used by template node functions
to render given template with given tree items in context.
Expand Down

0 comments on commit 673485b

Please sign in to comment.