Skip to content

Commit 756ddb8

Browse files
Substitute abstract classes with type aliases
1 parent e18e891 commit 756ddb8

File tree

4 files changed

+39
-61
lines changed

4 files changed

+39
-61
lines changed

schema_salad/metaschema.py

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
from urllib.parse import quote, urldefrag, urlparse, urlsplit, urlunsplit
2525
from urllib.request import pathname2url
2626

27-
from mypy_extensions import trait
2827
from rdflib import Graph
2928
from rdflib.plugins.parsers.notation3 import BadSyntax
3029
from ruamel.yaml.comments import CommentedMap
@@ -214,7 +213,6 @@ def graph(self) -> Graph:
214213
return graph
215214

216215

217-
@trait
218216
class Saveable(metaclass=ABCMeta):
219217
"""Mark classes than have a save() and fromDoc() function."""
220218

@@ -1187,12 +1185,7 @@ def parser_info() -> str:
11871185
return "org.w3id.cwl.salad"
11881186

11891187

1190-
@trait
1191-
class Documented(Saveable, metaclass=ABCMeta):
1192-
doc: None | Sequence[str] | str
1193-
1194-
1195-
class RecordField(Documented):
1188+
class RecordField(Saveable):
11961189
"""
11971190
A field of a record.
11981191
"""
@@ -3480,35 +3473,11 @@ def __init__(
34803473
attrs: ClassVar[Collection[str]] = frozenset(["specializeFrom", "specializeTo"])
34813474

34823475

3483-
@trait
3484-
class NamedType(Saveable, metaclass=ABCMeta):
3485-
name: str
3486-
inVocab: None | bool
3487-
3488-
3489-
@trait
3490-
class DocType(Documented, metaclass=ABCMeta):
3491-
doc: None | Sequence[str] | str
3492-
docParent: None | str
3493-
docChild: None | Sequence[str] | str
3494-
docAfter: None | str
3495-
3496-
3497-
@trait
3498-
class SchemaDefinedType(DocType, metaclass=ABCMeta):
34993476
"""
35003477
Abstract base for schema-defined types.
35013478
35023479
"""
35033480

3504-
doc: None | Sequence[str] | str
3505-
docParent: None | str
3506-
docChild: None | Sequence[str] | str
3507-
docAfter: None | str
3508-
jsonldPredicate: JsonldPredicate | None | str
3509-
documentRoot: None | bool
3510-
3511-
35123481
class SaladRecordField(RecordField):
35133482
"""
35143483
A field of a record.
@@ -3898,7 +3867,7 @@ def __init__(
38983867
)
38993868

39003869

3901-
class SaladRecordSchema(NamedType, RecordSchema, SchemaDefinedType):
3870+
class SaladRecordSchema(RecordSchema):
39023871
name: str
39033872

39043873
def __eq__(self, other: Any) -> bool:
@@ -4756,7 +4725,7 @@ def __init__(
47564725
)
47574726

47584727

4759-
class SaladEnumSchema(NamedType, EnumSchema, SchemaDefinedType):
4728+
class SaladEnumSchema(EnumSchema):
47604729
"""
47614730
Define an enumerated type.
47624731
@@ -5499,7 +5468,7 @@ def __init__(
54995468
)
55005469

55015470

5502-
class SaladMapSchema(NamedType, MapSchema, SchemaDefinedType):
5471+
class SaladMapSchema(MapSchema):
55035472
"""
55045473
Define a map type.
55055474
@@ -6186,7 +6155,7 @@ def __init__(
61866155
)
61876156

61886157

6189-
class SaladUnionSchema(NamedType, UnionSchema, DocType):
6158+
class SaladUnionSchema(UnionSchema):
61906159
"""
61916160
Define a union type.
61926161
@@ -6813,7 +6782,7 @@ def __init__(
68136782
)
68146783

68156784

6816-
class Documentation(NamedType, DocType):
6785+
class Documentation(Saveable):
68176786
"""
68186787
A documentation section. This type exists to facilitate self-documenting
68196788
schemas but has no role in formal validation.
@@ -7621,6 +7590,16 @@ def __init__(
76217590
)
76227591
)
76237592

7593+
SchemaDefinedType: TypeAlias = SaladEnumSchema | SaladMapSchema | SaladRecordSchema
7594+
NamedType: TypeAlias = (
7595+
Documentation
7596+
| SaladEnumSchema
7597+
| SaladMapSchema
7598+
| SaladRecordSchema
7599+
| SaladUnionSchema
7600+
)
7601+
DocType: TypeAlias = Documentation | SaladUnionSchema | SchemaDefinedType
7602+
Documented: TypeAlias = DocType | RecordField
76247603

76257604
def load_document(
76267605
doc: Any,

schema_salad/python_codegen.py

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import textwrap
44
from collections.abc import MutableSequence
5+
from graphlib import TopologicalSorter
56
from io import StringIO
67
from typing import IO, Any, Final
78

@@ -91,6 +92,7 @@ def __init__(
9192
self.copyright: Final = copyright
9293
self.parser_info: Final = parser_info
9394
self.salad_version: Final = salad_version
95+
self.subclasses: dict[str, list[str]] = {}
9496

9597
@staticmethod
9698
def safe_name(name: str) -> str:
@@ -148,18 +150,19 @@ def begin_class(
148150
self.current_fieldtypes: dict[str, TypeDef] = {}
149151
self.current_class_is_abstract = abstract
150152

151-
if extends:
152-
ext = ", ".join(self.safe_name(e) for e in extends)
153-
else:
154-
ext = "Saveable"
155-
156153
if self.current_class_is_abstract:
157-
ext += ", metaclass=ABCMeta"
158-
decorator = "@trait\n"
154+
self.subclasses[classname] = []
159155
else:
160-
decorator = ""
156+
parents = [
157+
self.safe_name(e) for e in extends if self.safe_name(e) not in self.subclasses
158+
]
159+
ext = ", ".join(parents) if parents else "Saveable"
160+
self.out.write(fmt(f"class {classname}({ext}):\n pass", 0)[:-9])
161+
162+
for ext in extends:
163+
if (safe_ext := self.safe_name(ext)) in self.subclasses:
164+
self.subclasses[safe_ext].append(classname)
161165

162-
self.out.write(fmt(f"{decorator}class {classname}({ext}):\n pass", 0)[:-9])
163166
# make a valid class for Black, but then trim off the "pass"
164167

165168
if doc:
@@ -241,15 +244,6 @@ def save(
241244
def end_class(self, classname: str, field_names: list[str]) -> None:
242245
"""Signal that we are done with this class."""
243246
if self.current_class_is_abstract:
244-
if field_names:
245-
for name in field_names:
246-
safename = self.safe_name(name)
247-
self.out.write(
248-
f" {safename}: {self.current_fieldtypes[safename].instance_type}\n"
249-
)
250-
self.out.write("\n\n")
251-
else:
252-
self.out.write(" pass\n\n\n")
253247
return
254248

255249
self.out.write(
@@ -341,9 +335,7 @@ def end_class(self, classname: str, field_names: list[str]) -> None:
341335
for name in field_names:
342336
if name == "class":
343337
field_inits += """ self.class_: Final[str] = "{}"
344-
""".format(
345-
self.safe_name(classname)
346-
)
338+
""".format(self.safe_name(classname))
347339
elif name == idfield_safe_name and name in optional_field_names:
348340
field_inits += (
349341
" self.{0}: str = {0} if {0} is not None else "
@@ -834,6 +826,15 @@ def epilogue(self, root_loader: TypeDef) -> None:
834826
self.out.write(fmt(f"{lazy_init.instance_type}", 0))
835827
self.out.write("\n")
836828

829+
for class_ in TopologicalSorter(self.subclasses).static_order():
830+
if self.subclasses.get(class_) is not None:
831+
self.out.write(
832+
fmt(
833+
f"{class_}: TypeAlias = {' | '.join(sorted(self.subclasses[class_]))}",
834+
0,
835+
)
836+
)
837+
837838
self.out.write(
838839
"""
839840
def load_document(

schema_salad/python_codegen_support.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
from urllib.parse import quote, urldefrag, urlparse, urlsplit, urlunsplit
2222
from urllib.request import pathname2url
2323

24-
from mypy_extensions import trait
2524
from rdflib import Graph
2625
from rdflib.plugins.parsers.notation3 import BadSyntax
2726
from ruamel.yaml.comments import CommentedMap
@@ -211,7 +210,6 @@ def graph(self) -> Graph:
211210
return graph
212211

213212

214-
@trait
215213
class Saveable(metaclass=ABCMeta):
216214
"""Mark classes than have a save() and fromDoc() function."""
217215

schema_salad/tests/test_python_codegen.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def test_cwl_gen(tmp_path: Path) -> None:
2828
python_codegen(cwl_file_uri, src_target)
2929
assert os.path.exists(src_target)
3030
with open(src_target) as f:
31-
assert "class Workflow(Process)" in f.read()
31+
assert "class Workflow(Saveable)" in f.read()
3232

3333

3434
def test_meta_schema_gen(tmp_path: Path) -> None:

0 commit comments

Comments
 (0)