Skip to content

Commit fe157ef

Browse files
Liudmila Molkovajoaopgrassi
andauthored
Adds required stability property to enum members (#267)
Co-authored-by: Joao Grassi <5938087+joaopgrassi@users.noreply.github.com>
1 parent 213927e commit fe157ef

45 files changed

Lines changed: 629 additions & 66 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

semantic-conventions/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Please update the changelog as part of any significant pull request.
44

55
## Unreleased
66

7+
- BREAKING: Add `stability` (required) and `deprecated` (optional) properties to `EnumMember`
8+
([#267](https://github.com/open-telemetry/build-tools/pull/267))
79
- Change minimum support python version to 3.10 in setup.cfg and Dockerfile
810
([#285](https://github.com/open-telemetry/build-tools/pull/285))
911
- BREAKING: Make stability required (also: fix ref and extends, render badges on metrics).

semantic-conventions/README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,18 @@ The `{semconv version}` (e.g. `1.24.0`) is the previously released version of se
133133
Following checks are performed
134134

135135
- On all attributes and metrics (experimental and stable):
136-
- attributes and metrics must not be removed.
136+
- attributes and metrics must not be removed
137+
- enum attribute members must not be removed
137138

138139
- On stable attributes and attribute templates:
139140
- stability must not be changed
140141
- the type of attribute must not be changed
141142
- enum attribute: type of value must not be changed
142-
- enum attribute: members must not be removed (changing `id` field is allowed, as long as `value` does not change)
143+
144+
- On stable enum attribute members:
145+
- stability must not be changed
146+
- `id` and `value` must not be changed
147+
143148
- On stable metrics:
144149
- stability must not be changed
145150
- instrument and unit must not be changed

semantic-conventions/semconv.schema.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,9 @@
259259
"note": {
260260
"type": "string",
261261
"description": "longer description. It defaults to an empty string."
262+
},
263+
"stability": {
264+
"allOf": [{ "$ref": "#/definitions/StabilityLevel" }]
262265
}
263266
}
264267
}
@@ -343,6 +346,14 @@
343346
}
344347
]
345348
},
349+
"StabilityLevel": {
350+
"description": "specifies the stability level. Can be 'stable' or 'experimental' (the default).",
351+
"type": "string",
352+
"enum": [
353+
"stable",
354+
"experimental"
355+
]
356+
},
346357
"Attribute": {
347358
"type": "object",
348359
"allOf": [
@@ -416,6 +427,9 @@
416427
"deprecated": {
417428
"type": "string",
418429
"description": "specifies if the attribute is deprecated. The string provided as <description> MUST specify why it's deprecated and/or what to use instead."
430+
},
431+
"stability": {
432+
"allOf": [{ "$ref": "#/definitions/StabilityLevel" }]
419433
}
420434
}
421435
},

semantic-conventions/src/opentelemetry/semconv/model/semantic_attribute.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,17 @@ def parse(
187187
stability = SemanticAttribute.parse_stability(
188188
attribute.get("stability"), position_data, strict_validation
189189
)
190+
if stability == StabilityLevel.EXPERIMENTAL and isinstance(
191+
attr_type, EnumAttributeType
192+
):
193+
for member in attr_type.members:
194+
if member.stability == StabilityLevel.STABLE:
195+
msg = (
196+
f"Member '{member.member_id}' is marked as stable "
197+
+ "but it is not allowed on experimental attribute!"
198+
)
199+
raise ValidationError.from_yaml_pos(position_data["type"], msg)
200+
190201
deprecated = SemanticAttribute.parse_deprecated(
191202
attribute.get("deprecated"), position_data
192203
)
@@ -482,7 +493,7 @@ def parse(attribute_type):
482493
attribute_type.lc.data["members"], "Enumeration without members!"
483494
)
484495

485-
allowed_keys = ["id", "value", "brief", "note"]
496+
allowed_keys = ["id", "value", "brief", "note", "stability", "deprecated"]
486497
mandatory_keys = ["id", "value"]
487498
for member in attribute_type["members"]:
488499
validate_values(member, allowed_keys, mandatory_keys)
@@ -492,12 +503,26 @@ def parse(attribute_type):
492503
f"Invalid value used in enum: <{member['value']}>",
493504
)
494505
validate_id(member["id"], member.lc.data["id"])
506+
507+
stability_str = member.get("stability")
508+
if not stability_str:
509+
raise ValidationError.from_yaml_pos(
510+
member.lc.data["id"],
511+
f"Enumeration member '{member['value']}' must have a stability level",
512+
)
513+
514+
stability = SemanticAttribute.parse_stability(stability_str, member.lc.data)
515+
deprecated = SemanticAttribute.parse_deprecated(
516+
member.get("deprecated"), member.lc.data
517+
)
495518
members.append(
496519
EnumMember(
497520
member_id=member["id"],
498521
value=member["value"],
499522
brief=member.get("brief", member["id"]).strip(),
500523
note=member.get("note", "").strip(),
524+
stability=stability,
525+
deprecated=deprecated,
501526
)
502527
)
503528
enum_type = AttributeType.get_type(members[0].value)
@@ -516,6 +541,8 @@ class EnumMember:
516541
value: str
517542
brief: str
518543
note: str
544+
stability: StabilityLevel
545+
deprecated: str
519546

