Skip to content

Commit 01ba4e0

Browse files
authored
Merge pull request #1 from xnuinside/v0.2.0_release
release 0.2.0 with support for Dataclasses
2 parents cb3c76d + 03f78cc commit 01ba4e0

19 files changed

+871
-43
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ py_models_parser/grammars/__pycache__
55
dist/
66
.pytest_cache/
77
.vscode/
8-
models/
8+
models/
9+
test.py

.isort.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
[settings]
2-
known_third_party = parsimonious
2+
known_third_party = gino,parsimonious,sqlalchemy

CHANGELOG.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
1+
**v0.2.0**
2+
1. Added support for Dataclasses
3+
2. Added parse_from_file method
4+
3. Added correct work with types with comma inside, like: Union[dict, list] or Union[dict, list, tuple, anything]
5+
16
**v0.1.1**
27
1. Added base parser logic & tests for Pydantic, Enums, SQLAlchemy Models, GinoORM models, TortoiseORM models

README.md

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44

55

66
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.
7-
Py-Models-Parser supports now ORM Sqlalchemy, Gino, Tortoise; Pydantic, Python Enum models & in nearest feature I plan to add Dataclasses & pure pyton classes. And next will be added other ORMs models.
7+
Py-Models-Parser supports now ORM Sqlalchemy, Gino, Tortoise; Pydantic, Python Enum models, Dataclasses & in nearest feature I plan to add pure pyton classes. And next will be added other ORMs models.
88

9+
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.
910

10-
Py-Models-Parser written with PEG parser and it's python implementation - parsimonious.
1111
Py-Models-Parser take as input different Python code with Models and provide output in standard form:
1212

1313
```python
@@ -55,7 +55,7 @@ You can parse models from python string:
5555

5656
```python
5757

58-
from py_models_parser.core import parse
58+
from py_models_parser import parse
5959

