diff --git a/bikeshed/boilerplate.py b/bikeshed/boilerplate.py index 07afd90988..57f4d99ac6 100644 --- a/bikeshed/boilerplate.py +++ b/bikeshed/boilerplate.py @@ -157,7 +157,7 @@ def getFillContainer(tag: str, doc: t.SpecT, default: bool = False) -> t.Element # Otherwise, append to the end of the document, # unless you're in the byos group - if doc.doctype.group.name == "byos": + if doc.doctype.group.name == "BYOS": return None if default: return doc.body @@ -240,7 +240,7 @@ def removeUnwantedBoilerplate(doc: t.SpecT) -> None: def w3cStylesheetInUse(doc: t.SpecT) -> bool: - return doc.md.prepTR or doc.doctype.group.name == "w3c" + return doc.md.prepTR or doc.doctype.group.name == "W3C" def addBikeshedBoilerplate(doc: t.SpecT) -> None: diff --git a/bikeshed/doctypes/__init__.py b/bikeshed/doctypes/__init__.py index e8a2d179ef..56ce75a303 100644 --- a/bikeshed/doctypes/__init__.py +++ b/bikeshed/doctypes/__init__.py @@ -1 +1 @@ -from .manager import NIL_GROUP, NIL_ORG, NIL_STATUS, DoctypeManager, Group, GroupW3C, Org, Status, StatusW3C +from .manager import NIL_GROUP, NIL_ORG, NIL_STATUS, DoctypeManager, Group, Org, Status diff --git a/bikeshed/doctypes/manager.py b/bikeshed/doctypes/manager.py index 8b46f2863b..f9ecc73d59 100644 --- a/bikeshed/doctypes/manager.py +++ b/bikeshed/doctypes/manager.py @@ -14,7 +14,7 @@ class Doctype: group: Group status: Status - def __str__(self): + def __str__(self) -> str: return f"Doctype" @@ -97,7 +97,7 @@ class Org: @staticmethod def fromKdlNode(node: kdl.Node) -> Org: - name = t.cast(str, node.args[0]) + name = t.cast(str, node.args[0]).upper() self = Org(name) for child in node.getAll("group"): g = Group.fromKdlNode(child, org=self) @@ -120,6 +120,7 @@ class Group: privSec: bool org: Org requires: list[str] = dataclasses.field(default_factory=list) + type: str | None = None def fullName(self) -> str: if self.org: @@ -129,14 +130,15 @@ def fullName(self) -> str: @staticmethod def fromKdlNode(node: kdl.Node, org: Org) -> Group: - if org.name == "w3c": - return GroupW3C.fromKdlNode(node, org) - name = t.cast(str, node.args[0]) + name = t.cast(str, node.args[0]).upper() privSec = t.cast(bool, node.props.get("priv-sec", False)) - requiresNode = node.get("requires") - self = Group(name, privSec, org) - if requiresNode: - self.requires = t.cast("list[str]", list(requiresNode.getArgs((..., str)))) + if "type" in node.props: + groupType = str(node.props.get("type")).lower() + else: + groupType = None + self = Group(name, privSec, org, type=groupType) + for n in node.getAll("requires"): + self.requires.extend(t.cast("list[str]", n.getArgs((..., str)))) return self def __bool__(self) -> bool: @@ -146,24 +148,13 @@ def __bool__(self) -> bool: NIL_GROUP = Group("(not provided)", privSec=False, org=NIL_ORG) -@dataclasses.dataclass -class GroupW3C(Group): - type: str | None = None - - @staticmethod - def fromKdlNode(node: kdl.Node, org: Org) -> GroupW3C: - name = t.cast(str, node.args[0]) - privSec = t.cast(bool, node.props.get("priv-sec", False)) - groupType = t.cast("str|None", node.props.get("type")) - return GroupW3C(name, privSec, org, [], groupType) - - @dataclasses.dataclass class Status: name: str longName: str org: Org requires: list[str] = dataclasses.field(default_factory=list) + groupTypes: list[str] = dataclasses.field(default_factory=list) def fullName(self) -> str: if self.org: @@ -183,14 +174,13 @@ def looselyMatch(self, rawStatus: str) -> bool: def fromKdlNode(node: kdl.Node, org: Org | None = None) -> Status: if org is None: org = NIL_ORG - if org and org.name == "w3c": - return StatusW3C.fromKdlNode(node, org) - name = t.cast(str, node.args[0]) + name = t.cast(str, node.args[0]).upper() longName = t.cast(str, node.args[1]) self = Status(name, longName, org) - requiresNode = node.get("requires") - if requiresNode: - self.requires = t.cast("list[str]", list(requiresNode.getArgs((..., str)))) + for n in node.getAll("requires"): + self.requires.extend(t.cast("list[str]", n.getArgs((..., str)))) + for n in node.getAll("group-types"): + self.groupTypes.extend(str(x).lower() for x in n.getArgs((..., str))) return self def __bool__(self) -> bool: @@ -198,23 +188,3 @@ def __bool__(self) -> bool: NIL_STATUS = Status("(not provided)", "", org=NIL_ORG) - - -@dataclasses.dataclass -class StatusW3C(Status): - groupTypes: list[str] = dataclasses.field(default_factory=list) - - @staticmethod - def fromKdlNode(node: kdl.Node, org: Org | None = None) -> StatusW3C: - if org is None: - org = NIL_ORG - name = t.cast(str, node.args[0]) - longName = t.cast(str, node.args[1]) - self = StatusW3C(name, longName, org) - requiresNode = node.get("requires") - if requiresNode: - self.requires = t.cast("list[str]", list(requiresNode.getArgs((..., str)))) - groupTypesNode = node.get("group-types") - if groupTypesNode: - self.groupTypes = t.cast("list[str]", list(groupTypesNode.getArgs((..., str)))) - return self diff --git a/bikeshed/doctypes/utils.py b/bikeshed/doctypes/utils.py index c86f02f399..a02f2f06d4 100644 --- a/bikeshed/doctypes/utils.py +++ b/bikeshed/doctypes/utils.py @@ -4,7 +4,7 @@ from .. import messages as m if t.TYPE_CHECKING: - from . import DoctypeManager, Group, GroupW3C, Org, Status, StatusW3C # pylint: disable=cyclic-import + from . import DoctypeManager, Group, Org, Status # pylint: disable=cyclic-import def canonicalize( @@ -20,10 +20,12 @@ def canonicalize( # any inline org specifiers. # Then, figure out what the actual org name is. orgFromStatus, statusName = splitOrg(rawStatus) + orgFromStatus = orgFromStatus.upper() if orgFromStatus is not None else None statusName = statusName.upper() if statusName is not None else None orgFromGroup, groupName = splitOrg(rawGroup) - groupName = groupName.lower() if groupName is not None else None + orgFromGroup = orgFromGroup.upper() if orgFromGroup is not None else None + groupName = groupName.upper() if groupName is not None else None orgName = reconcileOrgs(rawOrg, orgFromStatus, orgFromGroup) @@ -95,12 +97,23 @@ def canonicalize( if group and status and status.org and status.org != group.org: # If using an org-specific Status, Group must match. # (Any group can use a generic status.) - possibleStatusNames = config.englishFromList(f"'{x}'" for x in group.org.statuses) + possibleStatusNames = [x.name for x in group.org.statuses.values()] m.die( - f"Your Group is in the '{group.org.name}' Org, but your Status is only usable in the '{status.org.name}' Org. Allowed Status values for '{group.org.name}' are: {possibleStatusNames}", + f"Your Group ({group.name}) is in the '{group.org.name}' Org, but your Status ({status.name}) is only usable in the '{status.org.name}' Org. Allowed Status values for '{group.org.name}' are {config.englishFromList(sorted(possibleStatusNames))}", ) - if group and status and group.org.name == "w3c": + if group and group.type is not None and status and status.groupTypes and group.type not in status.groupTypes: + allowedStatuses = [s.name for s in group.org.statuses.values() if group.type in s.groupTypes] + if allowedStatuses: + m.warn( + f"You used Status {status.name}, but your Group ({group.name}) is limited to the statuses {config.englishFromList(sorted(allowedStatuses))}.", + ) + else: + m.die( + f"PROGRAMMING ERROR: Group '{group.fullName()}' has type '{group.type}', but none of the {group.org.name} Statuses are associated with that type.", + ) + + if group and status and group.org.name == "W3C": # Apply the special w3c rules validateW3CStatus(group, status) @@ -158,9 +171,9 @@ def reconcileOrgs(fromRaw: str | None, fromStatus: str | None, fromGroup: str | # Since there are three potential sources of "org" name, # figure out what the name actually is, # and complain if they disagree. - fromRaw = fromRaw.lower() if fromRaw else None - fromStatus = fromStatus.lower() if fromStatus else None - fromGroup = fromGroup.lower() if fromGroup else None + fromRaw = fromRaw.upper() if fromRaw else None + fromStatus = fromStatus.upper() if fromStatus else None + fromGroup = fromGroup.upper() if fromGroup else None orgName: str | None = fromRaw @@ -188,10 +201,7 @@ def reconcileOrgs(fromRaw: str | None, fromStatus: str | None, fromGroup: str | def validateW3CStatus(group: Group, status: Status) -> None: - if t.TYPE_CHECKING: - assert isinstance(group, GroupW3C) - assert isinstance(status, StatusW3C) - if status.org is None and status.name == "DREAM": + if status.name == "DREAM": m.warn("You used Status:DREAM for a W3C document. Consider Status:UD instead.") if status.name in ("IG-NOTE", "WG-NOTE"): @@ -199,24 +209,4 @@ def validateW3CStatus(group: Group, status: Status) -> None: f"Under Process2021, {status.name} is no longer a valid status. Use NOTE (or one of its variants NOTE-ED, NOTE-FPWD, NOTE-WD) instead.", ) - if group.type is not None and group.type not in status.groupTypes: - if group.type == "ig": - longTypeName = "W3C Interest Groups" - elif group.type == "tag": - longTypeName = "the W3C TAG" - elif group.type == "cg": - longTypeName = "W3C Community/Business Groups" - else: - longTypeName = "W3C Working Groups" - allowedStatuses = [ - x for x in t.cast("list[StatusW3C]", group.org.statuses.values()) if group.type in x.groupTypes - ] - if allowedStatuses: - m.warn( - f"You used Status:{status.name}, but {longTypeName} are limited to these statuses: {allowedStatuses}.", - ) - else: - m.die( - f"PROGRAMMING ERROR: Group '{group.fullName()}' has type '{group.type}', but that isn't present in any of org's Statuses.", - ) - return + diff --git a/bikeshed/metadata.py b/bikeshed/metadata.py index 757b7d7b02..f5da5cbdd2 100644 --- a/bikeshed/metadata.py +++ b/bikeshed/metadata.py @@ -186,7 +186,7 @@ def computeImplicitMetadata(self, doc: t.SpecT) -> None: # Do some "computed metadata", based on the value of other metadata. # Only call this when you're sure all metadata sources are parsed. - if doc.doctype.group.name == "byos": + if doc.doctype.group.name == "BYOS": self.boilerplate.default = False if not self.repository and doc: self.repository = getSpecRepository(doc) @@ -213,7 +213,7 @@ def computeImplicitMetadata(self, doc: t.SpecT) -> None: m.state.dieOn = self.dieOn def validate(self, doc: t.SpecT) -> bool: - if doc.doctype.group.name == "byos": + if doc.doctype.group.name == "BYOS": return True if not self.hasMetadata: @@ -285,7 +285,7 @@ def fillTextMacros(self, macros: t.DefaultDict[str, str], doc: t.SpecT) -> None: macros["longstatus"] = doc.doctype.status.longName else: macros["longstatus"] = "" - if doc.doctype.group.name == "w3c": + if doc.doctype.group.name == "W3C": if doc.doctype.status.name in ("LCWD", "FPWD"): macros["status"] = "WD" elif doc.doctype.status.name in ("NOTE-FPWD", "NOTE-WD"): @@ -328,7 +328,7 @@ def fillTextMacros(self, macros: t.DefaultDict[str, str], doc: t.SpecT) -> None: if self.deadline: macros["deadline"] = self.deadline.strftime(f"{self.deadline.day} %B %Y") macros["isodeadline"] = self.deadline.strftime("%Y-%m-%d") - if doc.doctype.org.name == "w3c" and "Date" in doc.doctype.status.requires: + if doc.doctype.org.name == "W3C" and "Date" in doc.doctype.status.requires: macros["version"] = ( f"https://www.w3.org/TR/{macros['year']}/{doc.doctype.status.name}-{macros['vshortname']}-{macros['cdate']}/" ) @@ -350,7 +350,7 @@ def fillTextMacros(self, macros: t.DefaultDict[str, str], doc: t.SpecT) -> None: macros["mailinglist"] = self.mailingList if self.mailingListArchives: macros["mailinglistarchives"] = self.mailingListArchives - if doc.doctype.org.name == "w3c": + if doc.doctype.org.name == "W3C": statusName = doc.doctype.status.name if statusName == "FPWD": macros["w3c-stylesheet-url"] = "https://www.w3.org/StyleSheets/TR/2021/W3C-WD" diff --git a/bikeshed/retrieve.py b/bikeshed/retrieve.py index ade8d6d260..a2d33045ac 100644 --- a/bikeshed/retrieve.py +++ b/bikeshed/retrieve.py @@ -122,13 +122,13 @@ def retrieveBoilerplateFile( if group is None: group = doc.doctype.group - groupName = group.name if group else None + groupName = group.name.lower() if group else None if status is None: status = doc.doctype.status - statusName = status.name if status else None + statusName = status.name.upper() if status else None if org is None: org = doc.doctype.org - orgName = org.name if org else None + orgName = org.name.lower() if org else None searchLocally = allowLocal and doc.md.localBoilerplate[name]