Skip to content

Commit 13ff0b8

Browse files
committed
refactor: AAA
1 parent 1e68392 commit 13ff0b8

File tree

7 files changed

+144
-112
lines changed

7 files changed

+144
-112
lines changed

docs/_templates/version.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
:Date: :version.date.by-year:`📅{{ date }} <{{ date }}>`
2+
:Date: :version.date+by-year:`📅{{ date }} <{{ date }}>`
33
:Download: :tag:`{{ title }}`
44

55
{% for line in content %}

docs/changelog.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Change Log
1919
2020
Change log here.
2121
22+
:version.date#by-year:`2024`
23+
2224
Version 2.x
2325
===========
2426

docs/usage.rst

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -145,19 +145,7 @@ Indices
145145

146146
According to sphinx documentation, we use :ref:`sphinx:ref-role` to create reference to object indices, and the index name should prefixed with domain name.
147147

148-
General Index
149-
~~~~~~~~~~~~~
150-
151-
Index "``domain``-\ ``objtype``" (In this case, it is ``any-cat``) creates reference to object index which grouped by all referenceable field values.
152-
153-
================== ==============
154-
``:ref:`any-cat``` :ref:`any-cat`
155-
================== ==============
156-
157-
Field Specific Index
158-
~~~~~~~~~~~~~~~~~~~~
159-
160-
Index "``domain``-\ ``objtype``.\ ``field``" will be created for all reference Fields (In this case, it is ``any-cat.name``, ``any-cat.id`` and ``any-cat.color``).
148+
Index "``domain``-\ ``objtype``.\ ``field``" will be created for all referenceable Fields (In this case, it is ``any-cat.name``, ``any-cat.id`` and ``any-cat.color``).
161149

162150
These indices create reference to object index which grouped by specific field values.
163151

@@ -166,6 +154,8 @@ These indices create reference to object index which grouped by specific field v
166154
``:ref:`any-cat.id``` :ref:`any-cat.id`
167155
======================= ===================
168156

157+
.. todo: indexer
158+
169159
.. _writing-template:
170160

171161
Writing Template

src/sphinxnotes/any/domain.py

Lines changed: 41 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from sphinx.util import logging
1919
from sphinx.util.nodes import make_refnode
2020

21-
from .schema import Schema, Object
21+
from .schema import Schema, Object, RefType, Indexer, LiteralIndexer
2222
from .directives import AnyDirective
2323
from .roles import AnyRole
2424
from .indices import AnyIndex
@@ -49,9 +49,9 @@ class AnyDomain(Domain):
4949
roles: dict[str, RoleFunction] = {}
5050
#: A list of Index subclasses
5151
indices: list[type[AnyIndex]] = []
52-
#: AnyDomain specific: type -> index class
52+
#: AnyDomain specific: reftype -> index class
5353
_indices_for_reftype: dict[str, type[AnyIndex]] = {}
54-
#: AnyDomain specific: type -> Schema instance
54+
#: AnyDomain specific: objtype -> Schema instance
5555
_schemas: dict[str, Schema] = {}
5656

