mquery-toolkit 0.1.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.
- mquery_toolkit-0.1.0/LICENSE +21 -0
- mquery_toolkit-0.1.0/NOTICE +9 -0
- mquery_toolkit-0.1.0/PKG-INFO +189 -0
- mquery_toolkit-0.1.0/README.md +153 -0
- mquery_toolkit-0.1.0/pyproject.toml +74 -0
- mquery_toolkit-0.1.0/setup.cfg +4 -0
- mquery_toolkit-0.1.0/src/mquery_toolkit/THIRD_PARTY_NOTICES.txt +34 -0
- mquery_toolkit-0.1.0/src/mquery_toolkit/__init__.py +35 -0
- mquery_toolkit-0.1.0/src/mquery_toolkit/_bridge.cjs +45531 -0
- mquery_toolkit-0.1.0/src/mquery_toolkit/cli.py +118 -0
- mquery_toolkit-0.1.0/src/mquery_toolkit/core.py +600 -0
- mquery_toolkit-0.1.0/src/mquery_toolkit/fabric.py +213 -0
- mquery_toolkit-0.1.0/src/mquery_toolkit/pqtest.py +93 -0
- mquery_toolkit-0.1.0/src/mquery_toolkit/py.typed +0 -0
- mquery_toolkit-0.1.0/src/mquery_toolkit.egg-info/PKG-INFO +189 -0
- mquery_toolkit-0.1.0/src/mquery_toolkit.egg-info/SOURCES.txt +23 -0
- mquery_toolkit-0.1.0/src/mquery_toolkit.egg-info/dependency_links.txt +1 -0
- mquery_toolkit-0.1.0/src/mquery_toolkit.egg-info/entry_points.txt +2 -0
- mquery_toolkit-0.1.0/src/mquery_toolkit.egg-info/requires.txt +12 -0
- mquery_toolkit-0.1.0/src/mquery_toolkit.egg-info/top_level.txt +1 -0
- mquery_toolkit-0.1.0/tests/test_adapters.py +366 -0
- mquery_toolkit-0.1.0/tests/test_cli.py +51 -0
- mquery_toolkit-0.1.0/tests/test_core.py +356 -0
- mquery_toolkit-0.1.0/tests/test_corpus.py +46 -0
- mquery_toolkit-0.1.0/tests/test_rules.py +15 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gopal Bagaswar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Unofficial project, not affiliated with Microsoft. The bundled JavaScript
|
|
2
|
+
components retain their copyright and MIT permission notices in
|
|
3
|
+
`mquery_toolkit/THIRD_PARTY_NOTICES.txt`.
|
|
4
|
+
The test corpus includes a short Power Query M let-expression example from the
|
|
5
|
+
Microsoft M language specification:
|
|
6
|
+
https://learn.microsoft.com/en-us/powerquery-m/m-spec-let
|
|
7
|
+
|
|
8
|
+
The vendored DataConnectors samples remain Copyright (c) Microsoft Corporation
|
|
9
|
+
and are redistributed under the MIT License included beside those fixtures.
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mquery-toolkit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Unofficial offline tooling for Power Query M source (parse, format, check, rename) via Microsoft's official parser and formatter
|
|
5
|
+
Author: Gopal Bagaswar
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/GopalGB/mquery-toolkit
|
|
8
|
+
Project-URL: Repository, https://github.com/GopalGB/mquery-toolkit
|
|
9
|
+
Project-URL: Issues, https://github.com/GopalGB/mquery-toolkit/issues
|
|
10
|
+
Keywords: power-query,m-language,power-bi,fabric,linter,formatter
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
License-File: NOTICE
|
|
25
|
+
Provides-Extra: fabric
|
|
26
|
+
Requires-Dist: pyarrow>=14; extra == "fabric"
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov>=5; extra == "dev"
|
|
30
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
31
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
32
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
33
|
+
Requires-Dist: twine>=6.1; extra == "dev"
|
|
34
|
+
Requires-Dist: pip-audit>=2.7; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# mquery-toolkit
|
|
38
|
+
|
|
39
|
+
Offline command-line and Python tooling for Power Query M source: parse, format,
|
|
40
|
+
lint (`check`), and safely rename a `let` binding.
|
|
41
|
+
|
|
42
|
+
> **Unofficial.** Not affiliated with or endorsed by Microsoft. Not an M runtime -
|
|
43
|
+
> it parses and formats M source text; it does not evaluate queries.
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install mquery-toolkit
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Requires **Node.js 22 or newer** on `PATH`, or point `MQUERY_NODE` at a Node
|
|
52
|
+
binary. The Microsoft parser and formatter packages are bundled inside the
|
|
53
|
+
wheel (`_bridge.cjs`) - no `npm install` needed.
|
|
54
|
+
|
|
55
|
+
## Quick start
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Parse to deterministic JSON (tokens, root kind, bindings/references)
|
|
59
|
+
mquery parse query.pq
|
|
60
|
+
|
|
61
|
+
# Format - dry run prints a unified diff, nothing is written
|
|
62
|
+
mquery format query.pq
|
|
63
|
+
|
|
64
|
+
# Format and write in place (atomic replace, preserves mode/newline/encoding)
|
|
65
|
+
mquery format query.pq --write
|
|
66
|
+
|
|
67
|
+
# Lint, machine-readable output; exit code 2 if any diagnostic is severity=error
|
|
68
|
+
mquery check query.pq --json
|
|
69
|
+
|
|
70
|
+
# Rename one top-level let binding - dry run first
|
|
71
|
+
mquery rename query.pq --old OldName --new NewName
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Python API
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from mquery_toolkit import check, format_source, parse, rename, update_file
|
|
78
|
+
|
|
79
|
+
parsed = parse(source_text) # dict: tokens, rootKind, analysis
|
|
80
|
+
formatted = format_source(source_text) # formatted M source, same encoding
|
|
81
|
+
diagnostics = check(source_text, "query.pq") # list[Diagnostic]
|
|
82
|
+
renamed = rename(source_text, "OldName", "NewName")
|
|
83
|
+
|
|
84
|
+
# File-level edit with the same dry-run/--write safety model as the CLI
|
|
85
|
+
diff = update_file(path, format_source) # dry run: unified diff
|
|
86
|
+
diff = update_file(path, format_source, write=True) # atomic write
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Diagnostics
|
|
90
|
+
|
|
91
|
+
| Code | Severity | Meaning |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| `M_PARSE_ERROR` | error | source does not parse |
|
|
94
|
+
| `M001` | error | duplicate `let` binding name |
|
|
95
|
+
| `M002` | warning | `Web.Contents` called with a non-literal (dynamic) URL |
|
|
96
|
+
| `M003` | warning | credential-like literal (`password`/`token`/`secret` = `"..."`) |
|
|
97
|
+
| `M004` | warning | `let` binding unreachable from the result |
|
|
98
|
+
| `M005` | warning | unresolved unqualified reference |
|
|
99
|
+
| `M006` | info | source-function inventory (`*.Contents` dependency) |
|
|
100
|
+
|
|
101
|
+
`M002` and `M003` are conservative text-pattern checks on the raw source, not
|
|
102
|
+
AST checks; expect occasional false positives inside comments or strings.
|
|
103
|
+
Every matching occurrence is reported, one diagnostic per call site or
|
|
104
|
+
literal.
|
|
105
|
+
|
|
106
|
+
`check --json` emits stable objects; `check` without `--json` prints
|
|
107
|
+
`file:line:column: severity code: message` per diagnostic. The CLI exits `2`
|
|
108
|
+
when any diagnostic has severity `error`, `0` otherwise.
|
|
109
|
+
|
|
110
|
+
## Safety model
|
|
111
|
+
|
|
112
|
+
- **Dry-run by default.** Every edit command (`format`, `rename`,
|
|
113
|
+
`replace-source`) prints a unified diff and touches nothing unless `--write`
|
|
114
|
+
is passed.
|
|
115
|
+
- **`--write` is an atomic replace**: the file is written to a sibling temp
|
|
116
|
+
file, `fsync`'d, `chmod`'d to match the original, then moved into place with
|
|
117
|
+
`os.replace`.
|
|
118
|
+
- **Layout is preserved**: UTF-8 encoding, a leading BOM (present in every
|
|
119
|
+
Power Query SDK connector file), newline convention (`\n` vs `\r\n`),
|
|
120
|
+
final-newline state, and file mode all round-trip unchanged.
|
|
121
|
+
- **Refuses symlinks and hardlinks** - writes require a regular, single-link
|
|
122
|
+
file.
|
|
123
|
+
- **Detects concurrent change**: the source is snapshotted before the
|
|
124
|
+
transform and re-checked immediately before the atomic replace - this final
|
|
125
|
+
snapshot check, not the lock, is the guarantee against lost updates; a
|
|
126
|
+
change in that microsecond window raises `SafeWriteError`.
|
|
127
|
+
- **Advisory lock while writing only** - a `--write` call takes a
|
|
128
|
+
cross-process advisory lock (`fcntl`/`msvcrt`) for the duration of the
|
|
129
|
+
write and removes the lock file afterward, best-effort. It only serialises
|
|
130
|
+
cooperating `mquery` processes and is not a correctness guarantee: because
|
|
131
|
+
the lock file is removed after use, a waiting process and a freshly
|
|
132
|
+
started one can end up locking different inodes. Dry-run calls take no
|
|
133
|
+
lock and create no lock file.
|
|
134
|
+
- This is **not mandatory locking** - no OS provides a portable mandatory
|
|
135
|
+
lock, and the advisory lock is not itself the correctness guard. Use
|
|
136
|
+
source control or external exclusive ownership for concurrent editors.
|
|
137
|
+
|
|
138
|
+
## Limits
|
|
139
|
+
|
|
140
|
+
- Input and output are capped at **10 MiB**.
|
|
141
|
+
- The Node subprocess is bounded to a **30 second** timeout.
|
|
142
|
+
- Supported extensions: `.pq`, `.m`, `.pqm`, and any `*.query.pq` file.
|
|
143
|
+
- `rename` scope: exactly **one unquoted top-level `let` binding**. It refuses
|
|
144
|
+
quoted identifiers (`#"..."`), record literals, lambda expressions, and
|
|
145
|
+
non-ASCII source.
|
|
146
|
+
- `Retry-After` on the Fabric adapter must be whole seconds; HTTP-date values
|
|
147
|
+
are rejected.
|
|
148
|
+
|
|
149
|
+
## Optional adapters
|
|
150
|
+
|
|
151
|
+
- **`fabric` extra** (`pip install "mquery-toolkit[fabric]"`) - a Fabric
|
|
152
|
+
Execute Query client that takes a caller-provided bearer token and an
|
|
153
|
+
injected HTTP transport. It never manages credentials itself and is fully
|
|
154
|
+
mocked in tests (no network access in the test suite).
|
|
155
|
+
- **`pqtest`** - a bounded wrapper around a user-installed Microsoft PQTest
|
|
156
|
+
executable, Windows-only, pinned to version `2.155.2`. It never downloads a
|
|
157
|
+
binary; it only validates and runs one already on disk.
|
|
158
|
+
|
|
159
|
+
## What it is not
|
|
160
|
+
|
|
161
|
+
- Not an M language runtime or evaluator.
|
|
162
|
+
- Not a Power BI or Fabric client, and it does not manage credentials.
|
|
163
|
+
- Not a general-purpose file editor - it only touches files with a supported
|
|
164
|
+
extension and only through the safety model above.
|
|
165
|
+
- Not a replacement for Microsoft's own parser/formatter - it vendors and
|
|
166
|
+
calls them directly rather than reimplementing M syntax.
|
|
167
|
+
|
|
168
|
+
## Development
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
git clone https://github.com/GopalGB/mquery-toolkit
|
|
172
|
+
cd mquery-toolkit
|
|
173
|
+
python -m venv .venv && source .venv/bin/activate
|
|
174
|
+
pip install -e ".[dev,fabric]"
|
|
175
|
+
npm ci --ignore-scripts
|
|
176
|
+
|
|
177
|
+
pytest -q --cov=mquery_toolkit --cov-fail-under=80
|
|
178
|
+
mypy src
|
|
179
|
+
ruff check .
|
|
180
|
+
ruff format --check .
|
|
181
|
+
npm test
|
|
182
|
+
python -m build
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
MIT - see `LICENSE`. Bundled Microsoft packages
|
|
188
|
+
(`@microsoft/powerquery-parser`, `@microsoft/powerquery-formatter`) and their
|
|
189
|
+
dependencies are also MIT; see `THIRD_PARTY_NOTICES.txt` and `NOTICE`.
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# mquery-toolkit
|
|
2
|
+
|
|
3
|
+
Offline command-line and Python tooling for Power Query M source: parse, format,
|
|
4
|
+
lint (`check`), and safely rename a `let` binding.
|
|
5
|
+
|
|
6
|
+
> **Unofficial.** Not affiliated with or endorsed by Microsoft. Not an M runtime -
|
|
7
|
+
> it parses and formats M source text; it does not evaluate queries.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install mquery-toolkit
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Requires **Node.js 22 or newer** on `PATH`, or point `MQUERY_NODE` at a Node
|
|
16
|
+
binary. The Microsoft parser and formatter packages are bundled inside the
|
|
17
|
+
wheel (`_bridge.cjs`) - no `npm install` needed.
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Parse to deterministic JSON (tokens, root kind, bindings/references)
|
|
23
|
+
mquery parse query.pq
|
|
24
|
+
|
|
25
|
+
# Format - dry run prints a unified diff, nothing is written
|
|
26
|
+
mquery format query.pq
|
|
27
|
+
|
|
28
|
+
# Format and write in place (atomic replace, preserves mode/newline/encoding)
|
|
29
|
+
mquery format query.pq --write
|
|
30
|
+
|
|
31
|
+
# Lint, machine-readable output; exit code 2 if any diagnostic is severity=error
|
|
32
|
+
mquery check query.pq --json
|
|
33
|
+
|
|
34
|
+
# Rename one top-level let binding - dry run first
|
|
35
|
+
mquery rename query.pq --old OldName --new NewName
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Python API
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from mquery_toolkit import check, format_source, parse, rename, update_file
|
|
42
|
+
|
|
43
|
+
parsed = parse(source_text) # dict: tokens, rootKind, analysis
|
|
44
|
+
formatted = format_source(source_text) # formatted M source, same encoding
|
|
45
|
+
diagnostics = check(source_text, "query.pq") # list[Diagnostic]
|
|
46
|
+
renamed = rename(source_text, "OldName", "NewName")
|
|
47
|
+
|
|
48
|
+
# File-level edit with the same dry-run/--write safety model as the CLI
|
|
49
|
+
diff = update_file(path, format_source) # dry run: unified diff
|
|
50
|
+
diff = update_file(path, format_source, write=True) # atomic write
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Diagnostics
|
|
54
|
+
|
|
55
|
+
| Code | Severity | Meaning |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| `M_PARSE_ERROR` | error | source does not parse |
|
|
58
|
+
| `M001` | error | duplicate `let` binding name |
|
|
59
|
+
| `M002` | warning | `Web.Contents` called with a non-literal (dynamic) URL |
|
|
60
|
+
| `M003` | warning | credential-like literal (`password`/`token`/`secret` = `"..."`) |
|
|
61
|
+
| `M004` | warning | `let` binding unreachable from the result |
|
|
62
|
+
| `M005` | warning | unresolved unqualified reference |
|
|
63
|
+
| `M006` | info | source-function inventory (`*.Contents` dependency) |
|
|
64
|
+
|
|
65
|
+
`M002` and `M003` are conservative text-pattern checks on the raw source, not
|
|
66
|
+
AST checks; expect occasional false positives inside comments or strings.
|
|
67
|
+
Every matching occurrence is reported, one diagnostic per call site or
|
|
68
|
+
literal.
|
|
69
|
+
|
|
70
|
+
`check --json` emits stable objects; `check` without `--json` prints
|
|
71
|
+
`file:line:column: severity code: message` per diagnostic. The CLI exits `2`
|
|
72
|
+
when any diagnostic has severity `error`, `0` otherwise.
|
|
73
|
+
|
|
74
|
+
## Safety model
|
|
75
|
+
|
|
76
|
+
- **Dry-run by default.** Every edit command (`format`, `rename`,
|
|
77
|
+
`replace-source`) prints a unified diff and touches nothing unless `--write`
|
|
78
|
+
is passed.
|
|
79
|
+
- **`--write` is an atomic replace**: the file is written to a sibling temp
|
|
80
|
+
file, `fsync`'d, `chmod`'d to match the original, then moved into place with
|
|
81
|
+
`os.replace`.
|
|
82
|
+
- **Layout is preserved**: UTF-8 encoding, a leading BOM (present in every
|
|
83
|
+
Power Query SDK connector file), newline convention (`\n` vs `\r\n`),
|
|
84
|
+
final-newline state, and file mode all round-trip unchanged.
|
|
85
|
+
- **Refuses symlinks and hardlinks** - writes require a regular, single-link
|
|
86
|
+
file.
|
|
87
|
+
- **Detects concurrent change**: the source is snapshotted before the
|
|
88
|
+
transform and re-checked immediately before the atomic replace - this final
|
|
89
|
+
snapshot check, not the lock, is the guarantee against lost updates; a
|
|
90
|
+
change in that microsecond window raises `SafeWriteError`.
|
|
91
|
+
- **Advisory lock while writing only** - a `--write` call takes a
|
|
92
|
+
cross-process advisory lock (`fcntl`/`msvcrt`) for the duration of the
|
|
93
|
+
write and removes the lock file afterward, best-effort. It only serialises
|
|
94
|
+
cooperating `mquery` processes and is not a correctness guarantee: because
|
|
95
|
+
the lock file is removed after use, a waiting process and a freshly
|
|
96
|
+
started one can end up locking different inodes. Dry-run calls take no
|
|
97
|
+
lock and create no lock file.
|
|
98
|
+
- This is **not mandatory locking** - no OS provides a portable mandatory
|
|
99
|
+
lock, and the advisory lock is not itself the correctness guard. Use
|
|
100
|
+
source control or external exclusive ownership for concurrent editors.
|
|
101
|
+
|
|
102
|
+
## Limits
|
|
103
|
+
|
|
104
|
+
- Input and output are capped at **10 MiB**.
|
|
105
|
+
- The Node subprocess is bounded to a **30 second** timeout.
|
|
106
|
+
- Supported extensions: `.pq`, `.m`, `.pqm`, and any `*.query.pq` file.
|
|
107
|
+
- `rename` scope: exactly **one unquoted top-level `let` binding**. It refuses
|
|
108
|
+
quoted identifiers (`#"..."`), record literals, lambda expressions, and
|
|
109
|
+
non-ASCII source.
|
|
110
|
+
- `Retry-After` on the Fabric adapter must be whole seconds; HTTP-date values
|
|
111
|
+
are rejected.
|
|
112
|
+
|
|
113
|
+
## Optional adapters
|
|
114
|
+
|
|
115
|
+
- **`fabric` extra** (`pip install "mquery-toolkit[fabric]"`) - a Fabric
|
|
116
|
+
Execute Query client that takes a caller-provided bearer token and an
|
|
117
|
+
injected HTTP transport. It never manages credentials itself and is fully
|
|
118
|
+
mocked in tests (no network access in the test suite).
|
|
119
|
+
- **`pqtest`** - a bounded wrapper around a user-installed Microsoft PQTest
|
|
120
|
+
executable, Windows-only, pinned to version `2.155.2`. It never downloads a
|
|
121
|
+
binary; it only validates and runs one already on disk.
|
|
122
|
+
|
|
123
|
+
## What it is not
|
|
124
|
+
|
|
125
|
+
- Not an M language runtime or evaluator.
|
|
126
|
+
- Not a Power BI or Fabric client, and it does not manage credentials.
|
|
127
|
+
- Not a general-purpose file editor - it only touches files with a supported
|
|
128
|
+
extension and only through the safety model above.
|
|
129
|
+
- Not a replacement for Microsoft's own parser/formatter - it vendors and
|
|
130
|
+
calls them directly rather than reimplementing M syntax.
|
|
131
|
+
|
|
132
|
+
## Development
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
git clone https://github.com/GopalGB/mquery-toolkit
|
|
136
|
+
cd mquery-toolkit
|
|
137
|
+
python -m venv .venv && source .venv/bin/activate
|
|
138
|
+
pip install -e ".[dev,fabric]"
|
|
139
|
+
npm ci --ignore-scripts
|
|
140
|
+
|
|
141
|
+
pytest -q --cov=mquery_toolkit --cov-fail-under=80
|
|
142
|
+
mypy src
|
|
143
|
+
ruff check .
|
|
144
|
+
ruff format --check .
|
|
145
|
+
npm test
|
|
146
|
+
python -m build
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
MIT - see `LICENSE`. Bundled Microsoft packages
|
|
152
|
+
(`@microsoft/powerquery-parser`, `@microsoft/powerquery-formatter`) and their
|
|
153
|
+
dependencies are also MIT; see `THIRD_PARTY_NOTICES.txt` and `NOTICE`.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mquery-toolkit"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Unofficial offline tooling for Power Query M source (parse, format, check, rename) via Microsoft's official parser and formatter"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE", "NOTICE"]
|
|
13
|
+
authors = [{ name = "Gopal Bagaswar" }]
|
|
14
|
+
keywords = ["power-query", "m-language", "power-bi", "fabric", "linter", "formatter"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Environment :: Console",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Software Development :: Libraries",
|
|
25
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
26
|
+
]
|
|
27
|
+
dependencies = []
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
fabric = ["pyarrow>=14"]
|
|
31
|
+
dev = [
|
|
32
|
+
"pytest>=8",
|
|
33
|
+
"pytest-cov>=5",
|
|
34
|
+
"mypy>=1.10",
|
|
35
|
+
"ruff>=0.5",
|
|
36
|
+
"build>=1.2",
|
|
37
|
+
"twine>=6.1",
|
|
38
|
+
"pip-audit>=2.7",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Homepage = "https://github.com/GopalGB/mquery-toolkit"
|
|
43
|
+
Repository = "https://github.com/GopalGB/mquery-toolkit"
|
|
44
|
+
Issues = "https://github.com/GopalGB/mquery-toolkit/issues"
|
|
45
|
+
|
|
46
|
+
[project.scripts]
|
|
47
|
+
mquery = "mquery_toolkit.cli:main"
|
|
48
|
+
|
|
49
|
+
[tool.setuptools]
|
|
50
|
+
package-dir = { "" = "src" }
|
|
51
|
+
|
|
52
|
+
[tool.setuptools.packages.find]
|
|
53
|
+
where = ["src"]
|
|
54
|
+
|
|
55
|
+
[tool.setuptools.package-data]
|
|
56
|
+
mquery_toolkit = ["_bridge.cjs", "THIRD_PARTY_NOTICES.txt", "py.typed"]
|
|
57
|
+
|
|
58
|
+
[tool.ruff]
|
|
59
|
+
line-length = 88
|
|
60
|
+
target-version = "py311"
|
|
61
|
+
|
|
62
|
+
[tool.ruff.lint]
|
|
63
|
+
select = ["E", "F", "I", "UP", "B"]
|
|
64
|
+
|
|
65
|
+
[tool.mypy]
|
|
66
|
+
python_version = "3.11"
|
|
67
|
+
strict = true
|
|
68
|
+
files = ["src"]
|
|
69
|
+
|
|
70
|
+
[tool.pytest.ini_options]
|
|
71
|
+
testpaths = ["tests"]
|
|
72
|
+
|
|
73
|
+
[tool.coverage.run]
|
|
74
|
+
source = ["mquery_toolkit"]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Bundled JavaScript dependencies
|
|
2
|
+
===============================
|
|
3
|
+
|
|
4
|
+
@microsoft/powerquery-parser 2.0.0 and 0.19.0
|
|
5
|
+
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
6
|
+
|
|
7
|
+
@microsoft/powerquery-formatter 1.0.0
|
|
8
|
+
Copyright (c) Microsoft Corporation.
|
|
9
|
+
|
|
10
|
+
grapheme-splitter 1.0.4
|
|
11
|
+
Copyright (c) 2015 Orlin Georgiev
|
|
12
|
+
|
|
13
|
+
performance-now 2.1.0
|
|
14
|
+
Copyright (c) 2013 Braveg1rl
|
|
15
|
+
|
|
16
|
+
All components above are licensed under the MIT License:
|
|
17
|
+
|
|
18
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
19
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
20
|
+
in the Software without restriction, including without limitation the rights
|
|
21
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
22
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
23
|
+
furnished to do so, subject to the following conditions:
|
|
24
|
+
|
|
25
|
+
The above copyright notice and this permission notice shall be included in all
|
|
26
|
+
copies or substantial portions of the Software.
|
|
27
|
+
|
|
28
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
29
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
30
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
31
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
32
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
33
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
34
|
+
SOFTWARE.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Typed offline tooling for Power Query M source."""
|
|
2
|
+
|
|
3
|
+
from .core import (
|
|
4
|
+
AdapterError,
|
|
5
|
+
Diagnostic,
|
|
6
|
+
MQueryError,
|
|
7
|
+
NodeError,
|
|
8
|
+
ParseError,
|
|
9
|
+
RenameRefusal,
|
|
10
|
+
SafeWriteError,
|
|
11
|
+
check,
|
|
12
|
+
dependencies,
|
|
13
|
+
format_source,
|
|
14
|
+
parse,
|
|
15
|
+
rename,
|
|
16
|
+
replace_source,
|
|
17
|
+
update_file,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"AdapterError",
|
|
22
|
+
"Diagnostic",
|
|
23
|
+
"MQueryError",
|
|
24
|
+
"NodeError",
|
|
25
|
+
"ParseError",
|
|
26
|
+
"RenameRefusal",
|
|
27
|
+
"SafeWriteError",
|
|
28
|
+
"check",
|
|
29
|
+
"dependencies",
|
|
30
|
+
"format_source",
|
|
31
|
+
"parse",
|
|
32
|
+
"rename",
|
|
33
|
+
"replace_source",
|
|
34
|
+
"update_file",
|
|
35
|
+
]
|