6060
models_str = """from gino import Gino
6161
@@ -74,6 +74,19 @@ class OrderItems(db.Model):
7474
result = parse(models_str)
7575

7676
```
77+
78+
or just provide the path to file:
79+
80+
```python
81+
82+
from py_models_parser import parse_from_file
83+
84+
85+
file_path = "path/to/your/models.py"
86+
# for example: tests/data/dataclass_defaults.py
87+
result = parse_from_file(file_path)
88+
```
89+
7790
It will produce the result:
7891

7992
```python
@@ -124,10 +137,15 @@ It will produce the result:
124137

125138
1. Parse from file method
126139
2. Add cli
127-
3. Add more tests for supported models (and fix existed not covered cases): Pydantic, Enums, Dataclasses, SQLAlchemy Models, GinoORM models, TortoiseORM models
140+
3. Add more tests for supported models (and fix existed not covered cases): Pydantic, Enums, Dataclasses, SQLAlchemy Models, GinoORM models, TortoiseORM models
128141
4. Add support for pure Python classes
129142
5. Add support for pure SQLAlchemy Core Tables
130143

131144
## Changelog
132-
**v0.1.0**
145+
**v0.2.0**
146+
1. Added support for Dataclasses
147+
2. Added parse_from_file method
148+
3. Added correct work with types with comma inside, like: Union[dict, list] or Union[dict, list, tuple, anything]
149+
150+
**v0.1.1**
133151
1. Added base parser logic & tests for Pydantic, Enums, SQLAlchemy Models, GinoORM models, TortoiseORM models

docs/README.rst

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,29 @@
22
Py-Models-Parser
33
----------------
44

5+
6+
.. image:: https://img.shields.io/pypi/v/py-models-parser
7+
:target: https://img.shields.io/pypi/v/py-models-parser
8+
:alt: badge1
9+
10+
.. image:: https://img.shields.io/pypi/l/py-models-parser
11+
:target: https://img.shields.io/pypi/l/py-models-parser
12+
:alt: badge2
13+
14+
.. image:: https://img.shields.io/pypi/pyversions/py-models-parser
15+
:target: https://img.shields.io/pypi/pyversions/py-models-parser
16+
:alt: badge3
17+
18+
.. image:: https://github.com/xnuinside/py-models-parser/actions/workflows/main.yml/badge.svg
19+
:target: https://github.com/xnuinside/py-models-parser/actions/workflows/main.yml/badge.svg
20+
:alt: workflow
21+
22+
523
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.
6-
Py-Models-Parser supports now ORM Sqlalchemy, Gino, Tortoise; Pydantic, Python Enum models & in nearest feature I plan to add Dataclasses & pure pyton classes. And next will be added other ORMs models.
24+
Py-Models-Parser supports now ORM Sqlalchemy, Gino, Tortoise; Pydantic, Python Enum models, Dataclasses & in nearest feature I plan to add pure pyton classes. And next will be added other ORMs models.
25+
26+
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.
727

8-
Py-Models-Parser written with PEG parser and it's python implementation - parsimonious.
928
Py-Models-Parser take as input different Python code with Models and provide output in standard form:
1029

1130
.. code-block:: python
@@ -55,7 +74,7 @@ You can parse models from python string:
5574
.. code-block:: python
5675
5776
58-
from py_models_parser.core import parse
77+
from py_models_parser import parse
5978
6079
models_str = """from gino import Gino
6180
@@ -73,6 +92,18 @@ You can parse models from python string:
7392
"""
7493
result = parse(models_str)
7594
95+
or just provide the path to file:
96+
97+
.. code-block:: python
98+
99+
100+
from py_models_parser import parse_from_file
101+
102+
103+
file_path = "path/to/your/models.py"
104+
# for example: tests/data/dataclass_defaults.py
105+
result = parse_from_file(file_path)
106+
76107
It will produce the result:
77108

78109
.. code-block:: python
@@ -124,14 +155,21 @@ TODO: in next Release
124155

125156
#. Parse from file method
126157
#. Add cli
127-
#. Add more tests for supported models (and fix existed not covered cases): Pydantic, Enums, Dataclasses, SQLAlchemy Models, GinoORM models, TortoiseORM models
158+
#. Add more tests for supported models (and fix existed not covered cases): Pydantic, Enums, Dataclasses, SQLAlchemy Models, GinoORM models, TortoiseORM models
128159
#. Add support for pure Python classes
129160
#. Add support for pure SQLAlchemy Core Tables
130161

131162
Changelog
132163
---------
133164

134-
**v0.1.0**
165+
**v0.2.0**
166+
167+
168+
#. Added support for Dataclasses
169+
#. Added parse_from_file method
170+
#. Added correct work with types with comma inside, like: Union[dict, list] or Union[dict, list, tuple, anything]
171+
172+
**v0.1.1**
135173

136174

137175
#. Added base parser logic & tests for Pydantic, Enums, SQLAlchemy Models, GinoORM models, TortoiseORM models

py_models_parser/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
__version__ = "0.1.0"
1+
from py_models_parser.core import parse, parse_from_file
2+
3+
__all__ = ["parse", "parse_from_file"]

py_models_parser/core.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
from typing import Dict, List
23

34
from py_models_parser.grammar import grammar
@@ -25,7 +26,7 @@ def get_models_type(models_source: str) -> str:
2526

2627
def pre_processing(models: str):
2728
models = models.split("\n")
28-
start_statements = ["from", "import", "#", '"', "'"]
29+
start_statements = ["from", "import", "#", '"', "'", "@"]
2930
inline_statements = ["Gino", "declarative_base"]
3031
to_process = []
3132
comment_start = True
@@ -62,7 +63,16 @@ def output(input: str):
6263

6364
def parse(models: str) -> List[Dict]:
6465
models = pre_processing(models)
65-
print(models)
6666
result = grammar.parse(models)
6767
result = output(result)
6868
return result
69+
70+
71+
def parse_from_file(file_path: str) -> List[Dict]:
72+
if not os.path.isfile(file_path):
73+
print(
74+
f"Path {file_path} is not a file or not exists. You need to provide valid path to .py module with models"
75+
)
76+
with open(file_path, "r") as f:
77+
models = f.read()
78+
return parse(models)

py_models_parser/grammar.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
r"""
55
expr = (class / call_result / attr_def / emptyline)*
66
class = class_def attr_def* ws?
7-
class_def = intend? class_name args? ":" ws?
7+
class_def = intend? class_name args? ":"* ws?
88
attr_def = intend? id ("=" right_part)* ws?
99
right_part = args / call_result / id / string / text
1010
string = one_quote_str / double_quotes_str
@@ -13,7 +13,7 @@ class = class_def attr_def* ws?
1313
args = "(" (( call_result / args / attr_def / id )* ","* )* ")"
1414
call_result = id args ws?
1515
class_name = "class" id
16-
id = (dot_id / text) ws?
16+
id = (((dot_id / text)+ ","*) * / dot_id / text) ws?
1717
dot_id = (text".")*text
1818
intend = " " / "\t"
1919
text = !class ~"['\_A-Z 0-9\{\}\[\]_\"\-\/\$:<%>\w]*"i

