framework-py 0.1.0.dev0__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.
Files changed (63) hide show
  1. framework/__init__.py +250 -0
  2. framework/__main__.py +89 -0
  3. framework/_assets/hooks/pre-commit +59 -0
  4. framework/_assets/init.sql +63 -0
  5. framework/_assets/schema/layer.v1.json +605 -0
  6. framework/_assets/schema/layers.v1.json +791 -0
  7. framework/_assets/schema/spec.v1.json +604 -0
  8. framework/_log.py +29 -0
  9. framework/_paths.py +45 -0
  10. framework/_secretservice.py +69 -0
  11. framework/_time.py +11 -0
  12. framework/_wincred.py +105 -0
  13. framework/_yaml.py +90 -0
  14. framework/ai.py +204 -0
  15. framework/bootstrap.py +206 -0
  16. framework/cache.py +335 -0
  17. framework/catalog.py +193 -0
  18. framework/cloud.py +899 -0
  19. framework/config_home.py +194 -0
  20. framework/connections.py +442 -0
  21. framework/console.py +41 -0
  22. framework/create.py +579 -0
  23. framework/data.py +519 -0
  24. framework/db.py +222 -0
  25. framework/dotenv.py +79 -0
  26. framework/errors.py +39 -0
  27. framework/global_config.py +238 -0
  28. framework/hooks.py +364 -0
  29. framework/integrity.py +22 -0
  30. framework/notebooks.py +384 -0
  31. framework/outputs.py +324 -0
  32. framework/packages.py +276 -0
  33. framework/project.py +52 -0
  34. framework/py.typed +0 -0
  35. framework/quarto.py +163 -0
  36. framework/scaffold.py +269 -0
  37. framework/scratch.py +108 -0
  38. framework/security.py +646 -0
  39. framework/settings.py +439 -0
  40. framework/spec/__init__.py +0 -0
  41. framework/spec/check.py +211 -0
  42. framework/spec/merge.py +195 -0
  43. framework/spec/resolve.py +254 -0
  44. framework/storage.py +516 -0
  45. framework/templates/defaults.yml +144 -0
  46. framework/templates/gitignore/course +120 -0
  47. framework/templates/gitignore/presentation +114 -0
  48. framework/templates/gitignore/project +108 -0
  49. framework/templates/gitignore/sensitive +139 -0
  50. framework/templates/skills/framework-data/SKILL.md +47 -0
  51. framework/templates/skills/framework-outputs/SKILL.md +68 -0
  52. framework/templates/skills/framework-packages/SKILL.md +33 -0
  53. framework/templates/skills/framework-sensitive-data/SKILL.md +38 -0
  54. framework/templates/skills/framework-workflow/SKILL.md +58 -0
  55. framework/templates/stubs/notebook-default.ipynb +34 -0
  56. framework/templates/stubs/notebook-default.qmd +43 -0
  57. framework/templates/stubs/notebook-minimal.qmd +15 -0
  58. framework/templates/stubs/notebook-revealjs.qmd +34 -0
  59. framework/templates/stubs/script-default.py +18 -0
  60. framework_py-0.1.0.dev0.dist-info/METADATA +111 -0
  61. framework_py-0.1.0.dev0.dist-info/RECORD +63 -0
  62. framework_py-0.1.0.dev0.dist-info/WHEEL +4 -0
  63. framework_py-0.1.0.dev0.dist-info/licenses/LICENSE +21 -0
