pygraphile 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pygraphile-0.2.0 → pygraphile-0.3.0}/PKG-INFO +1 -1
- {pygraphile-0.2.0 → pygraphile-0.3.0}/pyproject.toml +1 -1
- {pygraphile-0.2.0 → pygraphile-0.3.0}/src/pygraphile/__init__.py +1 -4
- pygraphile-0.3.0/src/pygraphile/main.py +66 -0
- {pygraphile-0.2.0 → pygraphile-0.3.0}/src/pygraphile/utils.py +1 -1
- {pygraphile-0.2.0 → pygraphile-0.3.0}/tests/test_basic.py +1 -1
- pygraphile-0.2.0/src/pygraphile/main.py +0 -70
- {pygraphile-0.2.0 → pygraphile-0.3.0}/.gitattributes +0 -0
- {pygraphile-0.2.0 → pygraphile-0.3.0}/.github/workflows/workflow.yml +0 -0
- {pygraphile-0.2.0 → pygraphile-0.3.0}/.gitignore +0 -0
- {pygraphile-0.2.0 → pygraphile-0.3.0}/.python-version +0 -0
- {pygraphile-0.2.0 → pygraphile-0.3.0}/CONTRIBUTING.md +0 -0
- {pygraphile-0.2.0 → pygraphile-0.3.0}/LICENSE +0 -0
- {pygraphile-0.2.0 → pygraphile-0.3.0}/README.md +0 -0
- {pygraphile-0.2.0 → pygraphile-0.3.0}/docs/README.md +0 -0
- {pygraphile-0.2.0 → pygraphile-0.3.0}/examples/README.md +0 -0
- {pygraphile-0.2.0 → pygraphile-0.3.0}/src/pygraphile/py.typed +0 -0
- {pygraphile-0.2.0 → pygraphile-0.3.0}/tests/__init__.py +0 -0
- {pygraphile-0.2.0 → pygraphile-0.3.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pygraphile
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: A Python library inspired by PostGraphile - automatically generates GraphQL APIs from databases. Currently supports SQLite with plans for additional database support.
|
|
5
5
|
Project-URL: Homepage, https://github.com/dshaw0004/pygraphile
|
|
6
6
|
Project-URL: Repository, https://github.com/dshaw0004/pygraphile
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pygraphile"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "A Python library inspired by PostGraphile - automatically generates GraphQL APIs from databases. Currently supports SQLite with plans for additional database support."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import sqlite3
|
|
2
1
|
"""
|
|
3
2
|
PyGraphile - A Python library for automatically generating GraphQL APIs from databases.
|
|
4
3
|
|
|
@@ -10,8 +9,6 @@ Currently supports SQLite with plans for PostgreSQL, MySQL, and other databases.
|
|
|
10
9
|
from .main import PyGraphile
|
|
11
10
|
from .utils import get_schema_from_table_name, generate_query_type, generate_type_defs, SQL_TO_GRAPHQL
|
|
12
11
|
|
|
13
|
-
__version__ = "0.
|
|
12
|
+
__version__ = "0.3.0"
|
|
14
13
|
__author__ = "dshaw0004"
|
|
15
14
|
__all__ = ["__version__", "PyGraphile", "get_schema_from_table_name", "generate_type_defs", "generate_query_type", "SQL_TO_GRAPHQL"]
|
|
16
|
-
|
|
17
|
-
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
|
|
3
|
+
from ariadne import QueryType, make_executable_schema
|
|
4
|
+
from ariadne.asgi import GraphQL
|
|
5
|
+
|
|
6
|
+
from .utils import get_schema_from_table_name, generate_query_type, generate_type_defs
|
|
7
|
+
|
|
8
|
+
__all__ = ["PyGraphile"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PyGraphile:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
db_name: str = 'pygraphile.sqlite',
|
|
15
|
+
db_type: str = 'sqlite3',
|
|
16
|
+
migration_folder: str = 'nomigration',
|
|
17
|
+
debug: bool = False,
|
|
18
|
+
):
|
|
19
|
+
if db_type != 'sqlite3':
|
|
20
|
+
raise ValueError(f"Unsupported database type: '{db_type}'. Currently only 'sqlite3' is supported.")
|
|
21
|
+
|
|
22
|
+
if migration_folder != 'nomigration':
|
|
23
|
+
# TODO: apply migration
|
|
24
|
+
print('need to implement this')
|
|
25
|
+
|
|
26
|
+
self._debug = debug
|
|
27
|
+
self.con = sqlite3.connect(db_name)
|
|
28
|
+
self.con.row_factory = sqlite3.Row
|
|
29
|
+
self.cursor = self.con.cursor()
|
|
30
|
+
|
|
31
|
+
result: list[tuple[str,]] = self.cursor.execute(
|
|
32
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
|
|
33
|
+
).fetchall()
|
|
34
|
+
self.tables = [res[0] for res in result]
|
|
35
|
+
print(f'Following tables detected: {self.tables}')
|
|
36
|
+
|
|
37
|
+
self.table_schemas = {
|
|
38
|
+
table: get_schema_from_table_name(self.cursor, table)
|
|
39
|
+
for table in self.tables
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
self.gql_type_def: str = generate_type_defs(self.table_schemas)
|
|
43
|
+
self.gql_query_types: str = generate_query_type(self.table_schemas)
|
|
44
|
+
|
|
45
|
+
query = QueryType()
|
|
46
|
+
|
|
47
|
+
# dynamically attach resolvers
|
|
48
|
+
for table in self.tables:
|
|
49
|
+
query.set_field(table, self.make_resolver(table))
|
|
50
|
+
|
|
51
|
+
type_defs = self.gql_type_def + "\n" + self.gql_query_types
|
|
52
|
+
self.schema = make_executable_schema(type_defs, query)
|
|
53
|
+
|
|
54
|
+
def get_query_app(self):
|
|
55
|
+
return GraphQL(self.schema, debug=self._debug)
|
|
56
|
+
|
|
57
|
+
def make_resolver(self, table_name: str):
|
|
58
|
+
# Quote the table name to prevent SQL injection
|
|
59
|
+
safe_table = table_name.replace('"', '""')
|
|
60
|
+
|
|
61
|
+
def resolver(_, info, **kwargs):
|
|
62
|
+
sql = f'SELECT * FROM "{safe_table}"'
|
|
63
|
+
rows = self.cursor.execute(sql).fetchall()
|
|
64
|
+
return [dict(row) for row in rows]
|
|
65
|
+
|
|
66
|
+
return resolver
|
|
@@ -29,7 +29,7 @@ def generate_type_defs(tables):
|
|
|
29
29
|
for table_name, columns in tables.items():
|
|
30
30
|
fields = []
|
|
31
31
|
for col in columns:
|
|
32
|
-
gql_type = SQL_TO_GRAPHQL.get(col["type"].upper(), "String")
|
|
32
|
+
gql_type = SQL_TO_GRAPHQL.get((col["type"] or "").upper(), "String")
|
|
33
33
|
not_null = "!" if col["notnull"] else ""
|
|
34
34
|
fields.append(f"{col['name']}: {gql_type}{not_null}")
|
|
35
35
|
type_def = f"type {table_name.capitalize()} {{\n " + \
|
|
@@ -7,7 +7,7 @@ def test_version():
|
|
|
7
7
|
"""Test that version is defined."""
|
|
8
8
|
assert hasattr(pygraphile, "__version__")
|
|
9
9
|
assert isinstance(pygraphile.__version__, str)
|
|
10
|
-
assert pygraphile.__version__ == "0.
|
|
10
|
+
assert pygraphile.__version__ == "0.3.0"
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def test_module_imports():
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import sqlite3
|
|
2
|
-
|
|
3
|
-
from ariadne import QueryType, make_executable_schema, gql
|
|
4
|
-
from ariadne.asgi import GraphQL
|
|
5
|
-
from fastapi import FastAPI
|
|
6
|
-
|
|
7
|
-
from utils import get_schema_from_table_name, generate_query_type, generate_type_defs
|
|
8
|
-
|
|
9
|
-
__all__ = ["PyGraphile"]
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class PyGraphile:
|
|
13
|
-
def __init__(self,
|
|
14
|
-
db_name: str = 'pygraphile.sqlite',
|
|
15
|
-
db_type: str = 'sqlite3',
|
|
16
|
-
migration_folder: str = 'nomigration',
|
|
17
|
-
):
|
|
18
|
-
|
|
19
|
-
if 'sqlite3' != db_type:
|
|
20
|
-
print('this database is not supported yet')
|
|
21
|
-
exit(1)
|
|
22
|
-
|
|
23
|
-
if 'nomigration' != migration_folder:
|
|
24
|
-
# TODO: apply migration
|
|
25
|
-
print('need to implement this')
|
|
26
|
-
|
|
27
|
-
self.con = sqlite3.connect(db_name)
|
|
28
|
-
self.con.row_factory = sqlite3.Row
|
|
29
|
-
self.cursor = self.con.cursor()
|
|
30
|
-
|
|
31
|
-
result: list[tuple[str,]] = self.cursor.execute(
|
|
32
|
-
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").fetchall()
|
|
33
|
-
self.tables = [res[0] for res in result]
|
|
34
|
-
print(f'Following tables detected {self.tables}')
|
|
35
|
-
|
|
36
|
-
self.table_schemas = {table: get_schema_from_table_name(
|
|
37
|
-
self.cursor, table) for table in self.tables}
|
|
38
|
-
|
|
39
|
-
self.gql_type_def: str = generate_type_defs(self.table_schemas)
|
|
40
|
-
self.gql_query_types: str = generate_query_type(self.table_schemas)
|
|
41
|
-
print(self.gql_type_def)
|
|
42
|
-
print(self.gql_query_types)
|
|
43
|
-
|
|
44
|
-
query = QueryType()
|
|
45
|
-
|
|
46
|
-
# dynamically attach resolvers
|
|
47
|
-
for table in self.tables:
|
|
48
|
-
query.set_field(table, self.make_resolver(table))
|
|
49
|
-
|
|
50
|
-
type_defs = self.gql_type_def + "\n" + self.gql_query_types
|
|
51
|
-
self.schema = make_executable_schema(type_defs, query)
|
|
52
|
-
|
|
53
|
-
def get_query_app(self):
|
|
54
|
-
return GraphQL(self.schema, debug=True)
|
|
55
|
-
|
|
56
|
-
def make_resolver(self, table_name: str):
|
|
57
|
-
def resolver(_, info, **kwargs):
|
|
58
|
-
sql = f"SELECT * FROM {table_name} LIMIT 1"
|
|
59
|
-
rows = self.cursor.execute(sql).fetchall()
|
|
60
|
-
return [dict(row) for row in rows]
|
|
61
|
-
return resolver
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
# if __name__ == "__main__":
|
|
65
|
-
# # initialize FastAPI
|
|
66
|
-
# app = FastAPI()
|
|
67
|
-
# # initialize PyGraphile
|
|
68
|
-
# pg = PyGraphile(db_name="pygraphile.sqlite")
|
|
69
|
-
# # mount Ariadne GraphQL app at /graphql
|
|
70
|
-
# app.mount("/graphql", pg.get_query_app())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|