Skip to content

Commit 2440845

Browse files
authored
Merge pull request #12 from alexrudy:docs/util-tag
Add documentation for the tag utility
2 parents fd1f5f2 + 9409f8e commit 2440845

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

docs/api/bootlace.util.Tag.rst

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
Tag
2+
===
3+
4+
.. currentmodule:: bootlace.util
5+
6+
.. autoclass:: Tag
7+
:show-inheritance:
8+
9+
.. rubric:: Attributes Summary
10+
11+
.. autosummary::
12+
13+
~Tag.attributes
14+
~Tag.classes
15+
~Tag.tag
16+
17+
.. rubric:: Methods Summary
18+
19+
.. autosummary::
20+
21+
~Tag.__call__
22+
~Tag.__tag__
23+
~Tag.update
24+
25+
.. rubric:: Attributes Documentation
26+
27+
.. autoattribute:: attributes
28+
.. autoattribute:: classes
29+
.. autoattribute:: tag
30+
31+
.. rubric:: Methods Documentation
32+
33+
.. automethod:: __call__
34+
.. automethod:: __tag__
35+
.. automethod:: update

src/bootlace/util.py

+31-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"IntoTag",
3232
"MaybeTaggable",
3333
"Taggable",
34+
"Tag",
3435
"as_tag",
3536
"ids",
3637
"is_active_endpoint",
@@ -147,6 +148,7 @@ def __len__(self) -> int:
147148
return len(self.tag.attributes.get("class", "").split())
148149

149150
def add(self, *classes: str) -> tags.html_tag: # type: ignore[override]
151+
"""Add classes to the tag."""
150152
current: list[str] = self.tag.attributes.get("class", "").split()
151153
for cls in classes:
152154
if cls not in current:
@@ -155,6 +157,7 @@ def add(self, *classes: str) -> tags.html_tag: # type: ignore[override]
155157
return self.tag
156158

157159
def remove(self, *classes: str) -> tags.html_tag: # type: ignore[override]
160+
"""Remove classes from the tag."""
158161
current: list[str] = self.tag.attributes.get("class", "").split()
159162
for cls in classes:
160163
if cls in current:
@@ -163,9 +166,11 @@ def remove(self, *classes: str) -> tags.html_tag: # type: ignore[override]
163166
return self.tag
164167

165168
def discard(self, value: str) -> None:
169+
"""Remove a class if it exists."""
166170
self.remove(value)
167171

168172
def swap(self, old: str, new: str) -> tags.html_tag:
173+
"""Swap one class for another."""
169174
current: list[str] = self.tag.attributes.get("class", "").split()
170175
if old in current:
171176
current.remove(old)
@@ -179,6 +184,7 @@ def swap(self, old: str, new: str) -> tags.html_tag:
179184
class PrefixAccessor:
180185
"""A helper for accessing attributes with a prefix."""
181186

187+
#: Attribute prefix
182188
prefix: str = attrs.field()
183189

184190
def __get__(self, instance: tags.html_tag, owner: type[tags.html_tag]) -> "PrefixAccess":
@@ -188,7 +194,10 @@ def __get__(self, instance: tags.html_tag, owner: type[tags.html_tag]) -> "Prefi
188194
@attrs.define
189195
class PrefixAccess(MutableMapping[str, str]):
190196

197+
#: Attribute prefix
191198
prefix: str = attrs.field()
199+
200+
#: The tag to access
192201
tag: tags.html_tag = attrs.field()
193202

194203
def __getitem__(self, name: str) -> str:
@@ -209,10 +218,12 @@ def __len__(self) -> int:
209218
return sum(1 for _ in self)
210219

211220
def set(self, name: str, value: str) -> tags.html_tag:
221+
"""Set an attribute with the given name."""
212222
self[name] = value
213223
return self.tag
214224

215225
def remove(self, name: str) -> tags.html_tag:
226+
"""Remove an attribute with the given name."""
216227
del self[name]
217228
return self.tag
218229

@@ -221,20 +232,30 @@ def remove(self, name: str) -> tags.html_tag:
221232
class HtmlIDScope:
222233
"""A helper for generating unique HTML IDs."""
223234

235+
#: A mapping of scopes to counters
224236
scopes: collections.defaultdict[str, itertools.count] = attrs.field(
225237
factory=lambda: collections.defaultdict(itertools.count)
226238
)
227239

228240
def __call__(self, scope: str) -> str:
241+
"""Generate a unique ID for a given scope.
242+
243+
Parameters
244+
----------
245+
scope : str
246+
Scopes are used to group IDs together, e.g. items in a list, or a form and its fields.
247+
"""
229248
counter = next(self.scopes[scope])
230249
if counter == 0:
231250
return scope
232251
return f"{scope}-{counter}"
233252

234253
def factory(self, scope: str) -> functools.partial:
254+
"""Create a factory function for generating IDs in a specific scope."""
235255
return functools.partial(self, scope)
236256

237257
def reset(self) -> None:
258+
"""Reset all ID scopes."""
238259
self.scopes.clear()
239260

240261

@@ -279,7 +300,8 @@ class Tag(Generic[H]):
279300
280301
Holds the tag type as well as attributes for the tag. This can be used
281302
by calling the instance as a function to create a tag, or by calling the
282-
:meth:`update` method to apply the attributes to an existing tag."""
303+
:meth:`update` method to apply the attributes to an existing tag.
304+
"""
283305

284306
#: The tag type
285307
tag: type[H] = attrs.field()
@@ -291,11 +313,18 @@ class Tag(Generic[H]):
291313
attributes: dict[str, str] = attrs.field(factory=dict)
292314

293315
def __tag__(self) -> H:
316+
"""Create a tag from the attributes and classes."""
294317
tag = self.tag(**self.attributes)
295318
tag.classes.add(*self.classes)
296319
return tag
297320

298321
def __call__(self, *args: Any, **kwds: Any) -> H:
322+
"""Create a tag from the attributes and classes.
323+
324+
This method is a convenience wrapper around :meth:`__tag__` that allows
325+
the tag to be created with additional arguments and keyword arguments passed
326+
to the tag constructor.
327+
"""
299328
tag = self.tag(*args, **{**self.attributes, **kwds})
300329
tag.classes.add(*self.classes)
301330
return tag
@@ -307,6 +336,7 @@ def __getitem__(self, name: str) -> str:
307336
return self.attributes[name]
308337

309338
def update(self, tag: H) -> H:
339+
"""Update the tag with the attributes and classes."""
310340
tag.classes.add(*self.classes)
311341
tag.attributes.update(self.attributes)
312342
return tag

0 commit comments

Comments
 (0)