py_models_parser/visitor.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
class Visitor(NodeVisitor):
77
def visit_class_name(self, node, visited_children):
88
"""get class name"""
9-
class_name = node.children[1].children[0].text.strip()
9+
class_name = node.children[1].children[0].text.strip().replace(":", "")
1010
return {"name": class_name}
1111

1212
def visit_class_def(self, node, visited_children):
@@ -58,7 +58,6 @@ def extract_orm_attr(self, text: str):
5858
text = text.split(",")
5959

6060
text = self.clean_up_cases_with_inner_pars(text)
61-
print(text)
6261
if i == "Field":
6362
# for tortoise orm
6463
split_by_field = base_text.split("Field")[0].split(".")
@@ -122,38 +121,45 @@ def visit_attr_def(self, node, visited_children):
122121
attr["attr"]["properties"] = children[-1][-1]["properties"]
123122
if children[-1][-1]["type"] is not None:
124123
attr["attr"]["type"] = children[-1][-1]["type"]
125-
126124
return attr
127125

128-
def process_chld(self, child, final_child, meta):
126+
def process_chld(self, child, final_child):
129127
if "attr" in child and child["attr"]["name"]:
130128
if "tablename" in child["attr"]["name"]:
131129
final_child["properties"]["table_name"] = child["attr"]["default"]
132-
elif "table_args" in child["attr"]["name"] or meta:
130+
elif "table_args" in child["attr"]["name"]:
133131
final_child["properties"][child["attr"]["name"]] = (
134132
child["attr"]["type"] or child["attr"]["default"]
135133
)
136134
else:
137-
if "class Meta" == child["attr"]["name"]:
138-
return final_child, True
139135
final_child["attrs"].append(child["attr"])
140136
else:
141137
if isinstance(child, dict):
142138
final_child.update(child)
143139
elif isinstance(child, list):
144140
for i in child:
145-
final_child, meta = self.process_chld(i, final_child, meta)
146-
return final_child, meta
141+
final_child = self.process_chld(i, final_child)
142+
return final_child
147143

148144
def visit_expr(self, node, visited_children):
149145
"""Makes a dict of the section (as key) and the key/value pairs."""
150146
children_values = []
147+
n = -1
151148
for i in visited_children:
152-
meta = False
153149
final_child = {"name": None, "attrs": [], "parents": [], "properties": {}}
154-
final_child, _ = self.process_chld(i, final_child, meta)
155-
if final_child["name"]:
150+
final_child = self.process_chld(i, final_child)
151+
if (
152+
final_child.get("name")
153+
and final_child["name"] == "Meta"
154+
and children_values
155+
):
156+
for attr in final_child["attrs"]:
157+
children_values[n]["properties"][attr["name"]] = (
158+
attr["type"] or attr["default"]
159+
)
160+
elif final_child.get("name"):
156161
children_values.append(final_child)
162+
n += 1
157163
if "attr" in final_child:
158164
del final_child["attr"]
159165
return children_values

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "py-models-parser"
3-
version = "0.1.0"
3+
version = "0.2.0"
44
description = "Parser for Different Python Models (Pydantic, Enums, ORMs: Tortoise, SqlAlchemy, GinoORM) to extract information about columns(attrs), model, table args,etc in one format."
55
authors = ["Iuliia Volkova <xnuinside@gmail.com>"]
66
license = "MIT"

0 commit comments

Comments
 (0)