520547

521548
class MdLink:

semantic-conventions/src/opentelemetry/semconv/templating/compatibility.py

Lines changed: 73 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -67,49 +67,66 @@ def _check_attribute(self, prev: SemanticAttribute, problems: list[Problem]):
6767
problems.append(Problem("attribute", prev.fqn, "was removed"))
6868
return
6969

70+
self._check_stability(
71+
prev.stability, cur.stability, "attribute", prev.fqn, problems
72+
)
7073
if prev.stability == StabilityLevel.STABLE:
71-
if cur.stability != prev.stability:
74+
self._check_attribute_type(prev, cur, problems)
75+
76+
if (
77+
isinstance(prev.attr_type, EnumAttributeType)
78+
and
79+
# this makes mypy happy, we already checked that type is the same for stable attributes
80+
isinstance(cur.attr_type, EnumAttributeType)
81+
):
82+
for member in prev.attr_type.members:
83+
self._check_member(prev.fqn, member, cur.attr_type.members, problems)
84+
85+
def _check_stability(
86+
self,
87+
prev: StabilityLevel,
88+
cur: StabilityLevel,
89+
signal: str,
90+
fqn: str,
91+
problems: list[Problem],
92+
):
93+
if prev == StabilityLevel.STABLE and cur != prev:
94+
problems.append(
95+
Problem(signal, fqn, f"stability changed from '{prev}' to '{cur}'")
96+
)
97+
98+
def _check_attribute_type(
99+
self, prev: SemanticAttribute, cur: SemanticAttribute, problems: list[Problem]
100+
):
101+
if isinstance(prev.attr_type, EnumAttributeType):
102+
if not isinstance(cur.attr_type, EnumAttributeType):
72103
problems.append(
73104
Problem(
74105
"attribute",
75106
prev.fqn,
76-
f"stability changed from '{prev.stability}' to '{cur.stability}'",
107+
f"type changed from '{prev.attr_type}' to '{cur.attr_type}'",
77108
)
78109
)
79-
80-
if isinstance(prev.attr_type, EnumAttributeType):
81-
if not isinstance(cur.attr_type, EnumAttributeType):
110+
else:
111+
# enum type change inevitably causes some values to be removed
112+
# which will be reported in _check_member method as well.
113+
# keeping this check to provide more detailed error message
114+
if cur.attr_type.enum_type != prev.attr_type.enum_type:
82115
problems.append(
83116
Problem(
84117
"attribute",
85118
prev.fqn,
86-
f"type changed from '{prev.attr_type}' to '{cur.attr_type}'",
119+
f"enum type changed from '{prev.attr_type.enum_type}' to '{cur.attr_type.enum_type}'",
87120
)
88121
)
89-
else:
90-
# enum type change inevitably causes some values to be removed
91-
# which will be reported in _check_member method as well.
92-
# keeping this check to provide more detailed error message
93-
if cur.attr_type.enum_type != prev.attr_type.enum_type:
94-
problems.append(
95-
Problem(
96-
"attribute",
97-
prev.fqn,
98-
f"enum type changed from '{prev.attr_type.enum_type}' to '{cur.attr_type.enum_type}'",
99-
)
100-
)
101-
for member in prev.attr_type.members:
102-
self._check_member(
103-
prev.fqn, member, cur.attr_type.members, problems
104-
)
105-
elif cur.attr_type != prev.attr_type:
106-
problems.append(
107-
Problem(
108-
"attribute",
109-
prev.fqn,
110-
f"type changed from '{prev.attr_type}' to '{cur.attr_type}'",
111-
)
122+
elif cur.attr_type != prev.attr_type:
123+
problems.append(
124+
Problem(
125+
"attribute",
126+
prev.fqn,
127+
f"type changed from '{prev.attr_type}' to '{cur.attr_type}'",
112128
)
129+
)
113130

114131
def _check_member(
115132
self,
@@ -118,8 +135,22 @@ def _check_member(
118135
members: list[EnumMember],
119136
problems: list[Problem],
120137
):
138+
found = False
121139
for member in members:
122140
if prev.member_id == member.member_id:
141+
found = True
142+
if prev.stability != StabilityLevel.STABLE:
143+
# we allow stability and value changes for non-stable members
144+
break
145+
146+
self._check_stability(
147+
prev.stability,
148+
member.stability,
149+
"enum attribute member",
150+
f"{fqn}.{prev.member_id}",
151+
problems,
152+
)
153+
123154
if prev.value != member.value:
124155
member_value = (
125156
f'"{member.value}"'
@@ -133,10 +164,12 @@ def _check_member(
133164
f"value changed from '{prev.value}' to '{member_value}'",
134165
)
135166
)
136-
return
137-
problems.append(
138-
Problem("enum attribute member", f"{fqn}.{prev.member_id}", "was removed")
139-
)
167+
if not found:
168+
problems.append(
169+
Problem(
170+
"enum attribute member", f"{fqn}.{prev.member_id}", "was removed"
171+
)
172+
)
140173

141174
def _check_metric(self, prev: MetricSemanticConvention, problems: list[Problem]):
142175
for cur in self.current_semconv.models.values():
@@ -145,14 +178,13 @@ def _check_metric(self, prev: MetricSemanticConvention, problems: list[Problem])
145178
and cur.metric_name == prev.metric_name
146179
):
147180
if prev.stability == StabilityLevel.STABLE:
148-
if cur.stability != prev.stability:
149-
problems.append(
150-
Problem(
151-
"metric",
152-
prev.metric_name,
153-
f"stability changed from '{prev.stability}' to '{cur.stability}'",
154-
)
155-
)
181+
self._check_stability(
182+
prev.stability,
183+
cur.stability,
184+
"metric",
185+
prev.metric_name,
186+
problems,
187+
)
156188
if cur.unit != prev.unit:
157189
problems.append(
158190
Problem(

semantic-conventions/src/opentelemetry/semconv/templating/markdown/__init__.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,10 @@ def to_markdown_attr(
126126
if isinstance(attribute.attr_type, EnumAttributeType)
127127
else AttributeType.get_instantiated_type(attribute.attr_type)
128128
)
129-
description = self._description_with_badge(attribute) + attribute.brief
129+
description = (
130+
self._description_with_badge(attribute.stability, attribute.deprecated)
131+
+ attribute.brief
132+
)
130133
if attribute.note:
131134
self.render_ctx.add_note(attribute.note)
132135
description += f" [{len(self.render_ctx.notes)}]"
@@ -242,7 +245,10 @@ def to_markdown_metric_table(
242245
"| -------- | --------------- | ----------- | -------------- |\n"
243246
)
244247

245-
description = self._description_with_badge(semconv) + semconv.brief
248+
description = (
249+
self._description_with_badge(semconv.stability, semconv.deprecated)
250+
+ semconv.brief
251+
)
246252
if semconv.note:
247253
self.render_ctx.add_note(semconv.note)
248254
description += f" [{len(self.render_ctx.notes)}]"
@@ -327,7 +333,10 @@ def to_markdown_enum(self, output: io.StringIO):
327333
counter = 1
328334
notes = []
329335
for member in enum.members:
330-
description = member.brief
336+
description = (
337+
self._description_with_badge(member.stability, member.deprecated)
338+
+ member.brief
339+
)
331340
if member.note:
332341
description += f" [{counter}]"
333342
counter += 1
@@ -528,22 +537,18 @@ def _render_group(self, semconv, parameters, output):
528537

529538
output.write("<!-- endsemconv -->")
530539

531-
def _description_with_badge(
532-
self, item: typing.Union[SemanticAttribute | BaseSemanticConvention]
533-
):
540+
def _description_with_badge(self, stability: StabilityLevel, deprecated: str):
534541
description = ""
535-
if item.deprecated and self.options.enable_deprecated:
536-
if "deprecated" in item.deprecated.lower():
537-
description = f"**{item.deprecated}**<br>"
542+
if deprecated and self.options.enable_deprecated:
543+
if "deprecated" in deprecated.lower():
544+
description = f"**{deprecated}**<br>"
538545
else:
539-
deprecated_msg = self.options.deprecated_md_snippet().format(
540-
item.deprecated
541-
)
546+
deprecated_msg = self.options.deprecated_md_snippet().format(deprecated)
542547
description = f"{deprecated_msg}<br>"
543-
elif item.stability == StabilityLevel.STABLE and self.options.enable_stable:
548+
elif stability == StabilityLevel.STABLE and self.options.enable_stable:
544549
description = f"{self.options.stable_md_snippet()}<br>"
545550
elif (
546-
item.stability == StabilityLevel.EXPERIMENTAL
551+
stability == StabilityLevel.EXPERIMENTAL
547552
and self.options.enable_experimental
548553
):
549554
description = f"{self.options.experimental_md_snippet()}<br>"

semantic-conventions/src/tests/data/compat/enum_member_removed/vnext.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ groups:
1010
members:
1111
- id: enum_two
1212
brief: "enum two"
13+
stability: experimental
1314
value: "two"
1415
brief: "third attribute"
1516
note: "third attribute note"
1617
examples: ["two"]
17-
stability: stable
18+
stability: experimental

semantic-conventions/src/tests/data/compat/enum_member_removed/vprev.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ groups:
1010
members:
1111
- id: enum_one
1212
brief: "enum one"
13+
stability: experimental
1314
value: "one"
1415
brief: "third attribute"
1516
note: "third attribute note"
1617
examples: ["one"]
17-
stability: stable
18+
stability: experimental

0 commit comments

Comments
 (0)