remove ConfigKey and GroupKey types
This commit is contained in:
@@ -1 +1,4 @@
|
|||||||
|
from .config import Config # noqa: F401
|
||||||
|
from .groupby import GroupBy # noqa: F401
|
||||||
from .plugin import GroupByPlugin # noqa: F401
|
from .plugin import GroupByPlugin # noqa: F401
|
||||||
|
from .watcher import GroupByCallbackArgs # noqa: F401
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
from inifile import IniFile
|
from inifile import IniFile
|
||||||
from lektor.utils import slugify
|
from lektor.utils import slugify
|
||||||
|
|
||||||
from typing import NewType, Set, Dict, Optional, Union
|
from typing import Set, Dict, Optional, Union
|
||||||
|
|
||||||
ConfigKey = NewType('ConfigKey', str) # attribute of lektor model
|
|
||||||
AnyConfig = Union['Config', IniFile, Dict]
|
AnyConfig = Union['Config', IniFile, Dict]
|
||||||
|
|
||||||
|
|
||||||
@@ -18,14 +17,14 @@ class Config:
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
key: ConfigKey, *,
|
key: str, *,
|
||||||
root: Optional[str] = None, # default: "/"
|
root: Optional[str] = None, # default: "/"
|
||||||
slug: Optional[str] = None, # default: "{attr}/{group}/index.html"
|
slug: Optional[str] = None, # default: "{attr}/{group}/index.html"
|
||||||
template: Optional[str] = None, # default: "groupby-{attr}.html"
|
template: Optional[str] = None, # default: "groupby-{attr}.html"
|
||||||
) -> None:
|
) -> None:
|
||||||
self.key = key
|
self.key = key
|
||||||
self.root = (root or '/').rstrip('/') + '/'
|
self.root = (root or '/').rstrip('/') + '/'
|
||||||
self.slug = slug or f'"{key}/" ~ this.key ~ "/"' # this: GroupBySource
|
self.slug = slug or (key + '/{key}/') # key = GroupBySource.key
|
||||||
self.template = template or f'groupby-{self.key}.html'
|
self.template = template or f'groupby-{self.key}.html'
|
||||||
# editable after init
|
# editable after init
|
||||||
self.enabled = True
|
self.enabled = True
|
||||||
@@ -57,7 +56,7 @@ class Config:
|
|||||||
return txt + '>'
|
return txt + '>'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_dict(key: ConfigKey, cfg: Dict[str, str]) -> 'Config':
|
def from_dict(key: str, cfg: Dict[str, str]) -> 'Config':
|
||||||
''' Set config fields manually. Allowed: key, root, slug, template. '''
|
''' Set config fields manually. Allowed: key, root, slug, template. '''
|
||||||
return Config(
|
return Config(
|
||||||
key=key,
|
key=key,
|
||||||
@@ -67,7 +66,7 @@ class Config:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_ini(key: ConfigKey, ini: IniFile) -> 'Config':
|
def from_ini(key: str, ini: IniFile) -> 'Config':
|
||||||
''' Read and parse ini file. Also adds dependency tracking. '''
|
''' Read and parse ini file. Also adds dependency tracking. '''
|
||||||
cfg = ini.section_as_dict(key) # type: Dict[str, str]
|
cfg = ini.section_as_dict(key) # type: Dict[str, str]
|
||||||
conf = Config.from_dict(key, cfg)
|
conf = Config.from_dict(key, cfg)
|
||||||
@@ -78,7 +77,7 @@ class Config:
|
|||||||
return conf
|
return conf
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_any(key: ConfigKey, config: AnyConfig) -> 'Config':
|
def from_any(key: str, config: AnyConfig) -> 'Config':
|
||||||
assert isinstance(config, (Config, IniFile, Dict))
|
assert isinstance(config, (Config, IniFile, Dict))
|
||||||
if isinstance(config, Config):
|
if isinstance(config, Config):
|
||||||
return config
|
return config
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ from lektor.sourceobj import SourceObject
|
|||||||
from lektor.utils import build_url
|
from lektor.utils import build_url
|
||||||
|
|
||||||
from typing import Set, Dict, List, Optional, Tuple
|
from typing import Set, Dict, List, Optional, Tuple
|
||||||
from .vobj import GroupBySource, GroupKey
|
from .vobj import GroupBySource
|
||||||
from .config import Config, ConfigKey, AnyConfig
|
from .config import Config, AnyConfig
|
||||||
from .watcher import Watcher
|
from .watcher import Watcher
|
||||||
|
|
||||||
|
|
||||||
@@ -19,13 +19,13 @@ class GroupBy:
|
|||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self._watcher = [] # type: List[Watcher]
|
self._watcher = [] # type: List[Watcher]
|
||||||
self._results = [] # type: List[GroupBySource]
|
self._results = [] # type: List[GroupBySource]
|
||||||
self._resolver = {} # type: Dict[str, Tuple[GroupKey, Config]]
|
self._resolver = {} # type: Dict[str, Tuple[str, Config]]
|
||||||
|
|
||||||
# ----------------
|
# ----------------
|
||||||
# Add observer
|
# Add observer
|
||||||
# ----------------
|
# ----------------
|
||||||
|
|
||||||
def add_watcher(self, key: ConfigKey, config: AnyConfig) -> Watcher:
|
def add_watcher(self, key: str, config: AnyConfig) -> Watcher:
|
||||||
''' Init Config and add to watch list. '''
|
''' Init Config and add to watch list. '''
|
||||||
w = Watcher(Config.from_any(key, config))
|
w = Watcher(Config.from_any(key, config))
|
||||||
self._watcher.append(w)
|
self._watcher.append(w)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from lektor.pluginsystem import Plugin # subclass
|
|||||||
from lektor.sourceobj import SourceObject # typing
|
from lektor.sourceobj import SourceObject # typing
|
||||||
|
|
||||||
from typing import List, Optional, Iterator
|
from typing import List, Optional, Iterator
|
||||||
from .vobj import GroupBySource, GroupByBuildProgram, GroupKey, VPATH
|
from .vobj import GroupBySource, GroupByBuildProgram, VPATH
|
||||||
from .groupby import GroupBy
|
from .groupby import GroupBy
|
||||||
from .pruner import prune
|
from .pruner import prune
|
||||||
from .watcher import GroupByCallbackArgs # typing
|
from .watcher import GroupByCallbackArgs # typing
|
||||||
@@ -39,7 +39,7 @@ class GroupByPlugin(Plugin):
|
|||||||
split = config.get(key + '.split') # type: str
|
split = config.get(key + '.split') # type: str
|
||||||
|
|
||||||
@watcher.grouping()
|
@watcher.grouping()
|
||||||
def _fn(args: GroupByCallbackArgs) -> Iterator[GroupKey]:
|
def _fn(args: GroupByCallbackArgs) -> Iterator[str]:
|
||||||
val = args.field
|
val = args.field
|
||||||
if isinstance(val, str):
|
if isinstance(val, str):
|
||||||
val = val.split(split) if split else [val] # make list
|
val = val.split(split) if split else [val] # make list
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ from lektor.environment import Expression
|
|||||||
from lektor.sourceobj import VirtualSourceObject # subclass
|
from lektor.sourceobj import VirtualSourceObject # subclass
|
||||||
from lektor.utils import build_url
|
from lektor.utils import build_url
|
||||||
|
|
||||||
from typing import Dict, List, Any, Optional, Iterator, NewType
|
from typing import Dict, List, Any, Optional, Iterator
|
||||||
from weakref import WeakSet
|
from weakref import WeakSet
|
||||||
from .config import Config
|
from .config import Config
|
||||||
from .pruner import track_not_prune
|
from .pruner import track_not_prune
|
||||||
from .util import report_config_error
|
from .util import report_config_error
|
||||||
|
|
||||||
VPATH = '@groupby' # potentially unsafe. All matching entries are pruned.
|
VPATH = '@groupby' # potentially unsafe. All matching entries are pruned.
|
||||||
GroupKey = NewType('GroupKey', str) # key of group-by
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------
|
# -----------------------------------
|
||||||
@@ -30,7 +29,7 @@ class GroupBySource(VirtualSourceObject):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
record: Record,
|
record: Record,
|
||||||
group: GroupKey,
|
group: str,
|
||||||
config: Config,
|
config: Config,
|
||||||
children: Optional[Dict[Record, List[object]]] = None,
|
children: Optional[Dict[Record, List[object]]] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -46,8 +45,11 @@ class GroupBySource(VirtualSourceObject):
|
|||||||
self._children[child] = extras
|
self._children[child] = extras
|
||||||
self._reverse_reference_records()
|
self._reverse_reference_records()
|
||||||
# evaluate slug Expression
|
# evaluate slug Expression
|
||||||
self.slug = self._eval(config.slug, field='slug') # type: str
|
if '{key}' in config.slug:
|
||||||
assert self.slug != Ellipsis, 'invalid config: ' + config.slug
|
self.slug = config.slug.replace('{key}', self.key)
|
||||||
|
else:
|
||||||
|
self.slug = self._eval(config.slug, field='slug')
|
||||||
|
assert self.slug != Ellipsis, 'invalid config: ' + config.slug
|
||||||
if self.slug and self.slug.endswith('/index.html'):
|
if self.slug and self.slug.endswith('/index.html'):
|
||||||
self.slug = self.slug[:-10]
|
self.slug = self.slug[:-10]
|
||||||
# extra fields
|
# extra fields
|
||||||
@@ -98,7 +100,7 @@ class GroupBySource(VirtualSourceObject):
|
|||||||
# -----------------------
|
# -----------------------
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def children(self):
|
def children(self) -> Dict[Record, List[object]]:
|
||||||
return self._children
|
return self._children
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ from lektor.db import Database, Record # typing
|
|||||||
from lektor.types.flow import Flow, FlowType
|
from lektor.types.flow import Flow, FlowType
|
||||||
from lektor.utils import bool_from_string
|
from lektor.utils import bool_from_string
|
||||||
|
|
||||||
from typing import Set, Dict, List, Tuple, Union, NamedTuple
|
from typing import Set, Dict, List, Tuple, Any, Union, NamedTuple
|
||||||
from typing import Optional, Callable, Iterable, Iterator, Generator
|
from typing import Optional, Callable, Iterable, Iterator, Generator
|
||||||
from .vobj import GroupBySource, GroupKey
|
from .vobj import GroupBySource
|
||||||
from .config import Config, ConfigKey
|
from .config import Config
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------
|
# -----------------------------------
|
||||||
@@ -21,14 +21,12 @@ class FieldKeyPath(NamedTuple):
|
|||||||
class GroupByCallbackArgs(NamedTuple):
|
class GroupByCallbackArgs(NamedTuple):
|
||||||
record: Record
|
record: Record
|
||||||
key: FieldKeyPath
|
key: FieldKeyPath
|
||||||
field: object # lektor model data-field value
|
field: Any # lektor model data-field value
|
||||||
|
|
||||||
|
|
||||||
GroupByCallbackYield = Union[GroupKey, Tuple[GroupKey, object]]
|
|
||||||
|
|
||||||
GroupingCallback = Callable[[GroupByCallbackArgs], Union[
|
GroupingCallback = Callable[[GroupByCallbackArgs], Union[
|
||||||
Iterator[GroupByCallbackYield],
|
Iterator[Union[str, Tuple[str, Any]]],
|
||||||
Generator[GroupByCallbackYield, Optional[str], None],
|
Generator[Union[str, Tuple[str, Any]], Optional[str], None],
|
||||||
]]
|
]]
|
||||||
|
|
||||||
|
|
||||||
@@ -36,10 +34,10 @@ GroupingCallback = Callable[[GroupByCallbackArgs], Union[
|
|||||||
# ModelReader
|
# ModelReader
|
||||||
# -----------------------------------
|
# -----------------------------------
|
||||||
|
|
||||||
class ModelReader:
|
class GroupByModelReader:
|
||||||
''' Find models and flow-models which contain attribute '''
|
''' Find models and flow-models which contain attribute '''
|
||||||
|
|
||||||
def __init__(self, db: Database, attrib: ConfigKey) -> None:
|
def __init__(self, db: Database, attrib: str) -> None:
|
||||||
self._flows = {} # type: Dict[str, Set[str]]
|
self._flows = {} # type: Dict[str, Set[str]]
|
||||||
self._models = {} # type: Dict[str, Dict[str, str]]
|
self._models = {} # type: Dict[str, Dict[str, str]]
|
||||||
# find flow blocks containing attribute
|
# find flow blocks containing attribute
|
||||||
@@ -67,7 +65,7 @@ class ModelReader:
|
|||||||
self,
|
self,
|
||||||
record: Record,
|
record: Record,
|
||||||
flatten: bool = False
|
flatten: bool = False
|
||||||
) -> Iterator[Tuple[FieldKeyPath, object]]:
|
) -> Iterator[Tuple[FieldKeyPath, Any]]:
|
||||||
'''
|
'''
|
||||||
Enumerate all fields of a Record with attrib = True.
|
Enumerate all fields of a Record with attrib = True.
|
||||||
Flows are either returned directly (flatten=False) or
|
Flows are either returned directly (flatten=False) or
|
||||||
@@ -97,30 +95,30 @@ class ModelReader:
|
|||||||
# State
|
# State
|
||||||
# -----------------------------------
|
# -----------------------------------
|
||||||
|
|
||||||
class State:
|
class GroupByState:
|
||||||
''' Holds and updates a groupby build state. '''
|
''' Store and update a groupby build state. {group: {record: [extras]}} '''
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.state = {} # type: Dict[GroupKey, Dict[Record, List[object]]]
|
self.state = {} # type: Dict[str, Dict[Record, List[Any]]]
|
||||||
self._processed = set() # type: Set[Record]
|
self._processed = set() # type: Set[Record]
|
||||||
|
|
||||||
def __contains__(self, record: Record) -> bool:
|
def __contains__(self, record: Record) -> bool:
|
||||||
''' Returns True if record was already processed. '''
|
''' Returns True if record was already processed. '''
|
||||||
return record.path in self._processed
|
return record.path in self._processed
|
||||||
|
|
||||||
def items(self) -> Iterable[Tuple[GroupKey, Dict]]:
|
def items(self) -> Iterable[Tuple[str, Dict[Record, List[Any]]]]:
|
||||||
''' Iterable with (GroupKey, {record: [extras]}) tuples. '''
|
''' Iterable with (group, {record: [extras]}) tuples. '''
|
||||||
return self.state.items()
|
return self.state.items()
|
||||||
|
|
||||||
def add(self, record: Record, group: Dict[GroupKey, List[object]]) -> None:
|
def add(self, record: Record, sub_groups: Dict[str, List[Any]]) -> None:
|
||||||
''' Append groups if not processed already. '''
|
''' Append groups if not processed already. {group: [extras]} '''
|
||||||
if record.path not in self._processed:
|
if record.path not in self._processed:
|
||||||
self._processed.add(record.path)
|
self._processed.add(record.path)
|
||||||
for group_key, extras in group.items():
|
for group, extras in sub_groups.items():
|
||||||
if group_key in self.state:
|
if group in self.state:
|
||||||
self.state[group_key][record] = extras
|
self.state[group][record] = extras
|
||||||
else:
|
else:
|
||||||
self.state[group_key] = {record: extras}
|
self.state[group] = {record: extras}
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------
|
# -----------------------------------
|
||||||
@@ -130,7 +128,7 @@ class State:
|
|||||||
class Watcher:
|
class Watcher:
|
||||||
'''
|
'''
|
||||||
Callback is called with (Record, FieldKeyPath, field-value).
|
Callback is called with (Record, FieldKeyPath, field-value).
|
||||||
Callback may yield one or more (group-key, extra-info) tuples.
|
Callback may yield one or more (group, extra-info) tuples.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, config: Config) -> None:
|
def __init__(self, config: Config) -> None:
|
||||||
@@ -144,7 +142,7 @@ class Watcher:
|
|||||||
Decorator to subscribe to attrib-elements.
|
Decorator to subscribe to attrib-elements.
|
||||||
If flatten = False, dont explode FlowType.
|
If flatten = False, dont explode FlowType.
|
||||||
|
|
||||||
(record, field-key, field) -> (group-key, extra-info)
|
(record, field-key, field) -> (group, extra-info)
|
||||||
'''
|
'''
|
||||||
def _decorator(fn: GroupingCallback) -> None:
|
def _decorator(fn: GroupingCallback) -> None:
|
||||||
self.flatten = flatten
|
self.flatten = flatten
|
||||||
@@ -155,8 +153,8 @@ class Watcher:
|
|||||||
''' Reset internal state. You must initialize before each build! '''
|
''' Reset internal state. You must initialize before each build! '''
|
||||||
assert callable(self.callback), 'No grouping callback provided.'
|
assert callable(self.callback), 'No grouping callback provided.'
|
||||||
self._root = self.config.root
|
self._root = self.config.root
|
||||||
self._state = State()
|
self._state = GroupByState()
|
||||||
self._model_reader = ModelReader(db, attrib=self.config.key)
|
self._model_reader = GroupByModelReader(db, attrib=self.config.key)
|
||||||
|
|
||||||
def should_process(self, node: Record) -> bool:
|
def should_process(self, node: Record) -> bool:
|
||||||
''' Check if record path is being watched. '''
|
''' Check if record path is being watched. '''
|
||||||
@@ -170,7 +168,7 @@ class Watcher:
|
|||||||
'''
|
'''
|
||||||
if record in self._state:
|
if record in self._state:
|
||||||
return
|
return
|
||||||
tmp = {} # type: Dict[GroupKey, List[object]]
|
tmp = {} # type: Dict[str, List[Any]] # {group: [extras]}
|
||||||
for key, field in self._model_reader.read(record, self.flatten):
|
for key, field in self._model_reader.read(record, self.flatten):
|
||||||
_gen = self.callback(GroupByCallbackArgs(record, key, field))
|
_gen = self.callback(GroupByCallbackArgs(record, key, field))
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user