omlish 0.0.0.dev295__py3-none-any.whl → 0.0.0.dev296__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev295'
2
- __revision__ = 'eb4e318f611f286b283b0afe04a4d6acb13ca8ee'
1
+ __version__ = '0.0.0.dev296'
2
+ __revision__ = '8bdb4cc593092b4303a7f5633b09f5c428ba9bc1'
3
3
 
4
4
 
5
5
  #
@@ -107,6 +107,8 @@ class Project(ProjectBase):
107
107
  ],
108
108
 
109
109
  'templates': [
110
+ 'markupsafe ~= 3.0',
111
+
110
112
  'jinja2 ~= 3.1',
111
113
  ],
112
114
 
@@ -77,11 +77,14 @@ from .ordered import ( # noqa
77
77
 
78
78
  from .persistent.persistent import ( # noqa
79
79
  PersistentMap,
80
+ PersistentMapping,
80
81
  )
81
82
 
82
83
  if _ta.TYPE_CHECKING:
83
84
  from .persistent.treapmap import ( # noqa
85
+ TreapDict,
84
86
  TreapMap,
87
+ new_treap_dict,
85
88
  new_treap_map,
86
89
  )
87
90
  else:
@@ -108,6 +111,8 @@ else:
108
111
 
109
112
  from .sorted.sorted import ( # noqa
110
113
  SortedCollection,
114
+ SortedItems,
115
+ SortedIter,
111
116
  SortedListDict,
112
117
  SortedMapping,
113
118
  SortedMutableMapping,
@@ -1,6 +1,8 @@
1
1
  import abc
2
2
  import typing as ta
3
3
 
4
+ from ... import lang
5
+
4
6
 
5
7
  K = ta.TypeVar('K')
6
8
  V = ta.TypeVar('V')
@@ -9,7 +11,7 @@ V = ta.TypeVar('V')
9
11
  ##
10
12
 
11
13
 
12
- class PersistentMap(ta.Generic[K, V], abc.ABC):
14
+ class PersistentMap(lang.Abstract, ta.Generic[K, V]):
13
15
  @abc.abstractmethod
14
16
  def __len__(self) -> int:
15
17
  raise NotImplementedError
@@ -23,17 +25,36 @@ class PersistentMap(ta.Generic[K, V], abc.ABC):
23
25
  raise NotImplementedError
24
26
 
25
27
  @abc.abstractmethod
26
- def __iter__(self) -> ta.Iterator[tuple[K, V]]:
28
+ def __iter__(self) -> ta.Iterator[K]:
29
+ raise NotImplementedError
30
+
31
+ @abc.abstractmethod
32
+ def items(self) -> ta.Iterator[tuple[K, V]]:
27
33
  raise NotImplementedError
28
34
 
29
35
  @abc.abstractmethod
30
- def with_(self, k: K, v: V) -> 'PersistentMap[K, V]':
36
+ def with_(self, k: K, v: V) -> ta.Self:
31
37
  raise NotImplementedError
32
38
 
33
39
  @abc.abstractmethod
34
- def without(self, k: K) -> 'PersistentMap[K, V]':
40
+ def without(self, k: K) -> ta.Self:
41
+ raise NotImplementedError
42
+
43
+ @abc.abstractmethod
44
+ def default(self, k: K, v: V) -> ta.Self:
45
+ raise NotImplementedError
46
+
47
+
48
+ class PersistentMapping(
49
+ PersistentMap[K, V],
50
+ ta.Mapping[K, V],
51
+ lang.Abstract,
52
+ ta.Generic[K, V],
53
+ ):
54
+ @abc.abstractmethod
55
+ def __contains__(self, item: K) -> bool: # type: ignore[override]
35
56
  raise NotImplementedError
36
57
 
37
58
  @abc.abstractmethod
38
- def default(self, k: K, v: V) -> 'PersistentMap[K, V]':
59
+ def items(self) -> ta.Iterator[tuple[K, V]]: # type: ignore[override] # FIXME: ItemsView
39
60
  raise NotImplementedError
@@ -20,8 +20,10 @@ import abc
20
20
  import random
21
21
  import typing as ta
22
22
 
23
+ from ..sorted.sorted import SortedItems
23
24
  from . import treap
24
25
  from .persistent import PersistentMap
26
+ from .persistent import PersistentMapping
25
27
 
26
28
 
27
29
  K = ta.TypeVar('K')
@@ -31,7 +33,7 @@ V = ta.TypeVar('V')
31
33
  ##
32
34
 
33
35
 
34
- class TreapMap(PersistentMap[K, V]):
36
+ class TreapMap(PersistentMap[K, V], SortedItems[K, V]):
35
37
  __slots__ = ('_n', '_c')
36
38
 
37
39
  def __init__(
@@ -69,10 +71,10 @@ class TreapMap(PersistentMap[K, V]):
69
71
  raise KeyError(item)
70
72
  return n.value
71
73
 
72
- def __iter__(self) -> ta.Iterator[tuple[K, V]]:
74
+ def __iter__(self) -> ta.Iterator[K]:
73
75
  i = self.items()
74
76
  while i.has_next():
75
- yield i.next()
77
+ yield i.next()[0]
76
78
 
77
79
  def items(self) -> 'TreapMapIterator[K, V]':
78
80
  i = TreapMapIterator(
@@ -84,14 +86,6 @@ class TreapMap(PersistentMap[K, V]):
84
86
  i._n = n.left # noqa
85
87
  return i
86
88
 
87
- def items_from(self, k: K) -> 'TreapMapIterator[K, V]':
88
- lst = treap.place(self._n, (k, None), self._c) # type: ignore
89
- i = TreapMapIterator(
90
- _st=lst,
91
- _n=lst.pop(),
92
- )
93
- return i
94
-
95
89
  def items_desc(self) -> 'TreapMapReverseIterator[K, V]':
96
90
  i = TreapMapReverseIterator(
97
91
  _st=[],
@@ -102,6 +96,14 @@ class TreapMap(PersistentMap[K, V]):
102
96
  i._n = n.right # noqa
103
97
  return i
104
98
 
99
+ def items_from(self, k: K) -> 'TreapMapIterator[K, V]':
100
+ lst = treap.place(self._n, (k, None), self._c) # type: ignore
101
+ i = TreapMapIterator(
102
+ _st=lst,
103
+ _n=lst.pop(),
104
+ )
105
+ return i
106
+
105
107
  def items_from_desc(self, k: K) -> 'TreapMapReverseIterator[K, V]':
106
108
  lst = treap.place(self._n, (k, None), self._c, desc=True) # type: ignore
107
109
  i = TreapMapReverseIterator(
@@ -137,7 +139,22 @@ def new_treap_map(cmp: ta.Callable[[tuple[K, V], tuple[K, V]], int]) -> Persiste
137
139
  return TreapMap(_n=None, _c=cmp)
138
140
 
139
141
 
140
- class BaseTreapMapIterator(abc.ABC, ta.Generic[K, V]):
142
+ #
143
+
144
+
145
+ class TreapDict(TreapMap[K, V], PersistentMapping[K, V]):
146
+ __contains__ = TreapMap.__contains__
147
+ items = TreapMap.items
148
+
149
+
150
+ def new_treap_dict(cmp: ta.Callable[[tuple[K, V], tuple[K, V]], int]) -> PersistentMapping[K, V]:
151
+ return TreapDict(_n=None, _c=cmp)
152
+
153
+
154
+ ##
155
+
156
+
157
+ class BaseTreapMapIterator(abc.ABC, ta.Iterator[tuple[K, V]], ta.Generic[K, V]):
141
158
  __slots__ = ('_st', '_n')
142
159
 
143
160
  def __init__(
@@ -151,6 +168,12 @@ class BaseTreapMapIterator(abc.ABC, ta.Generic[K, V]):
151
168
  self._st = _st
152
169
  self._n = _n
153
170
 
171
+ def __iter__(self) -> ta.Self:
172
+ return self
173
+
174
+ def __next__(self) -> tuple[K, V]:
175
+ return self.next()
176
+
154
177
  def has_next(self) -> bool:
155
178
  return self._n is not None
156
179
 
@@ -170,30 +170,38 @@ class SkipList(SortedCollection[T]):
170
170
 
171
171
  #
172
172
 
173
- def iter(self, base: T | None = None) -> ta.Iterable[T]:
174
- if base is not None:
175
- cur = self._find(base)
176
- while cur is not None and self._compare(base, cur.value) > 0: # type: ignore
177
- cur = cur.next[0]
178
- else:
179
- cur = self._head.next[0]
173
+ def iter(self) -> ta.Iterator[T]:
174
+ cur = self._head.next[0]
180
175
 
181
176
  while cur is not None:
182
177
  yield cur.value # type: ignore
183
178
  cur = cur.next[0]
184
179
 
185
- def iter_desc(self, base: T | None = None) -> ta.Iterable[T]:
186
- if base is not None:
187
- cur = self._find(base)
188
- while cur is not self._head and self._compare(base, cur.value) < 0: # type: ignore
189
- cur = cur.prev # type: ignore
190
- else:
191
- cur = self._head.next[self._height - 1]
192
- while True:
193
- next = cur.next[cur.next.index(None) - 1 if None in cur.next else -1] # type: ignore # noqa
194
- if next is None:
195
- break
196
- cur = next
180
+ def iter_desc(self) -> ta.Iterator[T]:
181
+ cur = self._head.next[self._height - 1]
182
+ while True:
183
+ next = cur.next[cur.next.index(None) - 1 if None in cur.next else -1] # type: ignore # noqa
184
+ if next is None:
185
+ break
186
+ cur = next
187
+
188
+ while cur is not self._head:
189
+ yield cur.value # type: ignore
190
+ cur = cur.prev # type: ignore
191
+
192
+ def iter_from(self, base: T) -> ta.Iterator[T]:
193
+ cur = self._find(base)
194
+ while cur is not None and self._compare(base, cur.value) > 0: # type: ignore
195
+ cur = cur.next[0]
196
+
197
+ while cur is not None:
198
+ yield cur.value # type: ignore
199
+ cur = cur.next[0]
200
+
201
+ def iter_from_desc(self, base: T) -> ta.Iterator[T]:
202
+ cur = self._find(base)
203
+ while cur is not self._head and self._compare(base, cur.value) < 0: # type: ignore
204
+ cur = cur.prev # type: ignore
197
205
 
198
206
  while cur is not self._head:
199
207
  yield cur.value # type: ignore
@@ -13,7 +13,30 @@ V = ta.TypeVar('V')
13
13
  ##
14
14
 
15
15
 
16
- class SortedCollection(lang.Abstract, ta.Collection[T]):
16
+ class SortedIter(lang.Abstract, ta.Generic[T]):
17
+ @abc.abstractmethod
18
+ def iter(self) -> ta.Iterator[T]:
19
+ raise NotImplementedError
20
+
21
+ @abc.abstractmethod
22
+ def iter_desc(self) -> ta.Iterator[T]:
23
+ raise NotImplementedError
24
+
25
+ @abc.abstractmethod
26
+ def iter_from(self, base: T) -> ta.Iterator[T]:
27
+ raise NotImplementedError
28
+
29
+ @abc.abstractmethod
30
+ def iter_from_desc(self, base: T) -> ta.Iterator[T]:
31
+ raise NotImplementedError
32
+
33
+
34
+ class SortedCollection(
35
+ SortedIter[T],
36
+ ta.Collection[T],
37
+ lang.Abstract,
38
+ ta.Generic[T],
39
+ ):
17
40
  Comparator = ta.Callable[[U, U], int]
18
41
 
19
42
  @staticmethod
@@ -46,18 +69,13 @@ class SortedCollection(lang.Abstract, ta.Collection[T]):
46
69
  def remove(self, value: T) -> bool:
47
70
  raise NotImplementedError
48
71
 
49
- @abc.abstractmethod
50
- def iter(self, base: T | None = None) -> ta.Iterable[T]:
51
- raise NotImplementedError
52
72
 
53
- @abc.abstractmethod
54
- def iter_desc(self, base: T | None = None) -> ta.Iterable[T]:
55
- raise NotImplementedError
73
+ #
56
74
 
57
75
 
58
- class SortedMapping(ta.Mapping[K, V]):
76
+ class SortedItems(lang.Abstract, ta.Generic[K, V]):
59
77
  @abc.abstractmethod
60
- def items(self) -> ta.Iterator[tuple[K, V]]: # type: ignore
78
+ def items(self) -> ta.Iterator[tuple[K, V]]:
61
79
  raise NotImplementedError
62
80
 
63
81
  @abc.abstractmethod
@@ -73,10 +91,29 @@ class SortedMapping(ta.Mapping[K, V]):
73
91
  raise NotImplementedError
74
92
 
75
93
 
76
- class SortedMutableMapping(ta.MutableMapping[K, V], SortedMapping[K, V]):
94
+ class SortedMapping(
95
+ SortedItems[K, V],
96
+ ta.Mapping[K, V],
97
+ lang.Abstract,
98
+ ta.Generic[K, V],
99
+ ):
100
+ @abc.abstractmethod
101
+ def items(self) -> ta.Iterator[tuple[K, V]]: # type: ignore[override] # FIXME: ItemsView
102
+ raise NotImplementedError
103
+
104
+
105
+ class SortedMutableMapping(
106
+ ta.MutableMapping[K, V],
107
+ SortedMapping[K, V],
108
+ lang.Abstract,
109
+ ta.Generic[K, V],
110
+ ):
77
111
  pass
78
112
 
79
113
 
114
+ ##
115
+
116
+
80
117
  class SortedListDict(SortedMutableMapping[K, V]):
81
118
  @staticmethod
82
119
  def _item_comparator(a: tuple[K, V], b: tuple[K, V]) -> int:
@@ -119,7 +156,7 @@ class SortedListDict(SortedMutableMapping[K, V]):
119
156
  yield from self._impl.iter_desc()
120
157
 
121
158
  def items_from(self, key: K) -> ta.Iterator[tuple[K, V]]:
122
- yield from self._impl.iter((key, None))
159
+ yield from self._impl.iter_from((key, None))
123
160
 
124
161
  def items_from_desc(self, key: K) -> ta.Iterator[tuple[K, V]]:
125
- yield from self._impl.iter_desc((key, None))
162
+ yield from self._impl.iter_from_desc((key, None))
@@ -11,6 +11,7 @@ import typing as ta
11
11
  from ... import check
12
12
  from ... import lang
13
13
  from ..processing.base import ProcessingContext
14
+ from ..processing.base import ProcessingOption
14
15
  from ..processing.base import Processor
15
16
  from ..processing.priority import ProcessorPriority
16
17
  from ..processing.registry import register_processor_type
@@ -32,11 +33,13 @@ from .registry import generator_type_for_plan_type
32
33
  ##
33
34
 
34
35
 
35
- class PlanOnly(ta.NamedTuple):
36
+ @dc.dataclass(frozen=True)
37
+ class PlanOnly(ProcessingOption):
36
38
  b: bool
37
39
 
38
40
 
39
- class Verbose(ta.NamedTuple):
41
+ @dc.dataclass(frozen=True)
42
+ class Verbose(ProcessingOption):
40
43
  b: bool
41
44
 
42
45
 
@@ -13,6 +13,7 @@ from ..specs import ClassSpec
13
13
 
14
14
 
15
15
  T = ta.TypeVar('T')
16
+ ProcessingOptionT = ta.TypeVar('ProcessingOptionT', bound='ProcessingOption')
16
17
 
17
18
 
18
19
  ##
@@ -21,6 +22,10 @@ T = ta.TypeVar('T')
21
22
  ProcessingContextItemFactory: ta.TypeAlias = ta.Callable[['ProcessingContext'], ta.Any]
22
23
 
23
24
 
25
+ class ProcessingOption(lang.Abstract):
26
+ pass
27
+
28
+
24
29
  class ProcessingContext:
25
30
  def __init__(
26
31
  self,
@@ -28,7 +33,7 @@ class ProcessingContext:
28
33
  cs: ClassSpec,
29
34
  item_factories: ta.Mapping[type, ProcessingContextItemFactory],
30
35
  *,
31
- options: ta.Sequence[ta.Any] | None = None,
36
+ options: ta.Sequence[ProcessingOption] | None = None,
32
37
  ) -> None:
33
38
  super().__init__()
34
39
 
@@ -63,7 +68,7 @@ class ProcessingContext:
63
68
  self._items[ty] = ret
64
69
  return ret
65
70
 
66
- def option(self, ty: type[T]) -> T | None:
71
+ def option(self, ty: type[ProcessingOptionT]) -> ProcessingOptionT | None:
67
72
  return self._options_dct.get(ty)
68
73
 
69
74
 
@@ -1,9 +1,9 @@
1
- import typing as ta
2
1
 
3
2
  from .. import concerns as _concerns # noqa # imported for registration
4
3
  from ..generation import processor as gp
5
4
  from ..specs import ClassSpec
6
5
  from .base import ProcessingContext
6
+ from .base import ProcessingOption
7
7
  from .base import Processor
8
8
  from .registry import all_processing_context_item_factories
9
9
  from .registry import ordered_processor_types
@@ -19,7 +19,7 @@ def drive_cls_processing(
19
19
  plan_only: bool = False,
20
20
  verbose: bool = False,
21
21
  ) -> type:
22
- options: list[ta.Any] = []
22
+ options: list[ProcessingOption] = []
23
23
  if plan_only:
24
24
  options.append(gp.PlanOnly(True))
25
25
  if verbose:
omlish/dom/__init__.py ADDED
@@ -0,0 +1,26 @@
1
+ from .building import ( # noqa
2
+ d,
3
+ D,
4
+ )
5
+
6
+ from .content import ( # noqa
7
+ String,
8
+ Content,
9
+
10
+ Dom,
11
+
12
+ STRING_TYPES,
13
+ CONTENT_TYPES,
14
+
15
+ check_content,
16
+ iter_content,
17
+ )
18
+
19
+ from .rendering import ( # noqa
20
+ InvalidTagError,
21
+ StrForbiddenError,
22
+
23
+ Renderer,
24
+
25
+ render,
26
+ )
omlish/dom/building.py ADDED
@@ -0,0 +1,54 @@
1
+ import typing as ta
2
+
3
+ from .. import dataclasses as dc
4
+ from .content import Content
5
+ from .content import Dom
6
+ from .content import kwargs_to_attrs
7
+
8
+
9
+ ##
10
+
11
+
12
+ def d(
13
+ tag: str,
14
+ *attrs_and_contents: tuple[str, ta.Any] | Content,
15
+ **kwargs: ta.Any,
16
+ ) -> Dom:
17
+ c = []
18
+ for a in attrs_and_contents:
19
+ if isinstance(a, tuple):
20
+ k, v = a
21
+ if k in kwargs:
22
+ raise KeyError(f'Attribute {k} already set')
23
+ kwargs[k] = v
24
+ else:
25
+ c.append(a)
26
+
27
+ return Dom(
28
+ tag,
29
+ attrs=kwargs_to_attrs(**kwargs) or None,
30
+ body=c or None,
31
+ )
32
+
33
+
34
+ ##
35
+
36
+
37
+ @dc.dataclass(frozen=True)
38
+ class DomBuilder:
39
+ tag: str
40
+
41
+ def __call__(
42
+ self,
43
+ *attrs_and_contents: tuple[str, ta.Any] | Content,
44
+ **kwargs: ta.Any,
45
+ ) -> Dom:
46
+ return d(self.tag, *attrs_and_contents, **kwargs)
47
+
48
+
49
+ class DomAccessor:
50
+ def __getattr__(self, tag: str) -> DomBuilder:
51
+ return DomBuilder(tag)
52
+
53
+
54
+ D = DomAccessor()
omlish/dom/content.py ADDED
@@ -0,0 +1,129 @@
1
+ import keyword
2
+ import typing as ta
3
+
4
+ from .. import check
5
+ from .. import dataclasses as dc
6
+ from .. import lang
7
+
8
+
9
+ if ta.TYPE_CHECKING:
10
+ import markupsafe as ms
11
+
12
+ _HAS_MARKUPSAFE = True
13
+
14
+ else:
15
+ ms = lang.proxy_import('markupsafe')
16
+
17
+ _HAS_MARKUPSAFE = lang.can_import('markupsafe')
18
+
19
+
20
+ String: ta.TypeAlias = ta.Union[
21
+ str,
22
+ 'ms.Markup',
23
+ ]
24
+
25
+ Content: ta.TypeAlias = ta.Union[
26
+ list['Content'],
27
+ 'Dom',
28
+ String,
29
+ None,
30
+ ]
31
+
32
+ ContentT = ta.TypeVar('ContentT', bound=Content)
33
+
34
+
35
+ ##
36
+
37
+
38
+ ATTR_NAMES_BY_KWARG: ta.Mapping[str, str] = {
39
+ **{f'{k}_': k for k in keyword.kwlist if k == k.lower()},
40
+ }
41
+
42
+ ATTR_KWARGS_BY_NAME: ta.Mapping[str, str] = {v: k for k, v in ATTR_NAMES_BY_KWARG.items()}
43
+
44
+
45
+ def kwargs_to_attrs(**kwargs: ta.Any) -> dict[str, ta.Any]:
46
+ return {
47
+ ATTR_NAMES_BY_KWARG.get(k, k).replace('_', '-'): v
48
+ for k, v in kwargs.items()
49
+ }
50
+
51
+
52
+ ##
53
+
54
+
55
+ @dc.dataclass()
56
+ class Dom:
57
+ tag: str
58
+ attrs: dict[str, ta.Any | None] | None = dc.xfield(None, repr_fn=lang.opt_repr)
59
+ body: list[Content] | None = dc.xfield(None, repr_fn=lang.opt_repr)
60
+
61
+ def set(self, **kwargs: ta.Any) -> 'Dom':
62
+ if self.attrs is None:
63
+ self.attrs = {}
64
+ self.attrs.update(**kwargs_to_attrs(**kwargs))
65
+ return self
66
+
67
+ def unset(self, *keys: str) -> 'Dom':
68
+ if self.attrs is not None:
69
+ for k in keys:
70
+ self.attrs.pop(k, None)
71
+ return self
72
+
73
+ def add(self, *contents: Content) -> 'Dom':
74
+ if self.body is None:
75
+ self.body = []
76
+ for c in contents:
77
+ check_content(c)
78
+ self.body.extend(contents)
79
+ return self
80
+
81
+ def remove(self, *contents: Content, strict: bool = False) -> 'Dom':
82
+ if self.body is not None:
83
+ i = 0
84
+ while i < len(self.body):
85
+ e = self.body[i]
86
+ if any(c is e for c in contents):
87
+ del self.body[i]
88
+ elif strict:
89
+ raise ValueError(f'Content {e} not in body')
90
+ else:
91
+ i += 1
92
+ return self
93
+
94
+
95
+ ##
96
+
97
+
98
+ STRING_TYPES: tuple[type, ...] = (
99
+ str,
100
+ *([ms.Markup] if _HAS_MARKUPSAFE else []),
101
+ )
102
+
103
+ CONTENT_TYPES: tuple[type, ...] = (
104
+ list,
105
+ Dom,
106
+ *STRING_TYPES,
107
+ type(None),
108
+ )
109
+
110
+
111
+ def check_content(c: ContentT) -> ContentT:
112
+ if isinstance(c, list):
113
+ for e in c:
114
+ check_content(e)
115
+ else:
116
+ check.isinstance(c, CONTENT_TYPES)
117
+ return c
118
+
119
+
120
+ def iter_content(c: Content) -> ta.Iterator[Dom | String]:
121
+ if isinstance(c, list):
122
+ for e in c:
123
+ yield from iter_content(e)
124
+ elif isinstance(c, (Dom, *STRING_TYPES)):
125
+ yield c # type: ignore[misc]
126
+ elif c is None:
127
+ pass
128
+ else:
129
+ raise TypeError(c)