sqlcompose 0.0.0__py3-none-any.whl → 0.0.2__py3-none-any.whl
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.
- sqlcompose/__init__.py +5 -1
- sqlcompose/__main__.py +9 -26
- sqlcompose/core/app.py +22 -0
- sqlcompose/core/circular_dependency_error.py +2 -0
- sqlcompose/core/functions.py +41 -37
- sqlcompose/core/include.py +1 -1
- sqlcompose/py.typed +0 -0
- {sqlcompose-0.0.0.dist-info → sqlcompose-0.0.2.dist-info}/METADATA +36 -12
- sqlcompose-0.0.2.dist-info/RECORD +14 -0
- {sqlcompose-0.0.0.dist-info → sqlcompose-0.0.2.dist-info}/WHEEL +2 -1
- sqlcompose-0.0.2.dist-info/entry_points.txt +2 -0
- sqlcompose-0.0.2.dist-info/top_level.txt +1 -0
- sqlcompose-0.0.0.dist-info/RECORD +0 -10
- sqlcompose-0.0.0.dist-info/entry_points.txt +0 -3
- {sqlcompose-0.0.0.dist-info → sqlcompose-0.0.2.dist-info/licenses}/LICENSE +0 -0
sqlcompose/__init__.py
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
from sqlcompose.core.functions import load, loads
|
|
2
|
+
from sqlcompose.core.circular_dependency_error import CircularDependencyError
|
|
2
3
|
|
|
3
4
|
__all__ = [
|
|
4
5
|
'load',
|
|
5
6
|
'loads',
|
|
6
|
-
|
|
7
|
+
'CircularDependencyError',
|
|
8
|
+
]
|
|
9
|
+
__package_name__ = "sqlcompose"
|
|
10
|
+
__version__ = "0.0.2"
|
sqlcompose/__main__.py
CHANGED
|
@@ -1,30 +1,13 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
3
|
-
from argparse import ArgumentParser
|
|
1
|
+
from sys import argv, exit, stderr, stdout
|
|
2
|
+
from sqlcompose.core.app import app
|
|
4
3
|
|
|
5
|
-
from sqlcompose.core.functions import load, loads
|
|
6
4
|
|
|
5
|
+
if __name__ == "__main__":
|
|
6
|
+
result, code = app(argv[1:])
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
pargs = parser.parse_args()
|
|
13
|
-
|
|
14
|
-
if path.isfile(pargs.input):
|
|
15
|
-
sql = load(pargs.input)
|
|
16
|
-
else:
|
|
17
|
-
sql = loads(pargs.input)
|
|
18
|
-
|
|
19
|
-
print(sql)
|
|
20
|
-
|
|
21
|
-
return
|
|
22
|
-
except SystemExit:
|
|
23
|
-
raise # let argparse print error(s)
|
|
24
|
-
except Exception as ex:
|
|
25
|
-
print(f"Unexpected error: {ex}")
|
|
26
|
-
exit(1)
|
|
27
|
-
|
|
8
|
+
if code == 0:
|
|
9
|
+
stdout.write(result)
|
|
10
|
+
else:
|
|
11
|
+
stderr.write(result)
|
|
28
12
|
|
|
29
|
-
|
|
30
|
-
main()
|
|
13
|
+
exit(code)
|
sqlcompose/core/app.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from os import path
|
|
2
|
+
from argparse import ArgumentParser
|
|
3
|
+
|
|
4
|
+
from sqlcompose.core.functions import load, loads
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def app(args: list[str]) -> tuple[str, int ]:
|
|
8
|
+
try:
|
|
9
|
+
parser = ArgumentParser(prog = "sqlcompose")
|
|
10
|
+
parser.add_argument("input", type=str, help = "SQL expression or location of an SQL file")
|
|
11
|
+
pargs = parser.parse_args(args)
|
|
12
|
+
|
|
13
|
+
if path.isfile(pargs.input):
|
|
14
|
+
sql = load(pargs.input)
|
|
15
|
+
else:
|
|
16
|
+
sql = loads(pargs.input)
|
|
17
|
+
|
|
18
|
+
return sql, 0
|
|
19
|
+
except SystemExit as ex:
|
|
20
|
+
return str(ex), 2
|
|
21
|
+
except Exception as ex: # pragma: no cover
|
|
22
|
+
return f"Unexpected error: {ex}", 1
|
sqlcompose/core/functions.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from os import path
|
|
2
|
-
from typing import Sequence
|
|
2
|
+
from typing import Sequence
|
|
3
3
|
from os import path
|
|
4
4
|
from re import compile, sub, escape, IGNORECASE
|
|
5
5
|
from textwrap import indent
|
|
6
6
|
|
|
7
|
+
from sqlcompose.core.circular_dependency_error import CircularDependencyError
|
|
7
8
|
from sqlcompose.core.include import Include
|
|
8
9
|
from sqlcompose.core.compat import fix_path, get_relative_path
|
|
9
10
|
|
|
@@ -19,7 +20,7 @@ def loads(sql: str) -> str:
|
|
|
19
20
|
Returns:
|
|
20
21
|
str: The composed query
|
|
21
22
|
"""
|
|
22
|
-
return
|
|
23
|
+
return compose(sql, "SQL", path.curdir, path.curdir)
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
def load(filename: str) -> str:
|
|
@@ -40,7 +41,7 @@ def load(filename: str) -> str:
|
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
with open(filename, "r", encoding="utf-8") as file:
|
|
43
|
-
return
|
|
44
|
+
return compose(file.read(), filename, filename, path.dirname(filename))
|
|
44
45
|
|
|
45
46
|
|
|
46
47
|
def compose(
|
|
@@ -49,52 +50,55 @@ def compose(
|
|
|
49
50
|
file_path: str,
|
|
50
51
|
root: str,
|
|
51
52
|
level: int = 1,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
) -> str | None:
|
|
53
|
+
stack: list[str] | None = None
|
|
54
|
+
) -> str:
|
|
55
55
|
|
|
56
56
|
file_path = fix_path(file_path)
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
stack = stack or []
|
|
58
|
+
parent = stack[-1] if stack else None
|
|
59
|
+
name = get_relative_path(name, root)
|
|
60
|
+
index = 1
|
|
61
|
+
included: list[str] = []
|
|
59
62
|
includes: list[Include] = []
|
|
60
|
-
|
|
63
|
+
stack.append(name)
|
|
61
64
|
|
|
62
|
-
if
|
|
63
|
-
|
|
64
|
-
return None
|
|
65
|
-
elif name in included:
|
|
66
|
-
raise Exception(f"Circular dependency detected: File \"{get_relative_path(file_path, root)}\" has already been already included")
|
|
67
|
-
else:
|
|
68
|
-
included.append(name)
|
|
69
|
-
included1[name] = (file_path, level)
|
|
70
|
-
|
|
71
|
-
index = 1
|
|
65
|
+
if len(stack) > 1 and name in (stack[1:-1]):
|
|
66
|
+
raise CircularDependencyError
|
|
72
67
|
|
|
73
68
|
for match in REGEX_INCLUDE.finditer(sql):
|
|
74
69
|
file_path_inner = fix_path(path.join(path.dirname(file_path), match.group(1)))
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
70
|
+
if file_path_inner not in included:
|
|
71
|
+
included.append(file_path_inner)
|
|
72
|
+
try:
|
|
73
|
+
with open(file_path_inner, "r", encoding="utf-8") as file:
|
|
74
|
+
composed = compose(
|
|
75
|
+
file.read(),
|
|
76
|
+
match.group(1),
|
|
77
|
+
file_path_inner,
|
|
78
|
+
root,
|
|
79
|
+
level + 1,
|
|
80
|
+
stack
|
|
81
|
+
)
|
|
82
|
+
includes.append(
|
|
83
|
+
Include(
|
|
84
|
+
composed,
|
|
85
|
+
f"Q_{level}_{index}",
|
|
86
|
+
match.group(0),
|
|
87
|
+
match.group(1)
|
|
88
|
+
)
|
|
91
89
|
)
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
index = index + 1
|
|
91
|
+
except FileNotFoundError:
|
|
92
|
+
if parent is not None:
|
|
93
|
+
raise FileNotFoundError(f"Include failed: File \"{get_relative_path(file_path_inner, root)}\" which was referred to in \"{get_relative_path(parent, root)}\", was not found...")
|
|
94
|
+
else:
|
|
95
|
+
raise FileNotFoundError(f"Include failed: File \"{get_relative_path(file_path_inner, root)}\" was not found...")
|
|
94
96
|
|
|
95
97
|
for include in includes:
|
|
96
98
|
sql = sub(escape(include.match), include.name, sql)
|
|
97
99
|
|
|
100
|
+
stack.pop()
|
|
101
|
+
|
|
98
102
|
return wrap_cte_sql(includes, sql, level, name)
|
|
99
103
|
|
|
100
104
|
def wrap_cte_sql(includes: Sequence[Include], sql: str, level: int, source: str) -> str:
|
sqlcompose/core/include.py
CHANGED
sqlcompose/py.typed
ADDED
|
File without changes
|
|
@@ -1,29 +1,54 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlcompose
|
|
3
|
-
Version: 0.0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Composition of linked SQL files
|
|
5
|
+
Author-email: Anders Madsen <anders.madsen@alphavue.com>
|
|
5
6
|
License: MIT
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Classifier:
|
|
10
|
-
Classifier: Operating System ::
|
|
7
|
+
Project-URL: repository, https://github.com/apmadsen/sqlcompose
|
|
8
|
+
Keywords: sql,composition,windows,linux
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Development Status :: 6 - Mature
|
|
11
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
12
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Natural Language :: English
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
11
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
13
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
-
|
|
22
|
+
Classifier: Programming Language :: Python
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.10
|
|
17
27
|
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Provides-Extra: test
|
|
30
|
+
Requires-Dist: pytest>=8.3.0; extra == "test"
|
|
31
|
+
Requires-Dist: pytest-cov>=6.1.0; extra == "test"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
[](https://github.com/apmadsen/sqlcompose/actions/workflows/python-test.yml)
|
|
35
|
+
[](https://github.com/apmadsen/sqlcompose/actions/workflows/python-test-coverage.yml)
|
|
36
|
+

|
|
37
|
+

|
|
38
|
+

|
|
39
|
+
|
|
18
40
|
|
|
19
41
|
# sqlcompose: Composition of linked SQL files
|
|
20
42
|
sqlcompose allows you to compose sql files from multiple files by introducing `INCLUDE` keywords. The SQL output is composed as CTE's or Common Table Expressions.
|
|
21
43
|
|
|
22
44
|
## Examples
|
|
23
45
|
__Execute the script directly:__
|
|
24
|
-
```
|
|
46
|
+
```console
|
|
25
47
|
sqlcompose query.sql
|
|
26
|
-
|
|
48
|
+
```
|
|
49
|
+
```bash
|
|
50
|
+
sqlcompose 'select * from $INCLUDE(included-query1.sql)' # on linux
|
|
51
|
+
sqlcompose "select * from $INCLUDE(included-query1.sql)" # on windows
|
|
27
52
|
```
|
|
28
53
|
|
|
29
54
|
__Import it in another script:__
|
|
@@ -84,4 +109,3 @@ WITH Q_1_1 AS (
|
|
|
84
109
|
)
|
|
85
110
|
SELECT * FROM Q_1
|
|
86
111
|
```
|
|
87
|
-
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
sqlcompose/__init__.py,sha256=Qh82ZdC3xjE6-a-BnwViHMgXRGuIeO3Sdp0SMKv5Ku4,252
|
|
2
|
+
sqlcompose/__main__.py,sha256=8-S7mIkyEbI2lYLhB_spSa6EAOHdSIkZqoQuFA2UNIM,244
|
|
3
|
+
sqlcompose/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
sqlcompose/core/app.py,sha256=3QstrdKsuTd59xtciwU9bVE8ndwC9V2wu4H_tgZU2DU,650
|
|
5
|
+
sqlcompose/core/circular_dependency_error.py,sha256=3JqXYqX4Qg5QC4te2IuiqU2snC2oU--g9m1qFah4A38,49
|
|
6
|
+
sqlcompose/core/compat.py,sha256=pIF8Lk-5MhwAlHD4_RCMXYOxiDyTJIA5nlbR5_Dyw0g,819
|
|
7
|
+
sqlcompose/core/functions.py,sha256=13960AR1GRJsoUwfI6g53opPYEcgoCdzVE7W2mcMbOw,3622
|
|
8
|
+
sqlcompose/core/include.py,sha256=0k5hyO0U_cYbj8iByIQoOF_YQb3kdRgegZVsYiMU2Vg,560
|
|
9
|
+
sqlcompose-0.0.2.dist-info/licenses/LICENSE,sha256=WtJk2ScYuo_lEP42z3DYU191mO2oU8z00nPsL9KAWCQ,1063
|
|
10
|
+
sqlcompose-0.0.2.dist-info/METADATA,sha256=F_iENCopEl7xrVlVBzCBPmirfWO78vcPB_BQyEuskx4,3756
|
|
11
|
+
sqlcompose-0.0.2.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
|
|
12
|
+
sqlcompose-0.0.2.dist-info/entry_points.txt,sha256=8-odh3ht86pH5a3UaGM0CNwyVT4upBILl6HU8SVPLPs,52
|
|
13
|
+
sqlcompose-0.0.2.dist-info/top_level.txt,sha256=-8g6qpTAzLcUSsbk3v6mgNbY34pJ2LIplugBVYB5Jq0,11
|
|
14
|
+
sqlcompose-0.0.2.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sqlcompose
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
sqlcompose/__init__.py,sha256=yAFSzkLv3bCZ6e4OWvws6aYjwHfLQcxhGVzK12Fs_CQ,89
|
|
2
|
-
sqlcompose/__main__.py,sha256=5TpsgP8V1SC14siCyUcHdEYWm9vDwxaUtqUgCC5f_4w,691
|
|
3
|
-
sqlcompose/core/compat.py,sha256=pIF8Lk-5MhwAlHD4_RCMXYOxiDyTJIA5nlbR5_Dyw0g,819
|
|
4
|
-
sqlcompose/core/functions.py,sha256=eHK_STVYOGDn3dtShTa0xaasYQi220tF12bQH9qEEUI,3693
|
|
5
|
-
sqlcompose/core/include.py,sha256=ejNaz2p2Lq1pUnwMxtBqE4p_hBOtG8OynqRpECLEbNQ,541
|
|
6
|
-
sqlcompose-0.0.0.dist-info/LICENSE,sha256=WtJk2ScYuo_lEP42z3DYU191mO2oU8z00nPsL9KAWCQ,1063
|
|
7
|
-
sqlcompose-0.0.0.dist-info/METADATA,sha256=dPh6avQdIOJ6NO20-Z3h8iRUEFM8VtVEzxSPhjcOqrk,2269
|
|
8
|
-
sqlcompose-0.0.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
|
9
|
-
sqlcompose-0.0.0.dist-info/entry_points.txt,sha256=N3eGXJzvw5yx5WgFFmX0L6Nw0-ojm90OfhtlC-C6t_4,51
|
|
10
|
-
sqlcompose-0.0.0.dist-info/RECORD,,
|
|
File without changes
|