fullscript.spellbook 2.2.0__tar.gz → 3.0.1__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.
- fullscript_spellbook-3.0.1/AGENTS.md +107 -0
- fullscript_spellbook-3.0.1/CLAUDE.md +1 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/PKG-INFO +1 -1
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/documentation/db_magic.md +93 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/fullscript.spellbook.egg-info/PKG-INFO +1 -1
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/fullscript.spellbook.egg-info/SOURCES.txt +6 -1
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/fullscript.spellbook.egg-info/scm_file_list.json +6 -1
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/fullscript.spellbook.egg-info/scm_version.json +2 -2
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/pyproject.toml +1 -1
- fullscript_spellbook-3.0.1/spellbook/__init__.py +19 -0
- fullscript_spellbook-3.0.1/spellbook/db_magic.py +635 -0
- fullscript_spellbook-3.0.1/spellbook/exceptions.py +41 -0
- fullscript_spellbook-3.0.1/spellbook/utils.py +238 -0
- fullscript_spellbook-3.0.1/tests/test_db_magic.py +594 -0
- fullscript_spellbook-3.0.1/tests/test_utils.py +257 -0
- fullscript_spellbook-2.2.0/spellbook/db_magic.py +0 -416
- fullscript_spellbook-2.2.0/spellbook/utils.py +0 -123
- fullscript_spellbook-2.2.0/tests/test_db_magic.py +0 -316
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/.gitignore +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/.gitlab-ci.yml +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/.python-version +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/LICENSE +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/MANIFEST.in +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/README.md +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/documentation/drive_magic.md +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/documentation/sheet_magic.md +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/documentation/snowflake_key_pair_setup.md +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/documentation/stats_magic.md +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/fullscript.spellbook.egg-info/dependency_links.txt +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/fullscript.spellbook.egg-info/requires.txt +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/fullscript.spellbook.egg-info/top_level.txt +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/requirements.txt +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/setup.cfg +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook/drive_magic.py +0 -0
- /fullscript_spellbook-2.2.0/spellbook/__init__.py → /fullscript_spellbook-3.0.1/spellbook/py.typed +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook/sheet_magic.py +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook/stats_magic.py +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook/wizards/__init__.py +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook/wizards/wizard1.txt +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook/wizards/wizard10.txt +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook/wizards/wizard11.txt +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook/wizards/wizard12.txt +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook/wizards/wizard2.txt +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook/wizards/wizard3.txt +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook/wizards/wizard4.txt +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook/wizards/wizard5.txt +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook/wizards/wizard6.txt +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook/wizards/wizard7.txt +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook/wizards/wizard8.txt +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook/wizards/wizard9.txt +0 -0
- {fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/spellbook_config.yaml +0 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Working on Spellbook
|
|
2
|
+
|
|
3
|
+
Notes for anyone - person or coding agent - changing this package. This file covers the
|
|
4
|
+
things that are not obvious from reading the code.
|
|
5
|
+
|
|
6
|
+
## What this package is
|
|
7
|
+
|
|
8
|
+
A thin, friendly wrapper over Snowflake, MySQL, Google Sheets, Google Drive and some
|
|
9
|
+
statistics helpers, published publicly to PyPI as `fullscript.spellbook`.
|
|
10
|
+
|
|
11
|
+
**It is a public package.** Never commit Fullscript hostnames, database names, RDS instance
|
|
12
|
+
identifiers, usernames or internal URLs - not in code, examples, docs or tests. Use
|
|
13
|
+
placeholders (`${MYSQL_HOST}`, `your_rds_instance_name`) instead.
|
|
14
|
+
|
|
15
|
+
## Running the tests
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install -e '.[test]'
|
|
19
|
+
pytest
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Tests mock the database and `subprocess`, so they need no network and no credentials.
|
|
23
|
+
`tests/test_db_magic.py` covers connections and auth, `tests/test_utils.py` covers config
|
|
24
|
+
loading. Testing against a real database needs AWS SSO, which no automated run can do.
|
|
25
|
+
|
|
26
|
+
## Versioning
|
|
27
|
+
|
|
28
|
+
The version is **not** in a file. `setuptools_scm` derives it from git tags, so releasing
|
|
29
|
+
means tagging:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git tag 3.0.0 && git push origin main --tags
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Pushing a tag triggers `.gitlab-ci.yml`, which publishes to both the internal registry and
|
|
36
|
+
public PyPI. Do not tag unless you intend to publish.
|
|
37
|
+
|
|
38
|
+
## Conventions worth keeping
|
|
39
|
+
|
|
40
|
+
**Errors raise, they do not return empty.** Every failure path raises a subclass of
|
|
41
|
+
`SpellbookError` from `spellbook/exceptions.py`. Returning `pd.DataFrame()` or `None` on
|
|
42
|
+
failure is the thing this package deliberately moved away from: a caller cannot tell
|
|
43
|
+
"matched no rows" from "never ran", which produces confidently wrong analysis. An empty
|
|
44
|
+
DataFrame must only ever mean zero rows.
|
|
45
|
+
|
|
46
|
+
**Decorative output goes to stderr, never stdout.** The wizard ASCII art is charm, not
|
|
47
|
+
data. `utils.show_wizard()` honours `SPELLBOOK_QUIET=1`; set that when calling Spellbook
|
|
48
|
+
from a script or agent. Use `logging` for diagnostics rather than `print`.
|
|
49
|
+
|
|
50
|
+
**Function signatures are `(db_name, query)`, in that order.** `execute_mysql` once took
|
|
51
|
+
`(query, dbname)`, which silently broke every `run_sql` call against MySQL. Keep the order
|
|
52
|
+
and the `db_name` spelling consistent across every function.
|
|
53
|
+
|
|
54
|
+
**Bind parameters in catalogue queries.** `list_tables` and `describe_table` pass values
|
|
55
|
+
through SQLAlchemy parameters so a `%` or a quote in user input cannot reshape the query.
|
|
56
|
+
`preview_table` cannot bind a table name, so it validates it against a strict identifier
|
|
57
|
+
pattern instead.
|
|
58
|
+
|
|
59
|
+
## Traps that have cost time before
|
|
60
|
+
|
|
61
|
+
**Validate the database being used, not the whole config.** The entire `databases` section
|
|
62
|
+
is re-parsed on every lookup, so any check that runs during parsing applies to databases the
|
|
63
|
+
caller never asked for. Release 3.0.0 raised on the first unset `${VAR}` anywhere in the
|
|
64
|
+
section, which meant an unrelated Snowflake entry with no key passphrase blocked every MySQL
|
|
65
|
+
query. `parse_env_variables` now substitutes permissively and records unresolved names under
|
|
66
|
+
`_unresolved_env`; `db_magic._validate_db_config` turns those into an error for the selected
|
|
67
|
+
database only, and only for settings in `REQUIRED_FIELDS`. Settings that are legitimately
|
|
68
|
+
empty - a Snowflake passphrase, `ssl_ca`, `port`, `schema` - must stay out of that list.
|
|
69
|
+
|
|
70
|
+
**Config file precedence.** `utils.find_config_file()` prefers `spellbook_config.yaml` in
|
|
71
|
+
the *current working directory* over the one in your home directory. Running anything from
|
|
72
|
+
this repository picks up the placeholder example config, not your real one. If a connection
|
|
73
|
+
fails with odd credentials, check which file was loaded.
|
|
74
|
+
|
|
75
|
+
**Two MySQL drivers.** Password auth uses `mysql+mysqlconnector`; AWS RDS IAM auth uses
|
|
76
|
+
`mysql+pymysql`, because only pymysql completes the IAM token handshake. They are not
|
|
77
|
+
interchangeable:
|
|
78
|
+
|
|
79
|
+
- pymysql substitutes parameters whenever `args is not None`, and pandas passes an empty
|
|
80
|
+
tuple, so a literal `%` in `like '%value%'` is read as a format character and raises.
|
|
81
|
+
mysql-connector guards on `if params:` instead. `db_magic._iam_cursor_class()` normalises
|
|
82
|
+
empty parameters to `None` so both drivers behave the same. Do not remove it without a
|
|
83
|
+
test that runs a `like '%...%'` query.
|
|
84
|
+
- An empty `ssl` dict makes TLS merely *preferred* in pymysql. The dict passed must be
|
|
85
|
+
truthy for TLS to be required.
|
|
86
|
+
|
|
87
|
+
**RDS IAM specifics.** Tokens expire in about 15 minutes, so they are fetched per
|
|
88
|
+
connection and never cached. `rx rds password` takes the *RDS instance* name, which usually
|
|
89
|
+
differs from the config alias - hence the optional `rds_db_name` field. The token is URL
|
|
90
|
+
shaped and must be `quote_plus`-escaped into the connection string.
|
|
91
|
+
|
|
92
|
+
**`iam_auth` is internal only.** It shells out to `rx`, which is Fullscript internal
|
|
93
|
+
tooling, so the feature cannot work for public users of this package. Keep password auth
|
|
94
|
+
working and keep the docs honest about the limitation.
|
|
95
|
+
|
|
96
|
+
## Repository layout
|
|
97
|
+
|
|
98
|
+
| Path | Purpose |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| `spellbook/db_magic.py` | Snowflake and MySQL connections, queries, schema helpers |
|
|
101
|
+
| `spellbook/exceptions.py` | The `SpellbookError` hierarchy |
|
|
102
|
+
| `spellbook/utils.py` | Config loading, env substitution, wizards, SQL file loading |
|
|
103
|
+
| `spellbook/sheet_magic.py` | Google Sheets |
|
|
104
|
+
| `spellbook/drive_magic.py` | Google Drive |
|
|
105
|
+
| `spellbook/stats_magic.py` | Statistical tests |
|
|
106
|
+
| `spellbook_config.yaml` | **Example** config, published - placeholders only |
|
|
107
|
+
| `documentation/` | User-facing docs per module |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
See [AGENTS.md](AGENTS.md) for how to work on this package.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fullscript.spellbook
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.1
|
|
4
4
|
Summary: A simple package to make your life easier.
|
|
5
5
|
Author-email: Meinhard Mare <meinhard.mare@fullscript.com>, Thomas Edwards <thomas.edwards@fullscript.com>, Gui Chagas <gui.chagas@fullscript.com>, David King <david.king@fullscript.com>, Karsten Melchers <karsten.melchers@fullscript.com>
|
|
6
6
|
License: MIT
|
|
@@ -10,6 +10,48 @@ A magical wrapper to perform work with databases.
|
|
|
10
10
|
from spellbook import db_magic
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
### Error Handling
|
|
14
|
+
|
|
15
|
+
Every failure raises an exception. An empty DataFrame always means "the query matched no
|
|
16
|
+
rows" and never "something went wrong", so results can be trusted without extra checks.
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from spellbook import db_magic
|
|
20
|
+
from spellbook.exceptions import SpellbookError, QueryError, DatabaseNotFoundError
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
df = db_magic.get_data('reporting_db', 'select * from users limit 10')
|
|
24
|
+
except DatabaseNotFoundError:
|
|
25
|
+
... # the name is not in spellbook_config.yaml
|
|
26
|
+
except QueryError:
|
|
27
|
+
... # the database rejected the query
|
|
28
|
+
except SpellbookError:
|
|
29
|
+
... # anything Spellbook raises
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
| Exception | Raised when |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| `SpellbookError` | Base class for all of the below |
|
|
35
|
+
| `ConfigError` | The config file is missing, malformed, or references an unset `${VAR}` |
|
|
36
|
+
| `DatabaseNotFoundError` | No database in the config matches the requested name |
|
|
37
|
+
| `AuthenticationError` | Credentials could not be obtained or were rejected |
|
|
38
|
+
| `ConnectionFailedError` | The database could not be reached |
|
|
39
|
+
| `QueryError` | The query was rejected or failed while running |
|
|
40
|
+
|
|
41
|
+
> **Upgrading from 2.x:** these functions used to print a message and return an empty
|
|
42
|
+
> DataFrame or `None` on failure. Code that inspected `df.empty` to detect *errors* now
|
|
43
|
+
> needs a `try`/`except`. Code that used `df.empty` to detect *zero rows* is unaffected.
|
|
44
|
+
|
|
45
|
+
### Quiet Mode
|
|
46
|
+
|
|
47
|
+
Spellbook prints a wizard for company on each query. It goes to stderr, never stdout, so it
|
|
48
|
+
cannot contaminate results. Set `SPELLBOOK_QUIET=1` to turn it off - recommended for
|
|
49
|
+
scripts, scheduled jobs and anything driven by an AI agent.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
export SPELLBOOK_QUIET=1
|
|
53
|
+
```
|
|
54
|
+
|
|
13
55
|
### MySQL Authentication
|
|
14
56
|
|
|
15
57
|
MySQL connections support two authentication methods, chosen per database in `spellbook_config.yaml`.
|
|
@@ -159,3 +201,54 @@ db_magic.run_sql("mysql_db", update_query)
|
|
|
159
201
|
|
|
160
202
|
print("SQL executed successfully!")
|
|
161
203
|
```
|
|
204
|
+
|
|
205
|
+
### Exploring a database
|
|
206
|
+
|
|
207
|
+
These helpers answer "what is in here?" without hand-writing `information_schema` queries.
|
|
208
|
+
They are useful interactively, and they are what an AI agent needs before it can write a
|
|
209
|
+
sensible query.
|
|
210
|
+
|
|
211
|
+
##### list_databases()
|
|
212
|
+
|
|
213
|
+
Lists the databases in your configuration file, with no credentials included.
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
db_magic.list_databases()
|
|
217
|
+
# name type database iam_auth
|
|
218
|
+
# 0 reporting_db mysql mysql_database True
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
##### list_tables(db_name, pattern=None)
|
|
222
|
+
|
|
223
|
+
Lists the tables in a database, optionally filtered by a case-insensitive substring.
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
db_magic.list_tables("mysql_db")
|
|
227
|
+
db_magic.list_tables("mysql_db", pattern="order")
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The pattern is passed as a bound parameter, so `%` and quotes in it are treated as literal
|
|
231
|
+
text rather than changing the query.
|
|
232
|
+
|
|
233
|
+
##### describe_table(db_name, table)
|
|
234
|
+
|
|
235
|
+
Returns one row per column, with its type, nullability and default. Raises `QueryError` if
|
|
236
|
+
the table does not exist.
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
db_magic.describe_table("mysql_db", "users")
|
|
240
|
+
# column_name data_type is_nullable column_default
|
|
241
|
+
# 0 id bigint NO None
|
|
242
|
+
# 1 name varchar YES None
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
##### preview_table(db_name, table, limit=10)
|
|
246
|
+
|
|
247
|
+
Reads the first few rows of a table.
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
db_magic.preview_table("mysql_db", "users", limit=5)
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
`table` must be a plain identifier - letters, digits, underscores, dots or dollar signs -
|
|
254
|
+
because a table name cannot be passed as a bound parameter.
|
{fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/fullscript.spellbook.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fullscript.spellbook
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.1
|
|
4
4
|
Summary: A simple package to make your life easier.
|
|
5
5
|
Author-email: Meinhard Mare <meinhard.mare@fullscript.com>, Thomas Edwards <thomas.edwards@fullscript.com>, Gui Chagas <gui.chagas@fullscript.com>, David King <david.king@fullscript.com>, Karsten Melchers <karsten.melchers@fullscript.com>
|
|
6
6
|
License: MIT
|
{fullscript_spellbook-2.2.0 → fullscript_spellbook-3.0.1}/fullscript.spellbook.egg-info/SOURCES.txt
RENAMED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
.gitignore
|
|
2
2
|
.gitlab-ci.yml
|
|
3
3
|
.python-version
|
|
4
|
+
AGENTS.md
|
|
5
|
+
CLAUDE.md
|
|
4
6
|
LICENSE
|
|
5
7
|
MANIFEST.in
|
|
6
8
|
README.md
|
|
@@ -22,6 +24,8 @@ fullscript.spellbook.egg-info/top_level.txt
|
|
|
22
24
|
spellbook/__init__.py
|
|
23
25
|
spellbook/db_magic.py
|
|
24
26
|
spellbook/drive_magic.py
|
|
27
|
+
spellbook/exceptions.py
|
|
28
|
+
spellbook/py.typed
|
|
25
29
|
spellbook/sheet_magic.py
|
|
26
30
|
spellbook/stats_magic.py
|
|
27
31
|
spellbook/utils.py
|
|
@@ -38,4 +42,5 @@ spellbook/wizards/wizard6.txt
|
|
|
38
42
|
spellbook/wizards/wizard7.txt
|
|
39
43
|
spellbook/wizards/wizard8.txt
|
|
40
44
|
spellbook/wizards/wizard9.txt
|
|
41
|
-
tests/test_db_magic.py
|
|
45
|
+
tests/test_db_magic.py
|
|
46
|
+
tests/test_utils.py
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
".gitignore",
|
|
4
4
|
".gitlab-ci.yml",
|
|
5
5
|
".python-version",
|
|
6
|
+
"AGENTS.md",
|
|
7
|
+
"CLAUDE.md",
|
|
6
8
|
"LICENSE",
|
|
7
9
|
"MANIFEST.in",
|
|
8
10
|
"README.md",
|
|
@@ -16,6 +18,8 @@
|
|
|
16
18
|
"spellbook/__init__.py",
|
|
17
19
|
"spellbook/db_magic.py",
|
|
18
20
|
"spellbook/drive_magic.py",
|
|
21
|
+
"spellbook/exceptions.py",
|
|
22
|
+
"spellbook/py.typed",
|
|
19
23
|
"spellbook/sheet_magic.py",
|
|
20
24
|
"spellbook/stats_magic.py",
|
|
21
25
|
"spellbook/utils.py",
|
|
@@ -33,6 +37,7 @@
|
|
|
33
37
|
"spellbook/wizards/wizard8.txt",
|
|
34
38
|
"spellbook/wizards/wizard9.txt",
|
|
35
39
|
"spellbook_config.yaml",
|
|
36
|
-
"tests/test_db_magic.py"
|
|
40
|
+
"tests/test_db_magic.py",
|
|
41
|
+
"tests/test_utils.py"
|
|
37
42
|
]
|
|
38
43
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Spellbook: connect to databases, spreadsheets and Drive, and run analyses."""
|
|
2
|
+
|
|
3
|
+
from spellbook.exceptions import (
|
|
4
|
+
AuthenticationError,
|
|
5
|
+
ConfigError,
|
|
6
|
+
ConnectionFailedError,
|
|
7
|
+
DatabaseNotFoundError,
|
|
8
|
+
QueryError,
|
|
9
|
+
SpellbookError,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
'SpellbookError',
|
|
14
|
+
'ConfigError',
|
|
15
|
+
'DatabaseNotFoundError',
|
|
16
|
+
'AuthenticationError',
|
|
17
|
+
'ConnectionFailedError',
|
|
18
|
+
'QueryError',
|
|
19
|
+
]
|