Skip to content

Commit 272d994

Browse files
committed
base support for Pydal table definition
1 parent e982a48 commit 272d994

File tree

3 files changed

+92
-44
lines changed

3 files changed

+92
-44
lines changed

CHANGELOG.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
**v0.5.0**
2+
1. Added base support for Pydal tables definition
3+
14
**v0.4.0**
25
1. return tuples (multiple values) is parsed correctly now
36
2. symbols like `*&^%$#!±~`§<>` now does not cause any errors

README.md

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

33
![badge1](https://img.shields.io/pypi/v/py-models-parser) ![badge2](https://img.shields.io/pypi/l/py-models-parser) ![badge3](https://img.shields.io/pypi/pyversions/py-models-parser) ![workflow](https://github.com/xnuinside/py-models-parser/actions/workflows/main.yml/badge.svg)
44

5-
65
It's as second Parser that done by me, first is a https://github.com/xnuinside/simple-ddl-parser for SQL DDL with different dialects.
76

8-
Py-Models-Parser can parse & extract information from models:
7+
Py-Models-Parser can parse & extract information from models & table definitions:
98

10-
- Sqlalchemy ORM,
11-
- Gino ORM,
12-
- Tortoise ORM,
13-
- Django ORM Model,
14-
- Pydantic,
15-
- Python Enum,
16-
- Pony ORM,
17-
- Python Dataclasses
18-
- pure Python Classes
9+
- Sqlalchemy ORM (https://docs.sqlalchemy.org/en/14/orm/),
10+
- Gino ORM (https://python-gino.org/),
11+
- Tortoise ORM (https://tortoise-orm.readthedocs.io/en/latest/),
12+
- Django ORM Model (https://docs.djangoproject.com/en/3.2/topics/db/queries/),
13+
- Pydantic (https://pydantic-docs.helpmanual.io/),
14+
- Python Enum (https://docs.python.org/3/library/enum.html),
15+
- Pony ORM (https://ponyorm.org/),
16+
- Pydal Tables definitions (http://www.web2py.com/books/default/chapter/29/06/the-database-abstraction-layer#The-DAL-A-quick-tour),
17+
- Python Dataclasses (https://docs.python.org/3/library/dataclasses.html),
18+
- pure Python Classes (https://docs.python.org/3/tutorial/classes.html#class-objects)
1919

20-
Number of supported models will be increased, check 'TODO' section.
20+
Number of supported models will be increased, check 'TODO' section, if you want to have support of different models types - please open the issue.
2121

2222
Py-Models-Parser written with PEG parser and it's python implementation - parsimonious. It's pretty new and I did not cover all possible test cases, so if you will have an issue - please just open an issue in this case with example, I will fix it as soon as possible.
2323

@@ -171,10 +171,10 @@ For model from point 1 (above) library will produce the result:
171171

172172
## TODO: in next Release
173173

174-
1. Add more tests for supported models (and fix existed not covered cases): Django ORM, Pydantic, Enums, Dataclasses, SQLAlchemy Models, GinoORM models, TortoiseORM models
174+
1. Add more tests for supported models (and fix existed not covered cases): Django ORM, Pydantic, Enums, Dataclasses, SQLAlchemy Models, GinoORM models, TortoiseORM models, PonyORM, Pydal tables definitions
175175
2. Add support for SQLAlchemy Core Tables
176-
3. Add support for Pony ORM models
177-
4. Add support for Piccolo ORM models
176+
3. Add support for Piccolo ORM models
177+
178178

179179
## Changelog
180180
**v0.4.0**

py_models_parser/visitor.py

Lines changed: 74 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Dict, Tuple
1+
from typing import Dict, List, Tuple, Union
22

33
from parsimonious.nodes import NodeVisitor
44

@@ -47,10 +47,10 @@ def extract_orm_attr(self, text: str):
4747
default = None
4848
not_orm = True
4949
properties = {}
50-
orm_columns = ["Column", "Field", "relationship", "ForeignKey"]
50+
orm_triggers = ["Column", "Field", "relationship", "ForeignKey"]
5151
pony_orm_fields = ["Required", "Set", "Optional", "PrimaryKey"]
52-
orm_columns.extend(pony_orm_fields)
53-
for i in orm_columns:
52+
orm_triggers.extend(pony_orm_fields)
53+
for i in orm_triggers:
5454
if i in text:
5555
not_orm = False
5656
# in case of models
@@ -112,6 +112,50 @@ def extractor(self, text: str) -> Dict:
112112
def visit_right_part(self, node, visited_children):
113113
return self.extractor(node.text.strip())
114114

115+
def visit_call_result(self, node, visited_children):
116+
value = node.text.strip()
117+
if "define_table" in value:
118+
# mean this is a pydal method
119+
return self.process_pydal_table_definition(value)
120+
return visited_children or node
121+
122+
@staticmethod
123+
def process_pydal_table_definition(pydal_def: str) -> Dict:
124+
table_def = {
125+
"attrs": [],
126+
"name": "name",
127+
"properties": {},
128+
}
129+
pydal_def = pydal_def.split("Field(")
130+
table_def["name"] = pydal_def[0].split(",")[0].split("define_table(")[1]
131+
for column in pydal_def[1:]:
132+
column = column.replace(")", "").strip().split(",")
133+
column_name = column[0]
134+
default = None
135+
_type = None
136+
properties = {}
137+
for num, param in enumerate(column[1:]):
138+
param = param.strip().split("=")
139+
if len(param) > 1:
140+
if "default" == param[0]:
141+
default = param[1]
142+
elif "type" == param[0]:
143+
_type = param[1]
144+
else:
145+
properties[param[0]] = param[1]
146+
else:
147+
if num == 0 and param[0]:
148+
_type = param[0]
149+
table_def["attrs"].append(
150+
{
151+
"name": column_name,
152+
"default": default,
153+
"type": _type,
154+
"properties": properties,
155+
}
156+
)
157+
return table_def
158+
115159
def visit_attr_def(self, node, visited_children):
116160
"""Makes a dict of the section (as key) and the key/value pairs."""
117161
left = node.children[1].children[0].children[0].text.strip()
@@ -137,33 +181,34 @@ def visit_attr_def(self, node, visited_children):
137181
attr["attr"]["type"] = children[-1]["type"]
138182
return attr
139183

140-
def process_chld(self, child, final_child):
141-
if "attr" in child and child["attr"]["name"]:
142-
# todo: this is a hack, need refactor it
143-
if child["attr"]["name"] == "self" and not final_child["properties"].get(
144-
"init"
145-
):
146-
final_child["properties"]["init"] = []
147-
elif "tablename" in child["attr"]["name"]:
148-
final_child["properties"]["table_name"] = child["attr"]["default"]
149-
elif "table_args" in child["attr"]["name"]:
150-
final_child["properties"][child["attr"]["name"]] = (
151-
child["attr"]["type"] or child["attr"]["default"]
152-
)
153-
else:
154-
if final_child["properties"].get("init") is not None:
155-
final_child["properties"]["init"].append(child["attr"])
156-
else:
157-
final_child["attrs"].append(child["attr"])
184+
@staticmethod
185+
def _process_attr(attr: Dict, final_item: Dict) -> Dict:
186+
# todo: this is a hack, need refactor it
187+
if attr["name"] == "self" and not final_item["properties"].get("init"):
188+
final_item["properties"]["init"] = []
189+
elif "tablename" in attr["name"]:
190+
final_item["properties"]["table_name"] = attr["default"]
191+
elif "table_args" in attr["name"]:
192+
final_item["properties"][attr["name"]] = attr["type"] or attr["default"]
158193
else:
194+
if final_item["properties"].get("init") is not None:
195+
final_item["properties"]["init"].append(attr)
196+
else:
197+
final_item["attrs"].append(attr)
198+
return final_item
159199

160-
if "attr" in child:
161-
final_child = process_no_name_attrs(final_child, child)
162-
elif isinstance(child, dict):
163-
final_child.update(child)
164-
elif isinstance(child, list):
165-
for i in child:
166-
final_child = self.process_chld(i, final_child)
200+
def process_chld(self, child: Union[Dict, List], final_child: Dict) -> Dict:
201+
if child:
202+
if "attr" in child and child["attr"].get("name"):
203+
final_child = self._process_attr(child["attr"], final_child)
204+
else:
205+
if "attr" in child:
206+
final_child = process_no_name_attrs(final_child, child)
207+
elif isinstance(child, dict):
208+
final_child.update(child)
209+
elif isinstance(child, list):
210+
for i in child:
211+
final_child = self.process_chld(i, final_child)
167212
return final_child
168213

169214
def visit_expr(self, node, visited_children):

0 commit comments

Comments
 (0)