-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathndb2sqlalchemy_migrator.py
166 lines (140 loc) · 4.98 KB
/
ndb2sqlalchemy_migrator.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
"""
Create migration scripts from NDB to SQLalchemy
"""
from ast import ClassDef, parse
from collections import deque
from functools import partial
from itertools import filterfalse
from operator import attrgetter, contains
from os import listdir, path
import cdd.sqlalchemy.parse
from cdd.shared.pure_utils import rpartial
from cdd.shared.source_transformer import to_code
from cdd_gae.ndb_sqlalchemy_migrator_utils import generate_ndb_to_sqlalchemy_mod
def generate_migration_file(
ndb_class_def,
sqlalchemy_class_def,
ndb_mod_to_import,
sqlalchemy_mod_to_import,
output_file,
):
"""
Generate migration file from NDB to SQLalchemy
:param ndb_class_def: NDB class
:type ndb_class_def: ```ClassDef```
:param sqlalchemy_class_def: SQLalchemy class
:type sqlalchemy_class_def: ```ClassDef```
:param ndb_mod_to_import: NDB module name that the entity will be imported from
:type ndb_mod_to_import: ```str```
:param sqlalchemy_mod_to_import: SQLalchemy module name that the entity will be imported from
:type sqlalchemy_mod_to_import: ```str```
:param output_file: Output file
:type output_file: ```str```
"""
mod = generate_ndb_to_sqlalchemy_mod(
name=ndb_class_def.name,
fields=cdd.sqlalchemy.parse.sqlalchemy(sqlalchemy_class_def)["params"].keys(),
ndb_mod_to_import=ndb_mod_to_import,
sqlalchemy_mod_to_import=sqlalchemy_mod_to_import,
)
with open(output_file, "wt") as f:
f.write(to_code(mod))
def ndb2sqlalchemy_migrator_folder(
ndb_file,
sqlalchemy_file,
ndb_mod_to_import,
sqlalchemy_mod_to_import,
output_folder,
dry_run=False,
):
"""
Create migration scripts from NDB to SQLalchemy
:param ndb_file: Python file containing the NDB `class`es
:type ndb_file: ```str```
:param sqlalchemy_file: Python file containing the Sqlalchemy `class`es
:type sqlalchemy_file: ```str```
:param ndb_mod_to_import: NDB module name that the entity will be imported from
:type ndb_mod_to_import: ```str```
:param sqlalchemy_mod_to_import: SQLalchemy module name that the entity will be imported from
:type sqlalchemy_mod_to_import: ```str```
:param output_folder: Empty folder to generate scripts that migrate from one NDB class to one SQLalchemy class
:type output_folder: ```str```
:param dry_run: Show what would be created; don't actually write to the filesystem
:type dry_run: ```bool```
"""
assert (
path.isdir(output_folder) and len(listdir(output_folder)) == 0
), "{!r} must be empty and existent".format(output_folder)
for f in ndb_file, sqlalchemy_file:
assert path.isfile(f), "FileNotFound({!r})".format(f)
if dry_run:
print(
"ndb2sqlalchemy_migrator_folder:",
{
"ndb_file": ndb_file,
"sqlalchemy_file": sqlalchemy_file,
"output_folder": output_folder,
"dry_run": dry_run,
},
)
return
with open(sqlalchemy_file, "rt") as f:
sqlalchemy_mod = parse(f.read())
with open(ndb_file, "rt") as f:
ndb_mod = parse(f.read())
sqlalchemy_class_defs = dict(
map(
lambda cls_def: (cls_def.name, cls_def),
filter(rpartial(isinstance, ClassDef), sqlalchemy_mod.body),
)
)
entities = frozenset(map(attrgetter("name"), sqlalchemy_class_defs.values()))
ndb_class_defs = dict(
map(
lambda cls_def: (cls_def.name, cls_def),
filter(
lambda cls_def: cls_def.name in entities,
filter(rpartial(isinstance, ClassDef), ndb_mod.body),
),
)
)
len_ndb_class_defs = len(ndb_class_defs)
len_sqlalchemy_class_defs = len(sqlalchemy_class_defs)
assert (
len_ndb_class_defs == len_sqlalchemy_class_defs
), "{} found SQLalchemy models != {} found NDB models, missing: {}".format(
len_ndb_class_defs,
len_sqlalchemy_class_defs,
frozenset(ndb_class_defs.keys()) ^ frozenset(sqlalchemy_class_defs.keys()),
)
deque(
map(
lambda entity: generate_migration_file(
ndb_class_defs[entity],
sqlalchemy_class_defs[entity],
ndb_mod_to_import,
sqlalchemy_mod_to_import,
output_file=path.join(
output_folder,
"{entity}{extsep}py".format(entity=entity, extsep=path.extsep),
),
),
filterfalse(
partial(
contains,
frozenset(ndb_class_defs.keys())
^ frozenset(sqlalchemy_class_defs.keys()),
),
entities,
),
),
maxlen=0,
)
open(
path.join(
output_folder,
"__init__{extsep}py".format(extsep=path.extsep),
),
"a",
).close()
__all__ = ["ndb2sqlalchemy_migrator_folder"]