Skip to content

Commit 9dc45bf

Browse files
committed
adding pydantic generation and jsonld generation to release
1 parent 459d530 commit 9dc45bf

File tree

2 files changed

+123
-1
lines changed

2 files changed

+123
-1
lines changed

.github/workflows/validate_and_release.yml

+25-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ on:
66
push:
77
branches:
88
- main
9+
- ga_rel_generate
910
pull_request:
1011
branches: ['*']
1112
# Allow to trigger the generation of release files automatically
@@ -36,6 +37,28 @@ jobs:
3637
python scripts/jsonParser.py
3738
reproschema validate examples
3839
40+
generate other formats:
41+
runs-on: ubuntu-latest
42+
steps:
43+
- uses: actions/checkout@v4
44+
- name: Set up Python
45+
uses: actions/setup-python@v5
46+
with:
47+
python-version: 3.12
48+
- name: Install dependencies
49+
run: |
50+
python -m pip install --upgrade pip setuptools
51+
python install linkml astor pre-commit
52+
- name: generate pydantic and fixing it
53+
run: |
54+
gen-pydantic --pydantic-version 2 linkml-schema/reproschema.yaml > reproschema_model_autogen.py
55+
python scripts/fix_pydantic.py reproschema_model_autogen.py reproschema_model.py
56+
pre-commit run --files reproschema_model.py
57+
- name: generate jsonld
58+
run: |
59+
gen-jsonld --context contexts/reproschema linkml-schema/reproschema.yaml > reproschema.jsonld
60+
61+
3962
release:
4063
needs: [validate]
4164
if: github.event_name == 'workflow_dispatch'
@@ -51,7 +74,8 @@ jobs:
5174
run: |
5275
echo "Making a release ${{ inputs.version }}"
5376
mkdir releases/${{ inputs.version }}
54-
cp contexts/reproschema releases/${{ inputs.version }}/base
77+
cp contexts/reproschema releases/${{ inputs.version }}/reproschema
78+
cp reproschema_model.py releases/${{ inputs.version }}/reproschema_model.py
5579
# python scripts/makeRelease.py ${{ inputs.version }}
5680

5781
- name: Open pull requests to add files

scripts/fix_pydantic.py

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
""" Using ast transformer to fix some issues with automatic pydantic generation"""
2+
3+
import sys
4+
import ast
5+
import astor
6+
7+
8+
class ClassRemover(ast.NodeTransformer):
9+
def __init__(self, class_name):
10+
self.class_name = class_name
11+
12+
def visit_ClassDef(self, node):
13+
# Remove the class if its name matches the class_to_remove
14+
if node.name == self.class_name:
15+
return None
16+
return node
17+
18+
def visit_Expr(self, node):
19+
# Check if the node is a call expression
20+
if isinstance(node.value, ast.Call):
21+
# Check if the call expression is an attribute (method call)
22+
if isinstance(node.value.func, ast.Attribute):
23+
# Check if the method call matches the specified class and method name
24+
if (
25+
isinstance(node.value.func.value, ast.Name)
26+
and node.value.func.value.id == self.class_name
27+
):
28+
return None # Remove this node
29+
return self.generic_visit(node)
30+
31+
32+
class TypeReplacer(ast.NodeTransformer):
33+
def __init__(self, old_type, new_type):
34+
self.old_type = old_type
35+
self.new_type = new_type
36+
37+
def visit_FunctionDef(self, node):
38+
# Check all arguments in the function definition
39+
for arg in node.args.args:
40+
if arg.annotation:
41+
arg.annotation = self.visit(arg.annotation)
42+
return self.generic_visit(node)
43+
44+
def visit_AsyncFunctionDef(self, node):
45+
# Handle async function definitions similarly
46+
for arg in node.args.args:
47+
if arg.annotation:
48+
arg.annotation = self.visit(arg.annotation)
49+
return self.generic_visit(node)
50+
51+
def visit_Name(self, node):
52+
# Replace the old type with the new type
53+
if node.id == self.old_type:
54+
node.id = self.new_type
55+
return node
56+
57+
def visit_Subscript(self, node):
58+
# Handle Union, Optional, and other subscripted types
59+
node.value = self.visit(node.value)
60+
node.slice = self.visit(node.slice)
61+
return node
62+
63+
def visit_Index(self, node):
64+
# Handle the index part of subscripted types
65+
node.value = self.visit(node.value)
66+
return node
67+
68+
def visit_Tuple(self, node):
69+
# Handle tuples in type annotations
70+
node.elts = [self.visit(elt) for elt in node.elts]
71+
return node
72+
73+
74+
def edit_pydantic(input_file, output_file):
75+
76+
with open(input_file, "r") as file:
77+
tree = ast.parse(file.read())
78+
79+
transformer_class = ClassRemover(class_name="LangString")
80+
tree_modclass = transformer_class.visit(tree)
81+
82+
transformer_tp = TypeReplacer(old_type="LangString", new_type="Dict[str, str]")
83+
tree_modclass_modtype = transformer_tp.visit(tree_modclass)
84+
85+
with open(output_file, "w") as file:
86+
file.write(astor.to_source(tree_modclass_modtype))
87+
88+
89+
if __name__ == "__main__":
90+
input_file = sys.argv[1]
91+
if len(sys.argv) < 3:
92+
output_file = input_file
93+
else:
94+
output_file = sys.argv[2]
95+
print(
96+
f"Fixing automatically generated pydantic file {input_file} and saving to {output_file}"
97+
)
98+
edit_pydantic(input_file, output_file)

0 commit comments

Comments
 (0)