5757
initial_data: dict[str, Any] = {
@@ -120,7 +120,7 @@ def resolve_xref(
120120
logger.debug('[any] resolveing xref of %s', (typ, target))
121121

122122
reftype = RefType.parse(typ)
123-
objtype, objfield, objidx = reftype.objtype, reftype.field, reftype.index
123+
objtype, objfield, objidx = reftype.objtype, reftype.field, reftype.indexer
124124
objids = set()
125125
if objidx:
126126
pass # no need to lookup objds
@@ -142,10 +142,7 @@ def resolve_xref(
142142
if len(objids) > 1 or objidx is not None:
143143
# Mulitple objects found or reference index explicitly,
144144
# create link to indices page.
145-
(
146-
todocname,
147-
anchor,
148-
) = self._get_index_anchor(typ, target)
145+
(todocname, anchor) = self._get_index_anchor(typ, target)
149146
if not has_explicit_title:
150147
newtitle = schema.render_ambiguous_reference(title)
151148
logger.debug(
@@ -181,43 +178,47 @@ def add_schema(cls, schema: Schema) -> None:
181178
# Add to schemas dict
182179
cls._schemas[schema.objtype] = schema
183180

184-
# Generates reftypes for all referenceable fields
185-
# For later use when generating roles and indices.
186-
reftypes = [str(RefType(schema.objtype, None, None))]
187-
for name, field in schema.fields(all=False):
188-
if not field.ref:
189-
continue
190-
191-
# Field is unique , use ``:objtype.field:`` to reference.
192-
if field.uniq:
193-
reftype = str(RefType(schema.objtype, name, None))
194-
reftypes.append(reftype)
195-
continue
196-
197-
for name, field in schema.fields(all=False):
198-
# Field is not unique, link to index page.
199-
for indexer in field.indexers:
200-
reftype = str(RefType(schema.objtype, name, indexer.name))
201-
reftypes.append(reftype)
202-
203-
# FIXME: name and content can not be index now
204-
index = AnyIndex.derive(schema, name, indexer)
205-
cls.indices.append(index)
206-
cls._indices_for_reftype[reftype] = index
207-
208-
for reftype in reftypes:
209-
field = RefType.parse(reftype).field
210-
# Create role for referencing object by field
211-
cls.roles[reftype] = AnyRole.derive(schema, field)(
181+
def mkrole(reftype: RefType):
182+
"""Create and register role for referencing object (by field/index)."""
183+
role = AnyRole(
212184
# Emit warning when missing reference (node['refwarn'] = True)
213185
warn_dangling=True,
214186
# Inner node (contnode) would be replaced in resolve_xref method,
215187
# so fix its class.
216188
innernodeclass=literal,
217189
)
190+
cls.roles[str(reftype)] = role
191+
logger.debug(f'[any] make role {reftype}{type(role)}')
192+
193+
def mkindex(reftype: RefType, indexer: Indexer):
194+
"""Create and register index."""
195+
index = AnyIndex.derive(schema, reftype.field, indexer)
196+
cls.indices.append(index)
197+
cls._indices_for_reftype[str(reftype)] = index
198+
logger.debug(f'[any] make index {reftype}{type(index)}')
199+
200+
# Generates reftypes, roles, indexers for all fields.
201+
reftypes = [RefType(schema.objtype)]
202+
mkrole(reftypes[0]) # create reference role, FIXME: ambiguous reference
203+
for name, field in schema.fields():
204+
if field.ref:
205+
reftype = RefType(schema.objtype, field=name)
206+
reftypes.append(reftype)
207+
mkrole(reftype) # create a role to reference object
208+
if len(field.indexers) == 0:
209+
# Create a fallback indexer, for possible ambiguous reference
210+
# (if field is not unique).
211+
mkindex(reftype, LiteralIndexer())
212+
213+
for indexer in field.indexers:
214+
reftype = RefType(schema.objtype, field=name, indexer=indexer.name)
215+
reftypes.append(reftype)
216+
# Create role and index for reference objects by index.
217+
mkrole(reftype)
218+
mkindex(reftype, indexer)
218219

219220
# TODO: document
220-
cls.object_types[schema.objtype] = ObjType(schema.objtype, *reftypes)
221+
cls.object_types[schema.objtype] = ObjType(schema.objtype, *[ str(x) for x in reftypes])
221222
# Generates directive for creating object.
222223
cls.directives[schema.objtype] = AnyDirective.derive(schema)
223224

@@ -238,42 +239,11 @@ def warn_missing_reference(
238239
if domain and domain.name != AnyDomain.name:
239240
return None
240241

241-
objtype = RefType.parse(node['reftype']).objtype
242+
reftype = RefType.parse(node['reftype'])
242243
target = node['reftarget']
243244

244-
msg = f'undefined {objtype}: {target}'
245-
logger.warning(msg, location=node, type='ref', subtype=objtype)
245+
msg = f'undefined reftype {reftype}: {target}'
246+
logger.warning(msg, location=node, type='ref', subtype=reftype.objtype)
246247
return True
247248

248249

249-
class RefType(object):
250-
"""Reference type, used as role name and node['reftype'] and
251-
and *typ* argument of :meth:`AnyDomain.resolve_xref` method."""
252-
253-
#: :attr:`ObjType.lname`
254-
objtype: str
255-
#: :attr:`.schema.Field.name`
256-
field: str | None
257-
#: :attr:`.schema.Indexer.name`
258-
index: str | None
259-
260-
def __init__(self, objtype: str, field: str | None, index: str | None):
261-
self.objtype = objtype
262-
self.field = field
263-
self.index = index
264-
265-
@classmethod
266-
def parse(cls, reftype: str):
267-
v = reftype.split('.', maxsplit=2)
268-
objtype = v[0]
269-
field = v[1] if len(v) > 1 else None
270-
index = v[2][3:] if len(v) > 2 else None # skip "by-"
271-
return cls(objtype, field, index)
272-
273-
def __str__(self):
274-
s = self.objtype
275-
if self.field is not None:
276-
s += '.' + self.field
277-
if self.index is not None:
278-
s += '.' + 'by-' + self.index
279-
return s

src/sphinxnotes/any/indices.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from docutils import core, nodes
1717
from docutils.parsers.rst import roles
1818

19-
from .schema import Schema, Value, Indexer, Category
19+
from .schema import Schema, Value, Indexer, Category, RefType
2020

2121
logger = logging.getLogger(__name__)
2222

@@ -36,23 +36,13 @@ def derive(
3636
cls, schema: Schema, field: str | None, indexer: Indexer
3737
) -> type['AnyIndex']:
3838
"""Generate an AnyIndex child class for indexing object."""
39-
# TODO: add Indexer.name
40-
if field:
41-
typ = f'Any{schema.objtype.title()}{field.title()}IndexBy{indexer.name.title()}'
42-
name = schema.objtype + '.' + field + '.by-' + indexer.name # TODO: RefType
43-
localname = f'{schema.objtype.title()} {field.title()} Reference Index by {indexer.name.title()}'
44-
else:
45-
typ = f'Any{schema.objtype.title()}IndexBy{indexer.name.title()}'
46-
name = schema.objtype + '.by-' + indexer.name # TODO: RefType
47-
localname = (
48-
f'{schema.objtype.title()} Reference Index by {indexer.name.title()}'
49-
)
39+
reftype = RefType(schema.objtype, field=field, indexer=indexer.name)
5040
return type(
51-
typ,
41+
reftype.index_classname(),
5242
(cls,),
5343
{
54-
'name': name,
55-
'localname': localname,
44+
'name': reftype.index_name(),
45+
'localname': reftype.index_localname(),
5646
'shortname': 'references',
5747
'schema': schema,
5848
'field': field,

src/sphinxnotes/any/roles.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,7 @@ class AnyRole(XRefRole):
2525
objtype.
2626
"""
2727

28-
@classmethod
29-
def derive(cls, schema: Schema, field: str | None = None) -> type['AnyRole']:
30-
"""Generate an AnyRole child class for referencing object."""
31-
return type(
32-
'Any%s%sRole' % (schema.objtype.title(), field.title() if field else ''),
33-
(cls,),
34-
{},
35-
)
28+
# NOTE: derive is not necessary for now.
29+
# @classmethod
30+
# def derive(cls) -> type['AnyRole']:
31+
# pass

src/sphinxnotes/any/schema.py

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ class Forms:
354354
required: bool = False
355355
form: Form = Forms.PLAIN
356356
indexers: list[Indexer] = dataclasses.field(
357-
default_factory=lambda: [LiteralIndexer()]
357+
default_factory=lambda: []
358358
)
359359

360360
def value_of(self, rawval: str | None) -> Value:
@@ -438,7 +438,7 @@ def __init__(
438438
else:
439439
has_unique = field.uniq
440440

441-
def fields(self, all=True) -> list[tuple[str, Field]]:
441+
def fields(self, exclude_name: bool = False, exclude_content: bool = False) -> list[tuple[str, Field]]:
442442
"""Return all fields of schema, including name and content.
443443
444444
.. note::
@@ -448,9 +448,9 @@ def fields(self, all=True) -> list[tuple[str, Field]]:
448448
"""
449449

450450
fields = list(self.attrs.items())
451-
if all and self.content is not None:
451+
if not exclude_content and self.content is not None:
452452
fields.insert(0, (self.CONTENT_KEY, self.content))
453-
if all and self.name is not None:
453+
if not exclude_name and self.name is not None:
454454
fields.insert(0, (self.NAME_KEY, self.name))
455455
return fields
456456

@@ -631,3 +631,87 @@ def __eq__(self, other: Any) -> bool:
631631
if not isinstance(other, Schema):
632632
return False
633633
return pickle.dumps(self) == pickle.dumps(other)
634+
635+
class RefType(object):
636+
"""Reference type, object can be referenced:
637+
638+
- by type *objtype*
639+
- by field *objtype*.*field*
640+
- by field index *objtype*.*field*+by-*index*
641+
"""
642+
643+
#: Object type, same to :attr:`Schema.objtype`.
644+
objtype: str
645+
#: Name of field, see :attr:`.schema.Field.name`.
646+
field: str | None
647+
#: Name of indexer, see :attr:`.schema.Indexer.name`.
648+
indexer: str | None
649+
650+
def __init__(self, objtype: str, field: str | None = None, indexer: str | None = None):
651+
self.objtype = objtype
652+
self.field = field
653+
self.indexer = indexer
654+
655+
@classmethod
656+
def parse(cls, reftype: str):
657+
"""Format: <objtype>[.<field>[+<action>]]. Possible action:
658+
659+
- by-<indexer>
660+
"""
661+
662+
if '+' in reftype:
663+
reftype, action = reftype.split('+', 1)
664+
if action.startswith('by-'):
665+
index = action[3:]
666+
else:
667+
raise ValueError(f'unknown action {action} in RefType {reftype}')
668+
else:
669+
index = None
670+
671+
if '.' in reftype:
672+
objtype, field = reftype.split('.', 1)
673+
else:
674+
objtype, field = reftype, None
675+
676+
return cls(objtype, field, index)
677+
678+
679+
def __str__(self):
680+
"""Used as key of role and index inside :cls:`AnyDomain`."""
681+
s = self.objtype
682+
if self.field:
683+
s += '.' + self.field
684+
if self.indexer:
685+
s += '+' + 'by-' + self.indexer
686+
return s
687+
688+
689+
def index_name(self) -> str:
690+
"""Used as :attr:`AnyIndex.name`."""
691+
s = self.objtype
692+
if self.field:
693+
s += '.' + self.field
694+
if self.indexer and self.indexer != LiteralIndexer.name:
695+
s += '+' + 'by-' + self.indexer
696+
return s
697+
698+
699+
def index_localname(self) -> str:
700+
"""Used as :attr:`AnyIndex.localname`."""
701+
if self.field:
702+
localname = f'{self.objtype.title()} {self.field.title()} Reference Index'
703+
else:
704+
localname = f'{self.objtype.title()} Reference Index'
705+
if self.indexer and self.indexer != LiteralIndexer.name:
706+
localname += 'by ' + self.indexer.title()
707+
return localname
708+
709+
def index_classname(self) -> str:
710+
"""Used as name of :attr:`AnyIndex` sub-class."""
711+
if self.field:
712+
clsname = f'Any{self.objtype.title()}{self.field.title()}Index'
713+
else:
714+
clsname = f'Any{self.objtype.title()}Index'
715+
if self.indexer and self.indexer != LiteralIndexer.name:
716+
clsname += 'By' + self.indexer.title()
717+
return clsname

0 commit comments

Comments
 (0)