framework/__init__.py ADDED
@@ -0,0 +1,250 @@
1
+ """Framework: structured data science projects.
2
+
3
+ Import it as ``import framework as fw``. The functions here match the R
4
+ package's names, so the same documentation and skills apply in both
5
+ languages.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from importlib.metadata import PackageNotFoundError, version
11
+
12
+ from framework._time import now
13
+ from framework.ai import ai_skills_update
14
+ from framework.bootstrap import BootstrapResult, bootstrap
15
+ from framework.cache import (
16
+ cache,
17
+ cache_flush,
18
+ cache_forget,
19
+ cache_get,
20
+ cache_list,
21
+ cache_remember,
22
+ )
23
+ from framework.catalog import DataInfo, data_info, data_list
24
+ from framework.cloud import (
25
+ CloudError,
26
+ cloud_login,
27
+ cloud_logout,
28
+ cloud_settings,
29
+ cloud_status,
30
+ cloud_sync,
31
+ data_verify,
32
+ ledger_list,
33
+ ledger_push,
34
+ )
35
+ from framework.config_home import (
36
+ config_dir,
37
+ config_sync,
38
+ global_settings,
39
+ project_list,
40
+ project_register,
41
+ project_unregister,
42
+ )
43
+ from framework.connections import (
44
+ Connection,
45
+ connections_list,
46
+ db_connect,
47
+ db_drivers_status,
48
+ db_execute,
49
+ db_list,
50
+ db_query,
51
+ db_transaction,
52
+ )
53
+ from framework.console import CapturedOutput, capture_output
54
+ from framework.create import (
55
+ new,
56
+ project_add_directory,
57
+ project_create,
58
+ project_create_from_layers,
59
+ )
60
+ from framework.data import data_add, data_read, data_save
61
+ from framework.errors import (
62
+ CacheWarning,
63
+ DataChangedWarning,
64
+ DataNotFoundError,
65
+ FrameworkError,
66
+ FrameworkWarning,
67
+ IntegrityError,
68
+ ProjectNotFoundError,
69
+ SettingsError,
70
+ UnsupportedFormatError,
71
+ )
72
+ from framework.global_config import (
73
+ configure_global,
74
+ framework_template_path,
75
+ load_settings_catalog,
76
+ read_framework_template,
77
+ reset_framework_template,
78
+ write_framework_template,
79
+ )
80
+ from framework.hooks import (
81
+ git_hooks_disable,
82
+ git_hooks_enable,
83
+ git_hooks_install,
84
+ git_hooks_list,
85
+ git_hooks_uninstall,
86
+ )
87
+ from framework.notebooks import (
88
+ make_notebook,
89
+ make_presentation,
90
+ make_qmd,
91
+ make_revealjs,
92
+ make_script,
93
+ stubs_list,
94
+ stubs_path,
95
+ stubs_publish,
96
+ )
97
+ from framework.outputs import (
98
+ result_list,
99
+ save_figure,
100
+ save_model,
101
+ save_notebook,
102
+ save_report,
103
+ save_table,
104
+ )
105
+ from framework.packages import (
106
+ PackagesStatus,
107
+ package_add,
108
+ packages_install,
109
+ packages_list,
110
+ packages_restore,
111
+ packages_snapshot,
112
+ packages_status,
113
+ packages_update,
114
+ )
115
+ from framework.project import project_root
116
+ from framework.quarto import quarto_generate_all, quarto_regenerate
117
+ from framework.scaffold import project_info, scaffold, status
118
+ from framework.scratch import scratch_capture, scratch_clean
119
+ from framework.security import AuditResult, git_security_audit
120
+ from framework.settings import settings, settings_read, settings_write
121
+ from framework.spec.check import spec_check
122
+ from framework.spec.merge import SpecError
123
+ from framework.spec.resolve import ResolvedSpec, spec_resolve
124
+ from framework.storage import (
125
+ publish,
126
+ publish_data,
127
+ publish_dir,
128
+ publish_list,
129
+ publish_notebook,
130
+ storage_test,
131
+ )
132
+
133
+ try:
134
+ __version__ = version("framework-py")
135
+ except PackageNotFoundError: # pragma: no cover (running from a source tree)
136
+ __version__ = "0.0.0"
137
+
138
+ __all__ = [
139
+ "AuditResult",
140
+ "BootstrapResult",
141
+ "CacheWarning",
142
+ "CapturedOutput",
143
+ "CloudError",
144
+ "Connection",
145
+ "DataChangedWarning",
146
+ "DataInfo",
147
+ "DataNotFoundError",
148
+ "FrameworkError",
149
+ "FrameworkWarning",
150
+ "IntegrityError",
151
+ "PackagesStatus",
152
+ "ProjectNotFoundError",
153
+ "ResolvedSpec",
154
+ "SettingsError",
155
+ "SpecError",
156
+ "UnsupportedFormatError",
157
+ "__version__",
158
+ "ai_skills_update",
159
+ "bootstrap",
160
+ "cache",
161
+ "cache_flush",
162
+ "cache_forget",
163
+ "cache_get",
164
+ "cache_list",
165
+ "cache_remember",
166
+ "capture_output",
167
+ "cloud_login",
168
+ "cloud_logout",
169
+ "cloud_settings",
170
+ "cloud_status",
171
+ "cloud_sync",
172
+ "config_dir",
173
+ "config_sync",
174
+ "configure_global",
175
+ "connections_list",
176
+ "data_add",
177
+ "data_info",
178
+ "data_list",
179
+ "data_read",
180
+ "data_save",
181
+ "data_verify",
182
+ "db_connect",
183
+ "db_drivers_status",
184
+ "db_execute",
185
+ "db_list",
186
+ "db_query",
187
+ "db_transaction",
188
+ "framework_template_path",
189
+ "git_hooks_disable",
190
+ "git_hooks_enable",
191
+ "git_hooks_install",
192
+ "git_hooks_list",
193
+ "git_hooks_uninstall",
194
+ "git_security_audit",
195
+ "global_settings",
196
+ "ledger_list",
197
+ "ledger_push",
198
+ "load_settings_catalog",
199
+ "make_notebook",
200
+ "make_presentation",
201
+ "make_qmd",
202
+ "make_revealjs",
203
+ "make_script",
204
+ "new",
205
+ "now",
206
+ "package_add",
207
+ "packages_install",
208
+ "packages_list",
209
+ "packages_restore",
210
+ "packages_snapshot",
211
+ "packages_status",
212
+ "packages_update",
213
+ "project_add_directory",
214
+ "project_create",
215
+ "project_create_from_layers",
216
+ "project_info",
217
+ "project_list",
218
+ "project_register",
219
+ "project_root",
220
+ "project_unregister",
221
+ "publish",
222
+ "publish_data",
223
+ "publish_dir",
224
+ "publish_list",
225
+ "publish_notebook",
226
+ "quarto_generate_all",
227
+ "quarto_regenerate",
228
+ "read_framework_template",
229
+ "reset_framework_template",
230
+ "result_list",
231
+ "save_figure",
232
+ "save_model",
233
+ "save_notebook",
234
+ "save_report",
235
+ "save_table",
236
+ "scaffold",
237
+ "scratch_capture",
238
+ "scratch_clean",
239
+ "settings",
240
+ "settings_read",
241
+ "settings_write",
242
+ "spec_check",
243
+ "spec_resolve",
244
+ "status",
245
+ "storage_test",
246
+ "stubs_list",
247
+ "stubs_path",
248
+ "stubs_publish",
249
+ "write_framework_template",
250
+ ]
framework/__main__.py ADDED
@@ -0,0 +1,89 @@
1
+ """Command-line entry points: ``python -m framework``.
2
+
3
+ python -m framework create --from-layers FILE --path DIR [--interactive]
4
+ python -m framework create --name NAME --path DIR [--type TYPE] [--interactive]
5
+ python -m framework bootstrap [--path DIR] [--interactive] [--no-packages]
6
+ python -m framework hook pre-commit
7
+
8
+ Exit codes: 0 on success, 1 on failure (the message is on stderr), and 2 for
9
+ a usage error.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import argparse
15
+ import sys
16
+ from collections.abc import Sequence
17
+
18
+ from framework.errors import FrameworkError
19
+
20
+
21
+ def _parser() -> argparse.ArgumentParser:
22
+ parser = argparse.ArgumentParser(prog="python -m framework", description="Framework for Python")
23
+ commands = parser.add_subparsers(dest="command", required=True)
24
+
25
+ create = commands.add_parser("create", help="create a Python project")
26
+ source = create.add_mutually_exclusive_group(required=True)
27
+ source.add_argument(
28
+ "--from-layers", metavar="FILE", help="a layers file (framework.pub format)"
29
+ )
30
+ source.add_argument("--name", help="project name; your defaults apply, as with framework.new()")
31
+ create.add_argument("--path", required=True, help="the project folder to create")
32
+ create.add_argument(
33
+ "--type",
34
+ choices=["project", "project_sensitive", "course", "presentation", "bare"],
35
+ help="project type, with --name (default: bare, or asked with --interactive)",
36
+ )
37
+ create.add_argument(
38
+ "--interactive",
39
+ action="store_true",
40
+ help="prompt for missing answers, initialize git, and register the project",
41
+ )
42
+
43
+ bootstrap = commands.add_parser("bootstrap", help="prepare a cloned project")
44
+ bootstrap.add_argument("--path", default=".", help="the project folder (default: .)")
45
+ bootstrap.add_argument("--interactive", action="store_true", help="ask for empty .env values")
46
+ bootstrap.add_argument(
47
+ "--no-packages", action="store_true", help="skip installing packages with uv"
48
+ )
49
+
50
+ hook = commands.add_parser("hook", help="run a git hook's checks (called by the hook)")
51
+ hook.add_argument("name", choices=["pre-commit"])
52
+ return parser
53
+
54
+
55
+ def main(argv: Sequence[str] | None = None) -> int:
56
+ parser = _parser()
57
+ args = parser.parse_args(argv)
58
+ if args.command == "hook":
59
+ from framework.hooks import hook_run
60
+
61
+ return hook_run(args.name)
62
+ if args.command == "create" and args.from_layers and args.type:
63
+ parser.error("--type can only be used with --name")
64
+ try:
65
+ if args.command == "bootstrap":
66
+ from framework.bootstrap import bootstrap
67
+
68
+ path = bootstrap(
69
+ args.path, interactive=args.interactive, packages=not args.no_packages
70
+ ).root
71
+ elif args.from_layers:
72
+ from framework.create import project_create_from_layers
73
+
74
+ path = project_create_from_layers(
75
+ args.from_layers, args.path, interactive=args.interactive
76
+ )
77
+ else:
78
+ from framework.create import new
79
+
80
+ path = new(args.name, args.path, args.type, interactive=args.interactive)
81
+ except FrameworkError as error:
82
+ print(f"Error: {error}", file=sys.stderr)
83
+ return 1
84
+ print(path)
85
+ return 0
86
+
87
+
88
+ if __name__ == "__main__":
89
+ sys.exit(main())
@@ -0,0 +1,59 @@
1
+ #!/bin/sh
2
+ # Framework pre-commit hook (shared by R, Python, and fw)
3
+ # Managed by Framework. Configure it with git_hooks_*() in R or Python.
4
+ #
5
+ # The checks themselves live in the language packages, which read
6
+ # git.hooks in settings.yml to decide what to run. This script only finds a
7
+ # runtime. A project with no usable runtime fails the commit, because the
8
+ # hook guards against committing restricted data. Bypass with
9
+ # `git commit --no-verify` only when you know the commit is safe.
10
+
11
+ set -u
12
+
13
+ cd "$(git rev-parse --show-toplevel)" || exit 1
14
+
15
+ run_r() {
16
+ command -v Rscript >/dev/null 2>&1 || return 127
17
+ Rscript -e 'quit(status = framework:::.hook_run("pre-commit"))'
18
+ }
19
+
20
+ run_python() {
21
+ if [ -x .venv/bin/python ]; then
22
+ .venv/bin/python -m framework hook pre-commit
23
+ elif [ -x .venv/Scripts/python.exe ]; then
24
+ .venv/Scripts/python.exe -m framework hook pre-commit
25
+ elif [ -f uv.lock ] && command -v uv >/dev/null 2>&1; then
26
+ uv run --frozen python -m framework hook pre-commit
27
+ elif command -v python3 >/dev/null 2>&1; then
28
+ python3 -m framework hook pre-commit
29
+ else
30
+ return 127
31
+ fi
32
+ }
33
+
34
+ has_rproj() {
35
+ for f in ./*.Rproj; do
36
+ [ -e "$f" ] && return 0
37
+ done
38
+ return 1
39
+ }
40
+
41
+ # fw's detection plus *.Rproj, which every R-built project has even without
42
+ # renv. R markers are checked first; a project with both runs the first
43
+ # runtime that is installed.
44
+ status=127
45
+ if [ -f renv.lock ] || [ -f .Rprofile ] || has_rproj; then
46
+ run_r
47
+ status=$?
48
+ fi
49
+ if [ "$status" -eq 127 ] && { [ -f pyproject.toml ] || [ -f uv.lock ] || [ -d .venv ]; }; then
50
+ run_python
51
+ status=$?
52
+ fi
53
+
54
+ if [ "$status" -eq 127 ]; then
55
+ echo "Framework hook: no R or Python runtime with framework installed was found." >&2
56
+ echo "Install one, or run 'git commit --no-verify' if you are sure this commit is safe." >&2
57
+ exit 1
58
+ fi
59
+ exit "$status"
@@ -0,0 +1,63 @@
1
+ CREATE TABLE results (
2
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
3
+ name TEXT UNIQUE,
4
+ type TEXT,
5
+ public BOOLEAN,
6
+ blind BOOLEAN,
7
+ comment TEXT,
8
+ hash TEXT,
9
+ last_read_at DATETIME,
10
+ created_at DATETIME,
11
+ updated_at DATETIME,
12
+ deleted_at DATETIME
13
+ );
14
+
15
+ CREATE TABLE data (
16
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
17
+ name TEXT UNIQUE,
18
+ path TEXT,
19
+ type TEXT,
20
+ delimiter TEXT,
21
+ locked BOOLEAN,
22
+ encrypted BOOLEAN,
23
+ hash TEXT,
24
+ last_read_at DATETIME,
25
+ created_at DATETIME,
26
+ updated_at DATETIME,
27
+ deleted_at DATETIME
28
+ );
29
+
30
+ CREATE TABLE cache (
31
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
32
+ name TEXT UNIQUE,
33
+ file_path TEXT,
34
+ hash TEXT,
35
+ expire_at DATETIME,
36
+ last_read_at DATETIME,
37
+ created_at DATETIME,
38
+ updated_at DATETIME,
39
+ deleted_at DATETIME
40
+ );
41
+
42
+ CREATE TABLE connections (
43
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
44
+ name TEXT UNIQUE,
45
+ driver TEXT,
46
+ host TEXT,
47
+ port INTEGER,
48
+ database TEXT,
49
+ schema TEXT,
50
+ user TEXT,
51
+ password TEXT,
52
+ last_used_at DATETIME,
53
+ created_at DATETIME,
54
+ updated_at DATETIME,
55
+ deleted_at DATETIME
56
+ );
57
+
58
+ CREATE TABLE meta (
59
+ key TEXT PRIMARY KEY,
60
+ value TEXT,
61
+ created_at DATETIME,
62
+ updated_at DATETIME
63
+ );