Skip to content

Commit

Permalink
Add attribute defaults, format
Browse files Browse the repository at this point in the history
Make default values part of the "machine readable" portion of an
attribute specification. This makes them stand out more in the
documentation, and also allows them to be exported to the JSON schema.
Also add value format, which is exported to the JSON schema but does not
appear in the main documentation.
  • Loading branch information
mwoehlke committed May 10, 2024
1 parent d6f9c8e commit d3e9bac
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 10 deletions.
47 changes: 41 additions & 6 deletions _extensions/cps/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
import re
from dataclasses import dataclass
from types import NoneType
from typing import cast
from typing import Any, cast

from docutils import nodes
from docutils.parsers.rst import Directive, directives, roles
from docutils.transforms import Transform
from docutils.utils.code_analyzer import Lexer, LexerError

import jsb

Expand Down Expand Up @@ -131,6 +132,8 @@ class AttributeDirective(Directive):
'overload': directives.flag,
'required': directives.flag,
'conditionally-required': directives.flag,
'format': directives.unchanged,
'default': directives.unchanged,
}

# -------------------------------------------------------------------------
Expand Down Expand Up @@ -190,6 +193,27 @@ def parse_type(self, typedesc):
else:
return [make_code(typedesc, 'object')]

# -------------------------------------------------------------------------
def parse_data(self, data):
language = 'json'
classes = ['code', 'highlight', f'highlight-{language}']

try:
tokens = Lexer(nodes.unescape(data, True), language, 'short')
except LexerError as error:
msg = self.state.inliner.reporter.warning(error)
prb = self.state.inliner.problematic(data, data, msg)
return [prb]

node = nodes.literal(data, '', classes=classes)
for classes, value in tokens:
if classes:
node += nodes.inline(value, value, classes=classes)
else:
node += nodes.Text(value)

return [node]

# -------------------------------------------------------------------------
def run(self):
name = self.arguments[0]
Expand All @@ -198,6 +222,8 @@ def run(self):
overload = 'overload' in self.options
required = 'required' in self.options
conditionally_required = 'conditionally-required' in self.options
typeformat = self.options.get('typeformat')
default = self.options.get('default')

if overload:
target = f'{name} ({context[0]})'
Expand Down Expand Up @@ -237,6 +263,9 @@ def run(self):
fields += self.make_field(
'Required', required_text, [nodes.Text(required_text)])
section += fields
if default:
fields += self.make_field(
'Default', default, self.parse_data(default))

# Parse attribute description
content = nodes.Element()
Expand All @@ -249,8 +278,10 @@ def run(self):
# Record object on domain
env = self.state.document.settings.env
domain = cast(CpsDomain, env.get_domain('cps'))
domain.note_attribute(name, context, typedesc, required=required,
description=simplify_text(content), node=section)
domain.note_attribute(name, context, typedesc, typeformat,
required=required, default=default,
description=simplify_text(content),
node=section)

# Return generated nodes
return [section]
Expand All @@ -266,8 +297,10 @@ def run(self):
@dataclass
class Attribute:
typedesc: str
typeformat: str
description: str
required: bool
default: Any

# =============================================================================
class AttributeSet:
Expand Down Expand Up @@ -340,9 +373,9 @@ def note_object(self, name, description):
self.objects[name] = description

# -------------------------------------------------------------------------
def note_attribute(self, name, context, typedesc,
required, description, node):
a = Attribute(typedesc, description, required)
def note_attribute(self, name, context, typedesc, typeformat,
required, default, description, node):
a = Attribute(typedesc, typeformat, description, required, default)
if name not in self.attributes:
self.attributes[name] = AttributeSet(name, context, a, node)
else:
Expand Down Expand Up @@ -381,7 +414,9 @@ def write_schema(app, exception):
schema.add_attribute(
attribute_set.name, i,
attribute.typedesc,
attribute.typeformat,
attribute.description,
attribute.default,
)

for context, attribute_ref in attribute_set.context.items():
Expand Down
13 changes: 11 additions & 2 deletions _packages/jsb.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,21 @@ def add_type(self, typedesc):
pass

# -------------------------------------------------------------------------
def add_attribute(self, name, instance, typedesc, description):
self.attributes[f'{name}@{instance}'] = {
def add_attribute(self, name, instance, typedesc, typeformat,
description, default=None):
attr = {
'description': description,
'$ref': f'#/definitions/types/{typedesc}',
}

if typeformat:
attr['format'] = typeformat

if default:
attr['default'] = json.loads(default)

self.attributes[f'{name}@{instance}'] = attr

self.add_type(typedesc)

# -------------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions schema-supplement.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ By definition, none of the following attributes are required.
.. cps:attribute:: website
:type: string
:context: package
:format: uri

Specifies the URI at which the package's website may be found.

Expand Down
4 changes: 2 additions & 2 deletions schema.rst
Original file line number Diff line number Diff line change
Expand Up @@ -385,14 +385,14 @@ Attribute names are case sensitive.
.. cps:attribute:: link_languages
:type: list(string)
:context: component configuration
:default: ["c"]

Specifies the ABI language or languages of a static library
(`type`_ :string:`"archive"`).
Officially supported (case-insensitive) values are
:string:`"c"` (no special handling required) and
:string:`"cpp"` (consuming the static library
also requires linking against the C++ standard runtime).
The default is :string:`"c"`.

.. ----------------------------------------------------------------------------
.. cps:attribute:: link_libraries
Expand Down Expand Up @@ -594,6 +594,7 @@ Attribute names are case sensitive.
.. cps:attribute:: version_schema
:type: string
:context: package
:default: "simple"

Specifies the structure
to which the package's version numbering conforms.
Expand All @@ -605,7 +606,6 @@ Attribute names are case sensitive.
If a package uses :string:`"custom"`,
version numbers may be compared,
but version ordering is not possible.
The default is :string:`"simple"`.

Needless to say,
changing a package's version scheme between releases
Expand Down

0 comments on commit d3e9bac

Please sign in to comment.