squirrels 0.4.0__py3-none-any.whl → 0.5.0__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.
Potentially problematic release.
This version of squirrels might be problematic. Click here for more details.
- dateutils/__init__.py +6 -0
- dateutils/_enums.py +25 -0
- squirrels/dateutils.py → dateutils/_implementation.py +58 -111
- dateutils/types.py +6 -0
- squirrels/__init__.py +13 -11
- squirrels/_api_routes/__init__.py +5 -0
- squirrels/_api_routes/auth.py +271 -0
- squirrels/_api_routes/base.py +165 -0
- squirrels/_api_routes/dashboards.py +150 -0
- squirrels/_api_routes/data_management.py +145 -0
- squirrels/_api_routes/datasets.py +257 -0
- squirrels/_api_routes/oauth2.py +298 -0
- squirrels/_api_routes/project.py +252 -0
- squirrels/_api_server.py +256 -450
- squirrels/_arguments/__init__.py +0 -0
- squirrels/_arguments/init_time_args.py +108 -0
- squirrels/_arguments/run_time_args.py +147 -0
- squirrels/_auth.py +960 -0
- squirrels/_command_line.py +126 -45
- squirrels/_compile_prompts.py +147 -0
- squirrels/_connection_set.py +48 -26
- squirrels/_constants.py +68 -38
- squirrels/_dashboards.py +160 -0
- squirrels/_data_sources.py +570 -0
- squirrels/_dataset_types.py +84 -0
- squirrels/_exceptions.py +29 -0
- squirrels/_initializer.py +177 -80
- squirrels/_logging.py +115 -0
- squirrels/_manifest.py +208 -79
- squirrels/_model_builder.py +69 -0
- squirrels/_model_configs.py +74 -0
- squirrels/_model_queries.py +52 -0
- squirrels/_models.py +926 -367
- squirrels/_package_data/base_project/.env +42 -0
- squirrels/_package_data/base_project/.env.example +42 -0
- squirrels/_package_data/base_project/assets/expenses.db +0 -0
- squirrels/_package_data/base_project/connections.yml +16 -0
- squirrels/_package_data/base_project/dashboards/dashboard_example.py +34 -0
- squirrels/_package_data/base_project/dashboards/dashboard_example.yml +22 -0
- squirrels/{package_data → _package_data}/base_project/docker/.dockerignore +5 -2
- squirrels/{package_data → _package_data}/base_project/docker/Dockerfile +3 -3
- squirrels/{package_data → _package_data}/base_project/docker/compose.yml +1 -1
- squirrels/_package_data/base_project/duckdb_init.sql +10 -0
- squirrels/{package_data/base_project/.gitignore → _package_data/base_project/gitignore} +3 -2
- squirrels/_package_data/base_project/macros/macros_example.sql +17 -0
- squirrels/_package_data/base_project/models/builds/build_example.py +26 -0
- squirrels/_package_data/base_project/models/builds/build_example.sql +16 -0
- squirrels/_package_data/base_project/models/builds/build_example.yml +57 -0
- squirrels/_package_data/base_project/models/dbviews/dbview_example.sql +12 -0
- squirrels/_package_data/base_project/models/dbviews/dbview_example.yml +26 -0
- squirrels/_package_data/base_project/models/federates/federate_example.py +37 -0
- squirrels/_package_data/base_project/models/federates/federate_example.sql +19 -0
- squirrels/_package_data/base_project/models/federates/federate_example.yml +65 -0
- squirrels/_package_data/base_project/models/sources.yml +38 -0
- squirrels/{package_data → _package_data}/base_project/parameters.yml +56 -40
- squirrels/_package_data/base_project/pyconfigs/connections.py +14 -0
- squirrels/{package_data → _package_data}/base_project/pyconfigs/context.py +21 -40
- squirrels/_package_data/base_project/pyconfigs/parameters.py +141 -0
- squirrels/_package_data/base_project/pyconfigs/user.py +44 -0
- squirrels/_package_data/base_project/seeds/seed_categories.yml +15 -0
- squirrels/_package_data/base_project/seeds/seed_subcategories.csv +15 -0
- squirrels/_package_data/base_project/seeds/seed_subcategories.yml +21 -0
- squirrels/_package_data/base_project/squirrels.yml.j2 +61 -0
- squirrels/_package_data/templates/dataset_results.html +112 -0
- squirrels/_package_data/templates/oauth_login.html +271 -0
- squirrels/_package_data/templates/squirrels_studio.html +20 -0
- squirrels/_package_loader.py +8 -4
- squirrels/_parameter_configs.py +104 -103
- squirrels/_parameter_options.py +348 -0
- squirrels/_parameter_sets.py +57 -47
- squirrels/_parameters.py +1664 -0
- squirrels/_project.py +721 -0
- squirrels/_py_module.py +7 -5
- squirrels/_schemas/__init__.py +0 -0
- squirrels/_schemas/auth_models.py +167 -0
- squirrels/_schemas/query_param_models.py +75 -0
- squirrels/{_api_response_models.py → _schemas/response_models.py} +126 -47
- squirrels/_seeds.py +35 -16
- squirrels/_sources.py +110 -0
- squirrels/_utils.py +248 -73
- squirrels/_version.py +1 -1
- squirrels/arguments.py +7 -0
- squirrels/auth.py +4 -0
- squirrels/connections.py +3 -0
- squirrels/dashboards.py +2 -81
- squirrels/data_sources.py +14 -631
- squirrels/parameter_options.py +13 -348
- squirrels/parameters.py +14 -1266
- squirrels/types.py +16 -0
- squirrels-0.5.0.dist-info/METADATA +113 -0
- squirrels-0.5.0.dist-info/RECORD +97 -0
- {squirrels-0.4.0.dist-info → squirrels-0.5.0.dist-info}/WHEEL +1 -1
- squirrels-0.5.0.dist-info/entry_points.txt +3 -0
- {squirrels-0.4.0.dist-info → squirrels-0.5.0.dist-info/licenses}/LICENSE +1 -1
- squirrels/_authenticator.py +0 -85
- squirrels/_dashboards_io.py +0 -61
- squirrels/_environcfg.py +0 -84
- squirrels/arguments/init_time_args.py +0 -40
- squirrels/arguments/run_time_args.py +0 -208
- squirrels/package_data/assets/favicon.ico +0 -0
- squirrels/package_data/assets/index.css +0 -1
- squirrels/package_data/assets/index.js +0 -58
- squirrels/package_data/base_project/assets/expenses.db +0 -0
- squirrels/package_data/base_project/connections.yml +0 -7
- squirrels/package_data/base_project/dashboards/dashboard_example.py +0 -32
- squirrels/package_data/base_project/dashboards.yml +0 -10
- squirrels/package_data/base_project/env.yml +0 -29
- squirrels/package_data/base_project/models/dbviews/dbview_example.py +0 -47
- squirrels/package_data/base_project/models/dbviews/dbview_example.sql +0 -22
- squirrels/package_data/base_project/models/federates/federate_example.py +0 -21
- squirrels/package_data/base_project/models/federates/federate_example.sql +0 -3
- squirrels/package_data/base_project/pyconfigs/auth.py +0 -45
- squirrels/package_data/base_project/pyconfigs/connections.py +0 -19
- squirrels/package_data/base_project/pyconfigs/parameters.py +0 -95
- squirrels/package_data/base_project/seeds/seed_subcategories.csv +0 -15
- squirrels/package_data/base_project/squirrels.yml.j2 +0 -94
- squirrels/package_data/templates/index.html +0 -18
- squirrels/project.py +0 -378
- squirrels/user_base.py +0 -55
- squirrels-0.4.0.dist-info/METADATA +0 -117
- squirrels-0.4.0.dist-info/RECORD +0 -60
- squirrels-0.4.0.dist-info/entry_points.txt +0 -4
- /squirrels/{package_data → _package_data}/base_project/assets/weather.db +0 -0
- /squirrels/{package_data → _package_data}/base_project/seeds/seed_categories.csv +0 -0
- /squirrels/{package_data → _package_data}/base_project/tmp/.gitignore +0 -0
squirrels/types.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from ._data_sources import DataSource
|
|
2
|
+
|
|
3
|
+
from ._parameter_options import ParameterOption
|
|
4
|
+
|
|
5
|
+
from ._parameters import Parameter, TextValue
|
|
6
|
+
|
|
7
|
+
from ._dataset_types import DatasetMetadata, DatasetResult
|
|
8
|
+
|
|
9
|
+
from ._dashboards import Dashboard
|
|
10
|
+
|
|
11
|
+
from ._parameter_configs import ParameterConfigBase
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"DataSource", "ParameterOption", "Parameter", "TextValue",
|
|
15
|
+
"DatasetMetadata", "DatasetResult", "Dashboard", "ParameterConfigBase"
|
|
16
|
+
]
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: squirrels
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Squirrels - API Framework for Data Analytics
|
|
5
|
+
Project-URL: Homepage, https://squirrels-analytics.github.io
|
|
6
|
+
Project-URL: Repository, https://github.com/squirrels-analytics/squirrels
|
|
7
|
+
Project-URL: Documentation, https://squirrels-analytics.github.io
|
|
8
|
+
Author-email: Tim Huang <tim.yuting@hotmail.com>
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
14
|
+
Classifier: Typing :: Typed
|
|
15
|
+
Requires-Python: ~=3.10
|
|
16
|
+
Requires-Dist: authlib<2,>=1.5.2
|
|
17
|
+
Requires-Dist: bcrypt<5,>=4.0.1
|
|
18
|
+
Requires-Dist: cachetools>=5.3.2
|
|
19
|
+
Requires-Dist: duckdb<2,>=1.4.0
|
|
20
|
+
Requires-Dist: fastapi<1,>=0.112.1
|
|
21
|
+
Requires-Dist: gitpython<4,>=3.1.41
|
|
22
|
+
Requires-Dist: inquirer<4,>=3.2.1
|
|
23
|
+
Requires-Dist: itsdangerous<3,>=2.2.0
|
|
24
|
+
Requires-Dist: jinja2<4,>=3.1.3
|
|
25
|
+
Requires-Dist: libpass<2,>=1.9.0
|
|
26
|
+
Requires-Dist: matplotlib<4,>=3.8.3
|
|
27
|
+
Requires-Dist: mcp>=1.13.1
|
|
28
|
+
Requires-Dist: pandas<3,>=2.1.4
|
|
29
|
+
Requires-Dist: polars<2,>=1.14.0
|
|
30
|
+
Requires-Dist: pyarrow>=19.0.1
|
|
31
|
+
Requires-Dist: pydantic<3,>=2.8.2
|
|
32
|
+
Requires-Dist: pyjwt<3,>=2.8.0
|
|
33
|
+
Requires-Dist: python-dotenv<2,>=1.0.1
|
|
34
|
+
Requires-Dist: python-multipart<1,>=0.0.9
|
|
35
|
+
Requires-Dist: pyyaml<7,>=6.0.1
|
|
36
|
+
Requires-Dist: sqlalchemy<3,>=2.0.25
|
|
37
|
+
Requires-Dist: sqlglot>=26.12.1
|
|
38
|
+
Requires-Dist: uvicorn<1,>=0.30.6
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
# Squirrels
|
|
42
|
+
|
|
43
|
+
Squirrels is an API framework that lets you create REST APIs for dynamic data analytics!
|
|
44
|
+
|
|
45
|
+
**Documentation**: <a href="https://squirrels-analytics.github.io/" target="_blank">https://squirrels-analytics.github.io/</a>
|
|
46
|
+
|
|
47
|
+
**Source Code**: <a href="https://github.com/squirrels-analytics/squirrels" target="_blank">https://github.com/squirrels-analytics/squirrels</a>
|
|
48
|
+
|
|
49
|
+
## Table of Contents
|
|
50
|
+
|
|
51
|
+
- [Main Features](#main-features)
|
|
52
|
+
- [License](#license)
|
|
53
|
+
- [Contributing to Squirrels](#contributing-to-squirrels)
|
|
54
|
+
- [Setup](#setup)
|
|
55
|
+
- [Testing](#testing)
|
|
56
|
+
- [Project Structure](#project-structure)
|
|
57
|
+
|
|
58
|
+
## Main Features
|
|
59
|
+
|
|
60
|
+
Here are a few of the things that Squirrels can do:
|
|
61
|
+
|
|
62
|
+
- Connect to any database by specifying its SQLAlchemy url (in `squirrels.yml`) or by using its native connector library in python (in `connections.py`).
|
|
63
|
+
- Configure API routes for datasets (in `squirrels.yml`) without writing code.
|
|
64
|
+
- Configure parameter widgets (types include single-select, multi-select, date, number, etc.) for your datasets (in `parameters.py`).
|
|
65
|
+
- Use SQL templates (templated with Jinja, like dbt) or python functions (that return a Python dataframe in polars or pandas) to define dynamic query logic based on parameter selections.
|
|
66
|
+
- Query multiple databases and join the results together in a final view in one API endpoint/dataset!
|
|
67
|
+
- Test your API endpoints with Squirrels Studio or by a command line that generates rendered sql queries and results as files (for a given set of parameter selections).
|
|
68
|
+
- Define User model (in `user.py`) and authorize privacy scope per dataset (in `squirrels.yml`). The user's attributes can even be used in your query logic!
|
|
69
|
+
- Serve dataset metadata and results to AI agents via MCP (Model Context Protocol)
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
|
|
73
|
+
Squirrels is released under the Apache 2.0 license.
|
|
74
|
+
|
|
75
|
+
See the file LICENSE for more details.
|
|
76
|
+
|
|
77
|
+
## Contributing to Squirrels
|
|
78
|
+
|
|
79
|
+
The sections below describe how to set up your local environment for Squirrels development and run unit tests. A high level overview of the project structure is also provided.
|
|
80
|
+
|
|
81
|
+
### Setup
|
|
82
|
+
|
|
83
|
+
This project requires python version 3.10 or above to be installed. It also uses the python package manager `uv`. Information on setting up poetry can be found at: https://docs.astral.sh/uv/getting-started/installation/.
|
|
84
|
+
|
|
85
|
+
Then, to install all dependencies in a virtual environment, run:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
uv sync
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
And activate the virtual environment with:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
source .venv/bin/activate
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
To confirm that the setup worked, run the following to show the help page for all Squirrels CLI commands:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
sqrl -h
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Testing
|
|
104
|
+
|
|
105
|
+
Run `uv run pytest`. Or if you have the virtual environment activated, simply run `pytest`.
|
|
106
|
+
|
|
107
|
+
### Project Structure
|
|
108
|
+
|
|
109
|
+
From the root of the git repo, the source code can be found in the `squirrels` folder and unit tests can be found in the `tests` folder.
|
|
110
|
+
|
|
111
|
+
To understand what a specific Squirrels command is doing, start from the `_command_line.py` file as your entry point.
|
|
112
|
+
|
|
113
|
+
The library version is maintained in both the `pyproject.toml` and the `squirrels/_version.py` files.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
dateutils/__init__.py,sha256=dq4VSlJ5ztaDPdvYBRAvXSyanT_CZif3I4O0YVmWfa8,277
|
|
2
|
+
dateutils/_enums.py,sha256=WBrnLqta_iMMhGMEn24cCO1Vlr7bST0E8oEfAL3z0P8,373
|
|
3
|
+
dateutils/_implementation.py,sha256=PVJAdNolDdTpCZXwvokKoMIZzTSNpCUvZlVLWpMeSho,15173
|
|
4
|
+
dateutils/types.py,sha256=xcRwBoftQi-uHM1tlVTW5xgN84qdrqCo5tQT7A34S8Y,124
|
|
5
|
+
squirrels/__init__.py,sha256=nvzVVd5yHYbpQAfyKdQGDMxZpW7wVq3uUlpg4Urc_ws,320
|
|
6
|
+
squirrels/_api_server.py,sha256=kGqVF-QGG7VoaR9VxOO-0wuj8OvbbNV7ZsL34VAQ0O0,17454
|
|
7
|
+
squirrels/_auth.py,sha256=2yEYrK9-w-mtRPUkv486nic0WTVfb_UIbWl7DiGtSyg,43572
|
|
8
|
+
squirrels/_command_line.py,sha256=wTJr_WahULV8iZwUyhJIiNTGFd0YByprpo9DBDyczpU,12797
|
|
9
|
+
squirrels/_compile_prompts.py,sha256=XIbRciSXak6NuDD4KedeDoUV5YSHvctQGk6HMxKchBw,5562
|
|
10
|
+
squirrels/_connection_set.py,sha256=qSNcFdEKRcgZyr9OEqAuoIZBk-DY_C_aL_F7OE9GDK4,4457
|
|
11
|
+
squirrels/_constants.py,sha256=SuAaceUN95BLdOt500A8fUx88dvwm-v-WBj3v87z4qM,4035
|
|
12
|
+
squirrels/_dashboards.py,sha256=FF6KG-GXohR5Hp-BMYRwEZ2hUQ_jbkFpcK5epwoD64w,4891
|
|
13
|
+
squirrels/_data_sources.py,sha256=jiTzYsECny4KBnalzuHYy6wQo4YXubJesDLH_a1M3dc,26433
|
|
14
|
+
squirrels/_dataset_types.py,sha256=J9nhLVZ1eOk_qfqjEXRaXQTB9sqyZcE_iUad0NNstgU,2742
|
|
15
|
+
squirrels/_exceptions.py,sha256=-hfDZoV-JhvuNRAERTFg-5NFjr0uNk4i9ogNgJfAyyc,1171
|
|
16
|
+
squirrels/_initializer.py,sha256=9BDMGVbZLdRnV9erzqw1FvpKBlWRk8-uiSsl9UioxJ0,14249
|
|
17
|
+
squirrels/_logging.py,sha256=t0stGreMPXGRAB5cL2so2cmlMXpMSqRc0uepi2mooqE,4004
|
|
18
|
+
squirrels/_manifest.py,sha256=S4TicbPm5-4u9gEocbeO1dKb8CgTV8rsR5RERFjpkU0,12779
|
|
19
|
+
squirrels/_model_builder.py,sha256=gOAcN_C0dfAvBe9YTM3QpdiA_2VeatRSHsALp2OVdJA,2844
|
|
20
|
+
squirrels/_model_configs.py,sha256=eJne5L-QLv42s7uodUOOASMRn1NbzGbiBmwMllkO5C4,3303
|
|
21
|
+
squirrels/_model_queries.py,sha256=2fl07feHtzddBpiXUWGfNBYeoN_EZ7K5mAz2Jc-vCvs,1087
|
|
22
|
+
squirrels/_models.py,sha256=KiGjr1rgU4p4UX4c8fZR6CQ86w7qvSyEEbNnu3Hkby0,52213
|
|
23
|
+
squirrels/_package_loader.py,sha256=xcIur5Z38OWd-OVjsueFstVV567zlkK9UBnLS4NawJY,1158
|
|
24
|
+
squirrels/_parameter_configs.py,sha256=K7p_pIobT-gPZ99ewzLKV1aUzhnmfbGO7WjuzgjhDM8,24867
|
|
25
|
+
squirrels/_parameter_options.py,sha256=HatFFPyHrZo2ct3BS-FCHPzNqcIst4dVh14SgkhhFyw,16874
|
|
26
|
+
squirrels/_parameter_sets.py,sha256=hwzgC4sm6KMIASGlaLlhg-UheH5p0qIKU483pZRLRBU,10275
|
|
27
|
+
squirrels/_parameters.py,sha256=Ta929_a-b8r1miDKXT_SZtHT_d8jJ2W5ABvkXL7k074,76372
|
|
28
|
+
squirrels/_project.py,sha256=7vhYrDFL_Bgpj3JLAA25VfNa9UEQdqGGFYbCJKWQgeo,37010
|
|
29
|
+
squirrels/_py_module.py,sha256=NW86sv1oYWh8oMqgGh2oIUwuFcLXoq3cwEuKRxPfEwY,2683
|
|
30
|
+
squirrels/_seeds.py,sha256=Q6ihhou5PBfdH2mKhWEkDOfpdi1CmftUtv8Yz3S18dg,2216
|
|
31
|
+
squirrels/_sources.py,sha256=3tgI5FtMDzFtWBiHa2vF3x4Qaj5Pnu6hK_xSGBAcORI,5102
|
|
32
|
+
squirrels/_utils.py,sha256=HKDv-Z4_lEUaKyOOhsDtdf-lAOA6aR4Iw0bHg_aKT4k,14710
|
|
33
|
+
squirrels/_version.py,sha256=M8aFbJ4vlAi3Sk9b7leRuEfkNBjkkX5S_F9lA4h8GK4,105
|
|
34
|
+
squirrels/arguments.py,sha256=6rWDwv8jbimBUYv4peE2kEuFzOQ4jn9Tr_QXUmEj0_o,324
|
|
35
|
+
squirrels/auth.py,sha256=oX5-0QT4aKiUSyImKkLtPLYoki1SVNgaU3KBQzIizP4,193
|
|
36
|
+
squirrels/connections.py,sha256=FVK0se1LUAzsapPZYlWc1q6jTJ6hiU2RuYinBEvZ5a0,122
|
|
37
|
+
squirrels/dashboards.py,sha256=egYWL7jV9hvyxZ0vFziQb4q3sKDLfE_TNJN5niQbzX4,98
|
|
38
|
+
squirrels/data_sources.py,sha256=P3VIGWqspXLlxKUTDXc5u5eJ2yQoo4rvy9RGEb_OIbQ,347
|
|
39
|
+
squirrels/parameter_options.py,sha256=5ucgTWvdwefurMrk7xiJhmRn6fbBXXOhQdH7Mbnm-1I,382
|
|
40
|
+
squirrels/parameters.py,sha256=TJcsjtj0Kg0F69I5Zk7r0uDJC7p8duk3gtjXjcQPE40,366
|
|
41
|
+
squirrels/types.py,sha256=O4SMn9mqeV3wSEr_RIT1CKjumbqcbmZ6wk0tnXcxYHQ,437
|
|
42
|
+
squirrels/_api_routes/__init__.py,sha256=-oGMDfM5Qo7NDiwpjHt8BKVZNNZ4798-NZCJzYCUdbc,106
|
|
43
|
+
squirrels/_api_routes/auth.py,sha256=AvQbvyjrgEgN0kWPuBs8rgfJpsDL9ETFQbeFJ9S6qVw,15206
|
|
44
|
+
squirrels/_api_routes/base.py,sha256=TgzRB_yJ62xdIa90RRJfUbxtV_bWc6wsbNGn7A1rrMM,7772
|
|
45
|
+
squirrels/_api_routes/dashboards.py,sha256=dY-FFO2zJA2g9QLw7I98hXzurJHrYe2FTdPGxKPRMD8,8904
|
|
46
|
+
squirrels/_api_routes/data_management.py,sha256=oT_4etdDH55jI3fGbFkBgED9R2ji003S2HzSSc9JGpU,8607
|
|
47
|
+
squirrels/_api_routes/datasets.py,sha256=v-qWCvLiJIKeuzDLXEoerK_By_02iQdvhDvNkKpMYW8,15256
|
|
48
|
+
squirrels/_api_routes/oauth2.py,sha256=JXx5svIz0gVt6SHFOG-Ql9QcoJIizQ5IVFPPnZ5V1Bg,14827
|
|
49
|
+
squirrels/_api_routes/project.py,sha256=LxCkKmlrt7fojpRDRVl7ac-FstWyIIRM-uQWGXIE06w,14146
|
|
50
|
+
squirrels/_arguments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
|
+
squirrels/_arguments/init_time_args.py,sha256=Y5gVbrU_yd1r5JtAChcL1xTpfz0i8gwNtZMe5QBT2mc,3511
|
|
52
|
+
squirrels/_arguments/run_time_args.py,sha256=bnibUxUHnLvKR1tlri0WCuqRnYKXi0mJ8R_rhIDEil0,5100
|
|
53
|
+
squirrels/_package_data/base_project/.env,sha256=KfUwxOG5KFicU1E-FkP8EyQRvAx5tAqXbZwtZxBPf08,1619
|
|
54
|
+
squirrels/_package_data/base_project/.env.example,sha256=XPqYRUVCim1MnPU8iJe5y_sRpp9HObDrJk7NFd-SSao,1656
|
|
55
|
+
squirrels/_package_data/base_project/connections.yml,sha256=eBIbTQqyp5FlxDs0HyQ_NuE2Nx87EW53Qp1Y_c1ygv0,1093
|
|
56
|
+
squirrels/_package_data/base_project/duckdb_init.sql,sha256=PYUiQZajja-TG7EU1__PoiyDajgngEil4Lxenv5FWT0,284
|
|
57
|
+
squirrels/_package_data/base_project/gitignore,sha256=B9OEkQ_j9fZGA2IAyVUvXeylxpya-AUwzLzqzMN4Bfc,155
|
|
58
|
+
squirrels/_package_data/base_project/parameters.yml,sha256=XJpbJ-kpgEnUBM45pN57Y9MOatPx5j-r2vM8_T4sw3U,7103
|
|
59
|
+
squirrels/_package_data/base_project/squirrels.yml.j2,sha256=8wOaGd1jiEfbUJFAQcdbLQioonsU_UDUaRYyJ3dKm3U,1910
|
|
60
|
+
squirrels/_package_data/base_project/assets/expenses.db,sha256=aO0QdApW9ad8LRc73MW1o3eimryzmOAH5vz9Vc3dWK0,77824
|
|
61
|
+
squirrels/_package_data/base_project/assets/weather.db,sha256=dsHPO36gQdZ4ULAA726Hg3jp8a1dCdig1DhrGg8wTeg,86016
|
|
62
|
+
squirrels/_package_data/base_project/dashboards/dashboard_example.py,sha256=jd6Sjs69QK4zBJlRnKaloati1cU5Oby1Pzg9bUxY0ZY,1637
|
|
63
|
+
squirrels/_package_data/base_project/dashboards/dashboard_example.yml,sha256=fIm56M3J1eWp9_zDcc5R04pSLbHgdjm2X3XZgpB0SaQ,417
|
|
64
|
+
squirrels/_package_data/base_project/docker/.dockerignore,sha256=IN0ZmxwLdmYlw6I2ziTdzXkTbZWCUyV4kfUI9_lDz-A,201
|
|
65
|
+
squirrels/_package_data/base_project/docker/Dockerfile,sha256=DBOfPajd7ikNr1Qg08TcQmQmWDYamlxoDv7u6FSWElE,470
|
|
66
|
+
squirrels/_package_data/base_project/docker/compose.yml,sha256=xMAjfJeNVv49ypMGxR8bG27P5JSbPTNMR7UeGTHGyeA,105
|
|
67
|
+
squirrels/_package_data/base_project/macros/macros_example.sql,sha256=kJMAxL9ihCd-ULPOfHkqt8TDIMcH6QAOFFw89VplGL0,508
|
|
68
|
+
squirrels/_package_data/base_project/models/sources.yml,sha256=axBHFoJtWjAcklZyibrr7zhyDRATxi9r_bGjI09Kulk,1816
|
|
69
|
+
squirrels/_package_data/base_project/models/builds/build_example.py,sha256=QqNicirKK_JXn44QGrp48Ods5iGfAnoD_PZN5u-YLzQ,1022
|
|
70
|
+
squirrels/_package_data/base_project/models/builds/build_example.sql,sha256=52tqAIzFWJgX13CP4HNeBd5BFy7Z929Z0XxfLgGKthM,508
|
|
71
|
+
squirrels/_package_data/base_project/models/builds/build_example.yml,sha256=kUd6j5u8qU6UD294AowPBHFOb8yYDA_dxWlCIBrPl90,1407
|
|
72
|
+
squirrels/_package_data/base_project/models/dbviews/dbview_example.sql,sha256=RMET1ni2qeEFysxy3lSFHQOxYkfMHvs6G3JUzPhfklU,268
|
|
73
|
+
squirrels/_package_data/base_project/models/dbviews/dbview_example.yml,sha256=lofWnI-fMN6L0q2Yxt6X-4nl6LHv4ZYggYYpRgFQZXI,930
|
|
74
|
+
squirrels/_package_data/base_project/models/federates/federate_example.py,sha256=PvRmdgMaBjatiUclk8L4YvOAENoyb-CnkTJPerLvfLg,1459
|
|
75
|
+
squirrels/_package_data/base_project/models/federates/federate_example.sql,sha256=-i6a4GzHDsiSsKmbsnftnMWh8l2BP447Eh2Qab3KEdo,616
|
|
76
|
+
squirrels/_package_data/base_project/models/federates/federate_example.yml,sha256=FpSVZV7xNSH5iUAXMkyd_jynFxWryutz5Izlgsq3aP0,2279
|
|
77
|
+
squirrels/_package_data/base_project/pyconfigs/connections.py,sha256=IRL9D2HG9pdwp12S896b0Q-hKnFfVck8we1q5sifX10,601
|
|
78
|
+
squirrels/_package_data/base_project/pyconfigs/context.py,sha256=KN5hw23ydMqKLr7IEPHXsBTULZ9a63gECIGTpl1Qg9w,3543
|
|
79
|
+
squirrels/_package_data/base_project/pyconfigs/parameters.py,sha256=-UlrCcuW2NqlNlEnYttQ-S09Lq5IpNkAVP5FNR2e33g,4794
|
|
80
|
+
squirrels/_package_data/base_project/pyconfigs/user.py,sha256=KWfYO7IeGO3qaM9_hJnHMSovG55PvzqHQ6Jc8oKBPcE,1764
|
|
81
|
+
squirrels/_package_data/base_project/seeds/seed_categories.csv,sha256=jppjf1nOIxy7-bi5lJn5CVqmnLfJHHq0ABgp6UqbXnw,104
|
|
82
|
+
squirrels/_package_data/base_project/seeds/seed_categories.yml,sha256=NZ4BVvYYCEq6OnjRLrE_WOMhYsW0BQhRPWOgUchzdp4,435
|
|
83
|
+
squirrels/_package_data/base_project/seeds/seed_subcategories.csv,sha256=Tta1oIgnc2nukNMDlUkIErRKNH_8YT5EPp1A2kQKcow,327
|
|
84
|
+
squirrels/_package_data/base_project/seeds/seed_subcategories.yml,sha256=QTgw8Eld-p6Kntf53FyXyn7-7vKYI7IOJVu-Lr-FHCY,583
|
|
85
|
+
squirrels/_package_data/base_project/tmp/.gitignore,sha256=XImoqcWvJY0C0L_TWCx1ljvqU7qh9fUTJmK4ACCmNFI,13
|
|
86
|
+
squirrels/_package_data/templates/dataset_results.html,sha256=Y-1xtw4ZzHcoW1vfWR1amnFJqFUMvwJDY3eeY7bfKJw,2793
|
|
87
|
+
squirrels/_package_data/templates/oauth_login.html,sha256=LmR7zbjFUYxWIhB9nhIkoGDmqUgEtSXCqo0pKrWqwXw,7108
|
|
88
|
+
squirrels/_package_data/templates/squirrels_studio.html,sha256=M0X3DP5dLRNvO6aJYSasbjWa4NbpsicrxAWV8Ephkyw,785
|
|
89
|
+
squirrels/_schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
90
|
+
squirrels/_schemas/auth_models.py,sha256=PEDr3jPmaQo7tigjhjyQZ6BYWafbzMUX-jZdo3jRtMU,6392
|
|
91
|
+
squirrels/_schemas/query_param_models.py,sha256=y9ZYa2QLS_1tKN7ODSR0DBOWU-M6cz4niAq5qAAklok,4488
|
|
92
|
+
squirrels/_schemas/response_models.py,sha256=3OVelx3DZmmze7RKmCFzukvHt1nIiLWfmzxSoCNpY50,15218
|
|
93
|
+
squirrels-0.5.0.dist-info/METADATA,sha256=FzhOxJIYUkCseGJJj1xFDAz6temOPRY0JUKMNXgA54E,4555
|
|
94
|
+
squirrels-0.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
95
|
+
squirrels-0.5.0.dist-info/entry_points.txt,sha256=i6vgjhJ3o_cdSFYofFcNY9DFMPr4MIcuwnkskSTXfJc,95
|
|
96
|
+
squirrels-0.5.0.dist-info/licenses/LICENSE,sha256=qqERuumQtQVsMrEXvJHuecJvV2sLxbleEubd_Zk8dY8,11338
|
|
97
|
+
squirrels-0.5.0.dist-info/RECORD,,
|
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
|
187
187
|
identification within third-party archives.
|
|
188
188
|
|
|
189
|
-
Copyright
|
|
189
|
+
Copyright 2025 Tim Huang
|
|
190
190
|
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
192
|
you may not use this file except in compliance with the License.
|
squirrels/_authenticator.py
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
from typing import Type, Optional
|
|
2
|
-
from datetime import datetime, timedelta, timezone
|
|
3
|
-
from jwt.exceptions import InvalidTokenError
|
|
4
|
-
import secrets, jwt
|
|
5
|
-
|
|
6
|
-
from . import _utils as u, _constants as c
|
|
7
|
-
from .arguments.run_time_args import AuthArgs
|
|
8
|
-
from ._py_module import PyModule
|
|
9
|
-
from .user_base import User, WrongPassword
|
|
10
|
-
from ._environcfg import EnvironConfig
|
|
11
|
-
from ._manifest import DatasetScope
|
|
12
|
-
from ._connection_set import ConnectionsArgs, ConnectionSet
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class Authenticator:
|
|
16
|
-
|
|
17
|
-
def __init__(self, base_path: str, env_cfg: EnvironConfig, conn_args: ConnectionsArgs, conn_set: ConnectionSet, token_expiry_minutes: int, *, auth_helper = None) -> None:
|
|
18
|
-
self.env_cfg = env_cfg
|
|
19
|
-
self.conn_args = conn_args
|
|
20
|
-
self.conn_set = conn_set
|
|
21
|
-
self.token_expiry_minutes = token_expiry_minutes
|
|
22
|
-
self.auth_helper = self._get_auth_helper(base_path, default_auth_helper=auth_helper)
|
|
23
|
-
self.user_cls: Type[User] = self.auth_helper.get_func_or_class("User", default_attr=User)
|
|
24
|
-
self.secret_key = self._get_secret_key()
|
|
25
|
-
self.algorithm = "HS256"
|
|
26
|
-
|
|
27
|
-
def _get_auth_helper(self, base_path: str, *, default_auth_helper = None):
|
|
28
|
-
auth_module_path = u.Path(base_path, c.PYCONFIGS_FOLDER, c.AUTH_FILE)
|
|
29
|
-
return PyModule(auth_module_path, default_class=default_auth_helper)
|
|
30
|
-
|
|
31
|
-
def _get_secret_key(self) -> str:
|
|
32
|
-
secret_key = self.env_cfg.get_secret(c.JWT_SECRET_KEY, default_factory=lambda: secrets.token_hex(32))
|
|
33
|
-
return str(secret_key)
|
|
34
|
-
|
|
35
|
-
def _get_auth_args(self, username: str, password: str):
|
|
36
|
-
connections = self.conn_set.get_engines_as_dict()
|
|
37
|
-
return AuthArgs(self.conn_args.proj_vars, self.conn_args.env_vars, self.conn_args._get_credential, connections, username, password)
|
|
38
|
-
|
|
39
|
-
def authenticate_user(self, username: str, password: str) -> Optional[User]:
|
|
40
|
-
get_user = self.auth_helper.get_func_or_class(c.GET_USER_FUNC, is_required=False)
|
|
41
|
-
try:
|
|
42
|
-
real_user = get_user(self._get_auth_args(username, password)) if get_user is not None else None
|
|
43
|
-
except Exception as e:
|
|
44
|
-
raise u.FileExecutionError(f'Failed to run "{c.GET_USER_FUNC}" in {c.AUTH_FILE}', e) from e
|
|
45
|
-
|
|
46
|
-
if isinstance(real_user, User):
|
|
47
|
-
return real_user
|
|
48
|
-
|
|
49
|
-
if not isinstance(real_user, WrongPassword):
|
|
50
|
-
fake_users = self.env_cfg.get_users()
|
|
51
|
-
if username in fake_users and secrets.compare_digest(fake_users[username].password, password):
|
|
52
|
-
fake_user = fake_users[username].model_dump()
|
|
53
|
-
fake_user.pop("username", "")
|
|
54
|
-
is_internal = fake_user.pop("is_internal", False)
|
|
55
|
-
try:
|
|
56
|
-
return self.user_cls.Create(username, is_internal=is_internal, **fake_user)
|
|
57
|
-
except Exception as e:
|
|
58
|
-
raise u.FileExecutionError(f'Failed to create user from User model in {c.AUTH_FILE}', e) from e
|
|
59
|
-
|
|
60
|
-
return None
|
|
61
|
-
|
|
62
|
-
def create_access_token(self, user: User) -> tuple[str, datetime]:
|
|
63
|
-
expire = datetime.now(timezone.utc) + timedelta(minutes=self.token_expiry_minutes)
|
|
64
|
-
to_encode = {**vars(user), "exp": expire}
|
|
65
|
-
encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
|
|
66
|
-
return encoded_jwt, expire
|
|
67
|
-
|
|
68
|
-
def get_user_from_token(self, token: Optional[str]) -> Optional[User]:
|
|
69
|
-
if token is not None:
|
|
70
|
-
try:
|
|
71
|
-
payload: dict = jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
|
|
72
|
-
payload.pop("exp")
|
|
73
|
-
return self.user_cls._FromDict(payload)
|
|
74
|
-
except InvalidTokenError:
|
|
75
|
-
return None
|
|
76
|
-
|
|
77
|
-
def can_user_access_scope(self, user: Optional[User], scope: DatasetScope) -> bool:
|
|
78
|
-
if user is None:
|
|
79
|
-
user_level = DatasetScope.PUBLIC
|
|
80
|
-
elif not user.is_internal:
|
|
81
|
-
user_level = DatasetScope.PROTECTED
|
|
82
|
-
else:
|
|
83
|
-
user_level = DatasetScope.PRIVATE
|
|
84
|
-
|
|
85
|
-
return user_level.value >= scope.value
|
squirrels/_dashboards_io.py
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
from typing import Type, TypeVar, Callable, Coroutine, Any
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
import inspect, os, time
|
|
4
|
-
|
|
5
|
-
from .arguments.run_time_args import DashboardArgs
|
|
6
|
-
from ._py_module import PyModule
|
|
7
|
-
from . import _constants as c, _utils as u, dashboards as d
|
|
8
|
-
|
|
9
|
-
T = TypeVar('T', bound=d.Dashboard)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@dataclass
|
|
13
|
-
class DashboardFunction:
|
|
14
|
-
dashboard_name: str
|
|
15
|
-
filepath: str
|
|
16
|
-
|
|
17
|
-
@property
|
|
18
|
-
def dashboard_func(self) -> Callable[[DashboardArgs], Coroutine[Any, Any, d.Dashboard]]:
|
|
19
|
-
if not hasattr(self, '_dashboard_func'):
|
|
20
|
-
module = PyModule(self.filepath)
|
|
21
|
-
self._dashboard_func = module.get_func_or_class(c.MAIN_FUNC)
|
|
22
|
-
return self._dashboard_func
|
|
23
|
-
|
|
24
|
-
def get_dashboard_format(self) -> str:
|
|
25
|
-
return_type = inspect.signature(self.dashboard_func).return_annotation
|
|
26
|
-
assert issubclass(return_type, d.Dashboard), f"Function must return Dashboard type"
|
|
27
|
-
if return_type == d.PngDashboard:
|
|
28
|
-
return c.PNG
|
|
29
|
-
elif return_type == d.HtmlDashboard:
|
|
30
|
-
return c.HTML
|
|
31
|
-
else:
|
|
32
|
-
raise NotImplementedError(f"Dashboard format {return_type} not supported")
|
|
33
|
-
|
|
34
|
-
async def get_dashboard(self, args: DashboardArgs, *, dashboard_type: Type[T] = d.Dashboard) -> T:
|
|
35
|
-
try:
|
|
36
|
-
dashboard = await self.dashboard_func(args)
|
|
37
|
-
assert isinstance(dashboard, dashboard_type), f"Function does not return expected Dashboard type: {dashboard_type}"
|
|
38
|
-
except (u.InvalidInputError, u.ConfigurationError, u.FileExecutionError) as e:
|
|
39
|
-
raise e
|
|
40
|
-
except Exception as e:
|
|
41
|
-
raise u.FileExecutionError(f'Failed to run "{c.MAIN_FUNC}" function for dashboard "{self.dashboard_name}"', e) from e
|
|
42
|
-
|
|
43
|
-
return dashboard
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class DashboardsIO:
|
|
47
|
-
|
|
48
|
-
@classmethod
|
|
49
|
-
def load_files(cls, logger: u.Logger, base_path: str) -> dict[str, DashboardFunction]:
|
|
50
|
-
start = time.time()
|
|
51
|
-
|
|
52
|
-
dashboards_by_name = {}
|
|
53
|
-
for dp, _, filenames in os.walk(u.Path(base_path, c.DASHBOARDS_FOLDER)):
|
|
54
|
-
for file in filenames:
|
|
55
|
-
filepath = os.path.join(dp, file)
|
|
56
|
-
file_stem, extension = os.path.splitext(file)
|
|
57
|
-
if extension == '.py':
|
|
58
|
-
dashboards_by_name[file_stem] = DashboardFunction(file_stem, filepath)
|
|
59
|
-
|
|
60
|
-
logger.log_activity_time("loading files for dashboards", start)
|
|
61
|
-
return dashboards_by_name
|
squirrels/_environcfg.py
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from typing import Any, Callable
|
|
3
|
-
from pydantic import BaseModel, Field, field_validator, ValidationError
|
|
4
|
-
import os, yaml, time
|
|
5
|
-
|
|
6
|
-
from . import _constants as c, _utils as u
|
|
7
|
-
|
|
8
|
-
_GLOBAL_SQUIRRELS_CFG_FILE = u.Path(os.path.expanduser('~'), '.squirrels', c.ENV_CONFIG_FILE)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class _UserConfig(BaseModel, extra="allow"):
|
|
12
|
-
username: str
|
|
13
|
-
password: str
|
|
14
|
-
is_internal: bool = False
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class _CredentialsConfig(BaseModel):
|
|
18
|
-
username: str
|
|
19
|
-
password: str
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class EnvironConfig(BaseModel):
|
|
23
|
-
users: dict[str, _UserConfig] = Field(default_factory=dict)
|
|
24
|
-
env_vars: dict[str, str] = Field(default_factory=dict)
|
|
25
|
-
credentials: dict[str, _CredentialsConfig] = Field(default_factory=dict)
|
|
26
|
-
secrets: dict[str, Any | None] = Field(default_factory=dict)
|
|
27
|
-
|
|
28
|
-
@field_validator("users", mode="before")
|
|
29
|
-
@classmethod
|
|
30
|
-
def inject_username(cls, users: dict[str, dict[str, Any]]) -> dict[str, dict[str, Any]]:
|
|
31
|
-
processed_users = {}
|
|
32
|
-
for username, user in users.items():
|
|
33
|
-
processed_users[username] = {"username": username, **user}
|
|
34
|
-
return processed_users
|
|
35
|
-
|
|
36
|
-
def get_users(self) -> dict[str, _UserConfig]:
|
|
37
|
-
return self.users.copy()
|
|
38
|
-
|
|
39
|
-
def get_all_env_vars(self) -> dict[str, str]:
|
|
40
|
-
return self.env_vars.copy()
|
|
41
|
-
|
|
42
|
-
def get_credential(self, key: str | None) -> tuple[str, str]:
|
|
43
|
-
if not key:
|
|
44
|
-
return "", ""
|
|
45
|
-
|
|
46
|
-
try:
|
|
47
|
-
credential = self.credentials[key]
|
|
48
|
-
except KeyError as e:
|
|
49
|
-
raise u.ConfigurationError(f'No credentials configured for "{key}"') from e
|
|
50
|
-
|
|
51
|
-
return credential.username, credential.password
|
|
52
|
-
|
|
53
|
-
def get_secret(self, key: str, default_factory: Callable[[], Any]) -> Any:
|
|
54
|
-
if self.secrets.get(key) is None:
|
|
55
|
-
self.secrets[key] = default_factory()
|
|
56
|
-
return self.secrets[key]
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
class EnvironConfigIO:
|
|
60
|
-
|
|
61
|
-
@classmethod
|
|
62
|
-
def load_from_file(cls, logger: u.Logger, base_path: str) -> EnvironConfig:
|
|
63
|
-
start = time.time()
|
|
64
|
-
def load_yaml(filename: str | Path) -> dict[str, dict]:
|
|
65
|
-
try:
|
|
66
|
-
with open(filename, 'r') as f:
|
|
67
|
-
return yaml.safe_load(f)
|
|
68
|
-
except FileNotFoundError:
|
|
69
|
-
return {}
|
|
70
|
-
|
|
71
|
-
master_env_config = load_yaml(_GLOBAL_SQUIRRELS_CFG_FILE)
|
|
72
|
-
proj_env_config = load_yaml(Path(base_path, c.ENV_CONFIG_FILE))
|
|
73
|
-
|
|
74
|
-
for key in proj_env_config:
|
|
75
|
-
master_env_config.setdefault(key, {})
|
|
76
|
-
master_env_config[key].update(proj_env_config[key])
|
|
77
|
-
|
|
78
|
-
try:
|
|
79
|
-
env_cfg = EnvironConfig(**master_env_config)
|
|
80
|
-
except ValidationError as e:
|
|
81
|
-
raise u.ConfigurationError(f"Failed to process {c.ENV_CONFIG_FILE} file. " + str(e)) from e
|
|
82
|
-
|
|
83
|
-
logger.log_activity_time(f"loading {c.ENV_CONFIG_FILE} file", start)
|
|
84
|
-
return env_cfg
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
from typing import Callable, Any
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
@dataclass
|
|
6
|
-
class BaseArguments:
|
|
7
|
-
_proj_vars: dict[str, Any]
|
|
8
|
-
_env_vars: dict[str, Any]
|
|
9
|
-
|
|
10
|
-
@property
|
|
11
|
-
def proj_vars(self) -> dict[str, Any]:
|
|
12
|
-
return self._proj_vars.copy()
|
|
13
|
-
|
|
14
|
-
@property
|
|
15
|
-
def env_vars(self) -> dict[str, Any]:
|
|
16
|
-
return self._env_vars.copy()
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@dataclass
|
|
20
|
-
class ConnectionsArgs(BaseArguments):
|
|
21
|
-
_get_credential: Callable[[str | None], tuple[str, str]]
|
|
22
|
-
|
|
23
|
-
def get_credential(self, key: str | None) -> tuple[str, str]:
|
|
24
|
-
"""
|
|
25
|
-
Return (username, password) tuple configured for credentials key in env.yaml
|
|
26
|
-
|
|
27
|
-
If key is None, returns tuple of empty strings ("", "")
|
|
28
|
-
|
|
29
|
-
Arguments:
|
|
30
|
-
key: The credentials key
|
|
31
|
-
|
|
32
|
-
Returns:
|
|
33
|
-
A tuple of 2 strings
|
|
34
|
-
"""
|
|
35
|
-
return self._get_credential(key)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@dataclass
|
|
39
|
-
class ParametersArgs(BaseArguments):
|
|
40
|
-
pass
|