framework-py 0.1.0.dev1__tar.gz → 0.1.0.dev3__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.
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/PKG-INFO +1 -1
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/pyproject.toml +2 -2
- framework_py-0.1.0.dev3/scripts/build_docs.py +377 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/scripts/sync_assets.py +2 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/__main__.py +6 -1
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/_assets/hooks/pre-commit +14 -6
- framework_py-0.1.0.dev3/src/framework/_assets/schema/docs-reference.v1.json +137 -0
- framework_py-0.1.0.dev3/src/framework/_pages.py +228 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/_paths.py +10 -0
- framework_py-0.1.0.dev3/src/framework/_private.py +69 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/ai.py +4 -1
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/bootstrap.py +8 -2
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/cache.py +20 -2
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/catalog.py +72 -20
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/cloud.py +80 -9
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/config_home.py +19 -2
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/connections.py +44 -1
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/console.py +6 -1
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/create.py +59 -14
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/data.py +43 -23
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/global_config.py +65 -5
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/hooks.py +47 -2
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/notebooks.py +94 -4
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/outputs.py +16 -1
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/packages.py +19 -1
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/project.py +7 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/quarto.py +44 -4
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/scaffold.py +17 -2
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/scratch.py +1 -1
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/security.py +13 -11
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/settings.py +56 -8
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/spec/check.py +10 -1
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/spec/resolve.py +7 -1
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/storage.py +178 -41
- {framework_py-0.1.0.dev1/tests/golden/no-assistants/expected/.agents → framework_py-0.1.0.dev3/src/framework/templates}/skills/framework-outputs/SKILL.md +5 -5
- {framework_py-0.1.0.dev1/tests/golden/project-sensitive/expected/.claude → framework_py-0.1.0.dev3/src/framework/templates}/skills/framework-sensitive-data/SKILL.md +3 -2
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/.claude/skills/framework-outputs/SKILL.md +5 -5
- {framework_py-0.1.0.dev1/src/framework/templates → framework_py-0.1.0.dev3/tests/golden/files-and-skills/expected/.claude}/skills/framework-outputs/SKILL.md +5 -5
- {framework_py-0.1.0.dev1/tests/golden/files-and-skills/expected/.claude → framework_py-0.1.0.dev3/tests/golden/no-assistants/expected/.agents}/skills/framework-outputs/SKILL.md +5 -5
- framework_py-0.1.0.dev3/tests/golden/presentation/expected/.claude/skills/framework-outputs/SKILL.md +68 -0
- framework_py-0.1.0.dev3/tests/golden/project/expected/.claude/skills/framework-outputs/SKILL.md +68 -0
- framework_py-0.1.0.dev3/tests/golden/project-sensitive/expected/.claude/skills/framework-outputs/SKILL.md +68 -0
- {framework_py-0.1.0.dev1/tests/golden/sensitive-facet → framework_py-0.1.0.dev3/tests/golden/project-sensitive}/expected/.claude/skills/framework-sensitive-data/SKILL.md +3 -2
- framework_py-0.1.0.dev3/tests/golden/removals-and-assistants/expected/.agents/skills/framework-outputs/SKILL.md +68 -0
- framework_py-0.1.0.dev3/tests/golden/sensitive-facet/expected/.claude/skills/framework-outputs/SKILL.md +68 -0
- {framework_py-0.1.0.dev1/src/framework/templates → framework_py-0.1.0.dev3/tests/golden/sensitive-facet/expected/.claude}/skills/framework-sensitive-data/SKILL.md +3 -2
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/integration/test_servers.py +1 -1
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_bootstrap.py +28 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_catalog.py +53 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_cloud.py +35 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_create.py +33 -1
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_data.py +66 -0
- framework_py-0.1.0.dev3/tests/test_docs_site.py +82 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_hooks.py +2 -1
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_package.py +2 -2
- framework_py-0.1.0.dev3/tests/test_pages.py +201 -0
- framework_py-0.1.0.dev3/tests/test_private.py +70 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_scaffold.py +45 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_security.py +60 -15
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_settings.py +36 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_storage.py +13 -12
- framework_py-0.1.0.dev1/tests/golden/presentation/expected/.claude/skills/framework-outputs/SKILL.md +0 -68
- framework_py-0.1.0.dev1/tests/golden/project/expected/.claude/skills/framework-outputs/SKILL.md +0 -68
- framework_py-0.1.0.dev1/tests/golden/project-sensitive/expected/.claude/skills/framework-outputs/SKILL.md +0 -68
- framework_py-0.1.0.dev1/tests/golden/removals-and-assistants/expected/.agents/skills/framework-outputs/SKILL.md +0 -68
- framework_py-0.1.0.dev1/tests/golden/sensitive-facet/expected/.claude/skills/framework-outputs/SKILL.md +0 -68
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/.gitignore +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/LICENSE +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/README.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/parity.toml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/scripts/__init__.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/scripts/check_parity.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/scripts/make_golden_trees.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/scripts/make_r_fixtures.R +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/__init__.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/_assets/init.sql +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/_assets/schema/layer.v1.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/_assets/schema/layers.v1.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/_assets/schema/spec.v1.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/_log.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/_secretservice.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/_time.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/_wincred.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/_yaml.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/db.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/dotenv.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/errors.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/integrity.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/py.typed +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/spec/__init__.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/spec/merge.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/templates/defaults.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/templates/gitignore/course +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/templates/gitignore/presentation +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/templates/gitignore/project +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/templates/gitignore/sensitive +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/templates/skills/framework-data/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/templates/skills/framework-packages/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/templates/skills/framework-workflow/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/templates/stubs/notebook-default.ipynb +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/templates/stubs/notebook-default.qmd +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/templates/stubs/notebook-minimal.qmd +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/templates/stubs/notebook-revealjs.qmd +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/src/framework/templates/stubs/script-default.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/__init__.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/conftest.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/.claude/skills/framework-data/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/.claude/skills/framework-outputs/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/.claude/skills/framework-packages/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/.claude/skills/framework-workflow/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/.env +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/.framework/spec.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/.gitignore +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/AGENTS.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/CLAUDE.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/docs/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/fixture-study.Rproj +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/fixture-study.code-workspace +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/framework.db +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/inputs/intermediate/r_clean.csv +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/inputs/private/ids.csv +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/inputs/raw/survey.csv +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/inputs/raw/visits.tsv +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/inputs/reference/test.dta +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/inputs/reference/test.sav +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/inputs/reference/test.xpt +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/notebooks/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/outputs/cache/r_value.rds +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/outputs/tables/r_summary.csv +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/scaffold.R +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/settings/ai.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/settings/author.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/settings/connections.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/settings/data.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/settings/git.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/settings/packages.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/settings/quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/settings/scaffold.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project/settings.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/fixtures/r_project.expected.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/bare/expected/.framework/spec.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/bare/expected/.gitignore +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/bare/expected/pyproject.toml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/bare/expected/settings.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/bare/layers.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/.claude/skills/framework-data/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/.claude/skills/framework-packages/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/.claude/skills/framework-workflow/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/.env +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/.framework/spec.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/.gitignore +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/AGENTS.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/CLAUDE.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/assignments/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/course_docs/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/golden-course.code-workspace +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/modules/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/pyproject.toml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/scaffold.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/settings.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/expected/slides/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/course/layers.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/.claude/skills/framework-data/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/.claude/skills/framework-packages/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/.claude/skills/framework-workflow/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/.claude/skills/study-protocol/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/.claude/skills/study-protocol/references/consort.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/.env +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/.framework/spec.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/.gitignore +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/AGENTS.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/CLAUDE.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/README.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/docs/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/golden-files-and-skills.code-workspace +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/notebooks/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/notes.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/pyproject.toml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/scaffold.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/settings/ai.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/settings/author.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/settings/connections.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/settings/data.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/settings/git.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/settings/quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/settings/scaffold.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/expected/settings.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/files-and-skills/layers.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/.agents/skills/framework-data/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/.agents/skills/framework-packages/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/.agents/skills/framework-workflow/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/.env +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/.framework/spec.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/.gitignore +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/AGENTS.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/docs/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/golden-no-assistants.code-workspace +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/notebooks/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/pyproject.toml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/scaffold.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/settings/ai.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/settings/author.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/settings/connections.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/settings/data.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/settings/git.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/settings/quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/settings/scaffold.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/expected/settings.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/no-assistants/layers.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/presentation/expected/.claude/skills/framework-data/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/presentation/expected/.claude/skills/framework-packages/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/presentation/expected/.claude/skills/framework-workflow/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/presentation/expected/.env +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/presentation/expected/.framework/spec.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/presentation/expected/.gitignore +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/presentation/expected/AGENTS.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/presentation/expected/CLAUDE.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/presentation/expected/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/presentation/expected/golden-presentation.code-workspace +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/presentation/expected/presentation.qmd +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/presentation/expected/pyproject.toml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/presentation/expected/scaffold.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/presentation/expected/settings.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/presentation/layers.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/.claude/skills/framework-data/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/.claude/skills/framework-packages/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/.claude/skills/framework-workflow/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/.env +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/.framework/spec.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/.gitignore +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/AGENTS.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/CLAUDE.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/docs/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/golden-project.code-workspace +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/notebooks/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/pyproject.toml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/scaffold.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/settings/ai.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/settings/author.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/settings/connections.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/settings/data.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/settings/git.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/settings/quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/settings/scaffold.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/expected/settings.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project/layers.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/.claude/settings.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/.claude/skills/framework-data/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/.claude/skills/framework-packages/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/.claude/skills/framework-workflow/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/.env +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/.framework/spec.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/.gitignore +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/AGENTS.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/CLAUDE.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/docs/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/golden-project-sensitive.code-workspace +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/notebooks/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/pyproject.toml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/scaffold.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/settings/ai.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/settings/author.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/settings/connections.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/settings/data.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/settings/git.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/settings/quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/settings/scaffold.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/expected/settings.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/project-sensitive/layers.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/.agents/skills/framework-data/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/.agents/skills/framework-packages/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/.agents/skills/framework-workflow/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/.env +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/.framework/spec.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/.github/copilot-instructions.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/.gitignore +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/AGENTS.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/docs/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/golden-removals-and-assistants.code-workspace +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/notebooks/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/pyproject.toml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/scaffold.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/settings/ai.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/settings/author.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/settings/connections.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/settings/data.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/settings/git.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/settings/quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/settings/scaffold.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/expected/settings.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/removals-and-assistants/layers.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/.claude/settings.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/.claude/skills/framework-data/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/.claude/skills/framework-packages/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/.claude/skills/framework-workflow/SKILL.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/.env +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/.framework/spec.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/.gitignore +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/AGENTS.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/CLAUDE.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/docs/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/golden-sensitive-facet.code-workspace +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/notebooks/_quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/pyproject.toml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/scaffold.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/settings/ai.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/settings/author.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/settings/connections.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/settings/data.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/settings/git.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/settings/quarto.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/settings/scaffold.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/expected/settings.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/golden/sensitive-facet/layers.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/integration/compose.yml +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/integration/mariadb/init.sql +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/integration/mysql/init.sql +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/integration/postgres/init.sql +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/integration/sqlserver/init.sql +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/integration/test_cloud_staging.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/shared/NAMESPACE +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/shared/conformance/README.md +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/shared/conformance/merge/atomic-entries.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/shared/conformance/merge/conflicts-and-labels.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/shared/conformance/merge/empty-sections.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/shared/conformance/merge/gitignore-and-assistants.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/shared/conformance/merge/maps-and-null-removal.json +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_cache.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_config_home.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_conformance.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_connections.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_db.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_dotenv.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_global_config.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_notebooks.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_outputs.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_packages.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_r_compat.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_r_live.py +0 -0
- {framework_py-0.1.0.dev1 → framework_py-0.1.0.dev3}/tests/test_secretservice.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: framework-py
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.dev3
|
|
4
4
|
Summary: Structured data science projects: a data catalog, integrity tracking, caching, outputs, and AI guardrails
|
|
5
5
|
Project-URL: Homepage, https://framework.pub
|
|
6
6
|
Project-URL: Source, https://github.com/frameworktools/framework-py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "framework-py"
|
|
3
|
-
version = "0.1.0.
|
|
3
|
+
version = "0.1.0.dev3"
|
|
4
4
|
description = "Structured data science projects: a data catalog, integrity tracking, caching, outputs, and AI guardrails"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -85,7 +85,7 @@ strict = true
|
|
|
85
85
|
files = ["src", "scripts"]
|
|
86
86
|
|
|
87
87
|
[[tool.mypy.overrides]]
|
|
88
|
-
module = ["pyarrow", "pyarrow.*", "keyring", "keyring.*", "secretstorage", "matplotlib", "matplotlib.*", "openpyxl", "pyreadstat", "boto3", "botocore", "botocore.*", "pyodbc", "pymysql", "psycopg", "psycopg.*", "duckdb"]
|
|
88
|
+
module = ["pyarrow", "pyarrow.*", "keyring", "keyring.*", "secretstorage", "jsonschema", "matplotlib", "matplotlib.*", "openpyxl", "pyreadstat", "boto3", "botocore", "botocore.*", "pyodbc", "pymysql", "psycopg", "psycopg.*", "duckdb"]
|
|
89
89
|
ignore_missing_imports = true
|
|
90
90
|
|
|
91
91
|
[tool.pytest.ini_options]
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"""Build the docs site archive for py.framework.pub.
|
|
2
|
+
|
|
3
|
+
Writes ``reference.json`` from the docstrings of everything in
|
|
4
|
+
``framework.__all__``, checks it against ``docs-reference.v1.json``, and
|
|
5
|
+
packs ``docs/site`` with it into ``docs-site.tar.gz``. The format is R's
|
|
6
|
+
``inst/schema/docs-site.md``.
|
|
7
|
+
|
|
8
|
+
python scripts/build_docs.py # check only
|
|
9
|
+
python scripts/build_docs.py --reference out.json # write reference.json
|
|
10
|
+
python scripts/build_docs.py --archive dist-docs/docs-site.tar.gz
|
|
11
|
+
|
|
12
|
+
Docstrings are Google style. The first line is the summary, the paragraphs
|
|
13
|
+
before the first section are the description, and ``Args:`` (or
|
|
14
|
+
``Attributes:`` for classes), ``Returns:``, and ``Raises:`` become
|
|
15
|
+
parameters, returns, and a section.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import argparse
|
|
21
|
+
import dataclasses
|
|
22
|
+
import inspect
|
|
23
|
+
import io
|
|
24
|
+
import json
|
|
25
|
+
import re
|
|
26
|
+
import sys
|
|
27
|
+
import tarfile
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
import jsonschema
|
|
32
|
+
import yaml
|
|
33
|
+
|
|
34
|
+
import framework
|
|
35
|
+
|
|
36
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
37
|
+
SITE = ROOT / "docs" / "site"
|
|
38
|
+
SCHEMA = ROOT / "src" / "framework" / "_assets" / "schema" / "docs-reference.v1.json"
|
|
39
|
+
|
|
40
|
+
_SECTION = re.compile(r"^(Args|Arguments|Attributes|Returns|Yields|Raises|Examples?|Notes?):\s*$")
|
|
41
|
+
_ITEM = re.compile(r"^(\*{0,2}[A-Za-z_][A-Za-z0-9_]*)(?:\s*\(([^)]*)\))?:\s*(.*)$")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class DocsError(Exception):
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# Docstrings -------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _markdown(text: str) -> str:
|
|
52
|
+
"""reStructuredText inline code to Markdown, linking exported names."""
|
|
53
|
+
exported = set(framework.__all__)
|
|
54
|
+
|
|
55
|
+
def code(match: re.Match[str]) -> str:
|
|
56
|
+
inner = match.group(1)
|
|
57
|
+
name = inner.removesuffix("()")
|
|
58
|
+
if name in exported:
|
|
59
|
+
return f"[`{inner}`](ref:{name})"
|
|
60
|
+
return f"`{inner}`"
|
|
61
|
+
|
|
62
|
+
return re.sub(r"``(.+?)``", code, text).strip()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _dedent_block(lines: list[str]) -> list[str]:
|
|
66
|
+
indents = [len(line) - len(line.lstrip()) for line in lines if line.strip()]
|
|
67
|
+
cut = min(indents, default=0)
|
|
68
|
+
return [line[cut:] for line in lines]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def parse_docstring(doc: str) -> dict[str, Any]:
|
|
72
|
+
"""Split a Google-style docstring into summary, description, and sections."""
|
|
73
|
+
lines = doc.strip().splitlines()
|
|
74
|
+
summary = lines[0].strip() if lines else ""
|
|
75
|
+
body: list[str] = []
|
|
76
|
+
sections: dict[str, list[str]] = {}
|
|
77
|
+
current: list[str] = body
|
|
78
|
+
for line in lines[1:]:
|
|
79
|
+
match = _SECTION.match(line)
|
|
80
|
+
if match:
|
|
81
|
+
key = {"Arguments": "Args", "Example": "Examples", "Notes": "Note"}.get(
|
|
82
|
+
match.group(1), match.group(1)
|
|
83
|
+
)
|
|
84
|
+
current = sections.setdefault(key, [])
|
|
85
|
+
continue
|
|
86
|
+
current.append(line)
|
|
87
|
+
paragraphs = re.split(r"\n\s*\n", "\n".join(body).strip())
|
|
88
|
+
paragraphs = [p for p in paragraphs if p.strip()]
|
|
89
|
+
return {
|
|
90
|
+
"summary": summary,
|
|
91
|
+
"description": "\n\n".join(paragraphs[:2]),
|
|
92
|
+
"details": "\n\n".join(paragraphs[2:]),
|
|
93
|
+
"sections": {key: _dedent_block(value) for key, value in sections.items()},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def parse_items(lines: list[str]) -> list[tuple[str, str]]:
|
|
98
|
+
"""``name: text`` items, with indented continuation lines folded in."""
|
|
99
|
+
items: list[tuple[str, list[str]]] = []
|
|
100
|
+
for line in lines:
|
|
101
|
+
if not line.strip():
|
|
102
|
+
if items:
|
|
103
|
+
items[-1][1].append("")
|
|
104
|
+
continue
|
|
105
|
+
match = _ITEM.match(line) if not line.startswith(" ") else None
|
|
106
|
+
if match:
|
|
107
|
+
items.append((match.group(1), [match.group(3)]))
|
|
108
|
+
elif items:
|
|
109
|
+
items[-1][1].append(line.strip())
|
|
110
|
+
return [
|
|
111
|
+
(name, re.sub(r"(?<!\n)\n(?!\n)", " ", "\n".join(text).strip())) for name, text in items
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# Entries ----------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _annotation(value: Any) -> str | None:
|
|
119
|
+
if value is inspect.Parameter.empty:
|
|
120
|
+
return None
|
|
121
|
+
return value if isinstance(value, str) else str(inspect.formatannotation(value))
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _prefix(param: inspect.Parameter) -> str:
|
|
125
|
+
if param.kind is inspect.Parameter.VAR_POSITIONAL:
|
|
126
|
+
return "*"
|
|
127
|
+
if param.kind is inspect.Parameter.VAR_KEYWORD:
|
|
128
|
+
return "**"
|
|
129
|
+
return ""
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _parameter(param: inspect.Parameter) -> str:
|
|
133
|
+
"""A parameter as written in the source, shortening long defaults."""
|
|
134
|
+
text = _prefix(param) + param.name
|
|
135
|
+
annotation = _annotation(param.annotation)
|
|
136
|
+
if annotation:
|
|
137
|
+
text += f": {annotation}"
|
|
138
|
+
if param.default is not param.empty:
|
|
139
|
+
default = repr(param.default)
|
|
140
|
+
if len(default) > 30:
|
|
141
|
+
default = f"{default[0]}...{default[-1]}" if default[0] in "([{" else "..."
|
|
142
|
+
text += f" = {default}" if annotation else f"={default}"
|
|
143
|
+
return text
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _usage(name: str, signature: inspect.Signature) -> str:
|
|
147
|
+
params: list[str] = []
|
|
148
|
+
for param in signature.parameters.values():
|
|
149
|
+
if param.name == "self":
|
|
150
|
+
continue
|
|
151
|
+
if param.kind == param.KEYWORD_ONLY and not any(p.startswith("*") for p in params):
|
|
152
|
+
params.append("*")
|
|
153
|
+
params.append(_parameter(param))
|
|
154
|
+
returns = _annotation(signature.return_annotation)
|
|
155
|
+
tail = f" -> {returns}" if returns else ""
|
|
156
|
+
one_line = f"{name}({', '.join(params)}){tail}"
|
|
157
|
+
if len(one_line) <= 88:
|
|
158
|
+
return one_line
|
|
159
|
+
inner = "".join(f" {p},\n" for p in params)
|
|
160
|
+
return f"{name}(\n{inner}){tail}"
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _signature(obj: Any) -> inspect.Signature | None:
|
|
164
|
+
try:
|
|
165
|
+
return inspect.signature(obj)
|
|
166
|
+
except (TypeError, ValueError):
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def build_entry(name: str, obj: Any, category: str, problems: list[str]) -> dict[str, Any]:
|
|
171
|
+
doc = inspect.getdoc(obj)
|
|
172
|
+
if not doc:
|
|
173
|
+
raise DocsError(f"{name} has no docstring")
|
|
174
|
+
parsed = parse_docstring(doc)
|
|
175
|
+
sections = parsed["sections"]
|
|
176
|
+
is_class = inspect.isclass(obj)
|
|
177
|
+
entry: dict[str, Any] = {
|
|
178
|
+
"id": name,
|
|
179
|
+
"name": name,
|
|
180
|
+
"kind": "class" if is_class else "function",
|
|
181
|
+
"category": category,
|
|
182
|
+
"summary": _markdown(parsed["summary"]),
|
|
183
|
+
}
|
|
184
|
+
for key in ("description", "details"):
|
|
185
|
+
if parsed[key]:
|
|
186
|
+
entry[key] = _markdown(parsed[key])
|
|
187
|
+
|
|
188
|
+
documented = dict(parse_items(sections.get("Attributes" if is_class else "Args", [])))
|
|
189
|
+
parameters: list[dict[str, Any]] = []
|
|
190
|
+
if is_class and dataclasses.is_dataclass(obj):
|
|
191
|
+
for field in dataclasses.fields(obj):
|
|
192
|
+
if field.name.startswith("_"):
|
|
193
|
+
continue
|
|
194
|
+
if field.name not in documented:
|
|
195
|
+
problems.append(f"{name}: attribute '{field.name}' is not documented")
|
|
196
|
+
item: dict[str, Any] = {
|
|
197
|
+
"name": field.name,
|
|
198
|
+
"description": _markdown(documented.get(field.name, "")),
|
|
199
|
+
}
|
|
200
|
+
if _annotation(field.type):
|
|
201
|
+
item["type"] = _annotation(field.type)
|
|
202
|
+
parameters.append(item)
|
|
203
|
+
elif not is_class:
|
|
204
|
+
signature = _signature(obj)
|
|
205
|
+
if signature is None:
|
|
206
|
+
raise DocsError(f"{name}: no signature")
|
|
207
|
+
entry["usage"] = _usage(name, signature)
|
|
208
|
+
for param in signature.parameters.values():
|
|
209
|
+
key = _prefix(param)
|
|
210
|
+
label = f"{key}{param.name}"
|
|
211
|
+
text = documented.get(label) or documented.get(param.name)
|
|
212
|
+
if text is None:
|
|
213
|
+
problems.append(f"{name}: parameter '{param.name}' is not documented")
|
|
214
|
+
item = {"name": label, "description": _markdown(text or "")}
|
|
215
|
+
if _annotation(param.annotation):
|
|
216
|
+
item["type"] = _annotation(param.annotation)
|
|
217
|
+
if param.default is not param.empty:
|
|
218
|
+
item["default"] = repr(param.default)
|
|
219
|
+
elif key == "":
|
|
220
|
+
item["required"] = True
|
|
221
|
+
parameters.append(item)
|
|
222
|
+
if parameters:
|
|
223
|
+
entry["parameters"] = parameters
|
|
224
|
+
|
|
225
|
+
for key in ("Returns", "Yields"):
|
|
226
|
+
if key in sections:
|
|
227
|
+
entry["returns"] = _markdown("\n".join(sections[key]))
|
|
228
|
+
if "Note" in sections:
|
|
229
|
+
entry["note"] = _markdown("\n".join(sections["Note"]))
|
|
230
|
+
if "Raises" in sections:
|
|
231
|
+
raises = "\n".join(
|
|
232
|
+
f"- `{error}`: {_markdown(text)}" for error, text in parse_items(sections["Raises"])
|
|
233
|
+
)
|
|
234
|
+
entry["sections"] = [{"title": "Raises", "content": raises}]
|
|
235
|
+
if "Examples" in sections:
|
|
236
|
+
code = "\n".join(sections["Examples"]).strip()
|
|
237
|
+
code = "\n".join(re.sub(r"^(>>> |\.\.\. )", "", line) for line in code.splitlines())
|
|
238
|
+
entry["examples"] = [{"code": code}]
|
|
239
|
+
return entry
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def build_reference(categories: list[dict[str, Any]], problems: list[str]) -> dict[str, Any]:
|
|
243
|
+
exported = list(framework.__all__)
|
|
244
|
+
placed: dict[str, str] = {}
|
|
245
|
+
for category in categories:
|
|
246
|
+
for name in category["names"]:
|
|
247
|
+
if name in placed:
|
|
248
|
+
raise DocsError(f"{name} is in both {placed[name]} and {category['id']}")
|
|
249
|
+
placed[name] = category["id"]
|
|
250
|
+
for name in category.get("common", []):
|
|
251
|
+
if name not in category["names"]:
|
|
252
|
+
raise DocsError(f"{category['id']}: common name {name} is not in its names")
|
|
253
|
+
documented = [name for name in exported if not name.startswith("__")]
|
|
254
|
+
missing = sorted(set(documented) - set(placed))
|
|
255
|
+
unknown = sorted(set(placed) - set(documented))
|
|
256
|
+
if missing or unknown:
|
|
257
|
+
raise DocsError(
|
|
258
|
+
f"categories.yml: missing {missing or 'none'}; not exported {unknown or 'none'}"
|
|
259
|
+
)
|
|
260
|
+
entries = [
|
|
261
|
+
build_entry(name, getattr(framework, name), placed[name], problems)
|
|
262
|
+
for category in categories
|
|
263
|
+
for name in category["names"]
|
|
264
|
+
]
|
|
265
|
+
return {
|
|
266
|
+
"schema": "docs-reference.v1",
|
|
267
|
+
"product": "py",
|
|
268
|
+
"version": framework.__version__,
|
|
269
|
+
"categories": [
|
|
270
|
+
{
|
|
271
|
+
key: value
|
|
272
|
+
for key, value in {
|
|
273
|
+
"id": category["id"],
|
|
274
|
+
"title": category["title"],
|
|
275
|
+
"description": category.get("description"),
|
|
276
|
+
"position": position,
|
|
277
|
+
"common": category.get("common"),
|
|
278
|
+
}.items()
|
|
279
|
+
if value is not None
|
|
280
|
+
}
|
|
281
|
+
for position, category in enumerate(categories, start=1)
|
|
282
|
+
],
|
|
283
|
+
"entries": entries,
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
# Site -------------------------------------------------------------------------
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _prose(markdown: str) -> str:
|
|
291
|
+
"""Markdown without fenced code blocks, whose lines are not headings or links."""
|
|
292
|
+
return re.sub(r"^(```|~~~).*?^\1[ \t]*$", "", markdown, flags=re.M | re.S)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def check_site(reference: dict[str, Any]) -> list[str]:
|
|
296
|
+
"""Check site.yml, the guides' front matter, and every guide:/ref: link."""
|
|
297
|
+
problems: list[str] = []
|
|
298
|
+
site = yaml.safe_load((SITE / "site.yml").read_text(encoding="utf-8"))
|
|
299
|
+
sections = {section["id"] for section in site["sections"]}
|
|
300
|
+
guides: dict[str, Path] = {}
|
|
301
|
+
for path in sorted((SITE / "guides").glob("*.md")):
|
|
302
|
+
text = path.read_text(encoding="utf-8")
|
|
303
|
+
match = re.match(r"\A---\n(.*?)\n---\n", text, re.S)
|
|
304
|
+
if not match:
|
|
305
|
+
problems.append(f"{path.name}: no front matter")
|
|
306
|
+
continue
|
|
307
|
+
meta = yaml.safe_load(match.group(1)) or {}
|
|
308
|
+
for key in ("id", "title", "description", "section", "position"):
|
|
309
|
+
if key not in meta:
|
|
310
|
+
problems.append(f"{path.name}: front matter is missing {key}")
|
|
311
|
+
if meta.get("section") not in sections:
|
|
312
|
+
problems.append(f"{path.name}: unknown section {meta.get('section')}")
|
|
313
|
+
if meta.get("id") != path.stem:
|
|
314
|
+
problems.append(f"{path.name}: id {meta.get('id')} does not match the file name")
|
|
315
|
+
if re.search(r"^# ", _prose(text[match.end() :]), re.M):
|
|
316
|
+
problems.append(f"{path.name}: guides have no h1")
|
|
317
|
+
guides[path.stem] = path
|
|
318
|
+
ids = {entry["id"] for entry in reference["entries"]}
|
|
319
|
+
texts = [(path.name, _prose(path.read_text(encoding="utf-8"))) for path in guides.values()]
|
|
320
|
+
texts.append(("reference.json", json.dumps(reference)))
|
|
321
|
+
for source, text in texts:
|
|
322
|
+
for kind, target in re.findall(r"\((guide|ref):([^)#\s]+)", text):
|
|
323
|
+
if kind == "guide" and target not in guides:
|
|
324
|
+
problems.append(f"{source}: no guide {target}")
|
|
325
|
+
if kind == "ref" and target not in ids:
|
|
326
|
+
problems.append(f"{source}: no reference entry {target}")
|
|
327
|
+
for image in re.findall(r"\]\((images/[^)\s]+)\)", text):
|
|
328
|
+
if not (SITE / image).is_file():
|
|
329
|
+
problems.append(f"{source}: missing {image}")
|
|
330
|
+
return problems
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def write_archive(reference: dict[str, Any], target: Path) -> None:
|
|
334
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
335
|
+
with tarfile.open(target, "w:gz") as archive:
|
|
336
|
+
for path in sorted(SITE.rglob("*")):
|
|
337
|
+
if path.is_file() and path.name != "categories.yml":
|
|
338
|
+
archive.add(path, arcname=f"docs-site/{path.relative_to(SITE).as_posix()}")
|
|
339
|
+
data = (json.dumps(reference, indent=2, ensure_ascii=False) + "\n").encode("utf-8")
|
|
340
|
+
info = tarfile.TarInfo("docs-site/reference.json")
|
|
341
|
+
info.size = len(data)
|
|
342
|
+
archive.addfile(info, io.BytesIO(data))
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def main(argv: list[str] | None = None) -> int:
|
|
346
|
+
parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
|
|
347
|
+
parser.add_argument("--reference", type=Path, help="write reference.json here")
|
|
348
|
+
parser.add_argument("--archive", type=Path, help="write docs-site.tar.gz here")
|
|
349
|
+
parser.add_argument("--schema", type=Path, default=SCHEMA, help="docs-reference.v1.json")
|
|
350
|
+
args = parser.parse_args(argv)
|
|
351
|
+
problems: list[str] = []
|
|
352
|
+
try:
|
|
353
|
+
categories = yaml.safe_load((SITE / "categories.yml").read_text(encoding="utf-8"))
|
|
354
|
+
reference = build_reference(categories, problems)
|
|
355
|
+
except DocsError as error:
|
|
356
|
+
print(f"error: {error}", file=sys.stderr)
|
|
357
|
+
return 1
|
|
358
|
+
schema = json.loads(args.schema.read_text(encoding="utf-8"))
|
|
359
|
+
errors = sorted(
|
|
360
|
+
jsonschema.Draft202012Validator(schema).iter_errors(reference), key=lambda e: e.path
|
|
361
|
+
)
|
|
362
|
+
problems += [f"reference.json: {list(e.path)}: {e.message}" for e in errors]
|
|
363
|
+
problems += check_site(reference)
|
|
364
|
+
if problems:
|
|
365
|
+
print("\n".join(f"error: {problem}" for problem in problems), file=sys.stderr)
|
|
366
|
+
return 1
|
|
367
|
+
if args.reference:
|
|
368
|
+
args.reference.write_text(json.dumps(reference, indent=2, ensure_ascii=False) + "\n")
|
|
369
|
+
if args.archive:
|
|
370
|
+
write_archive(reference, args.archive)
|
|
371
|
+
guides = len(list((SITE / "guides").glob("*.md")))
|
|
372
|
+
print(f"{len(reference['entries'])} reference entries and {guides} guides are valid")
|
|
373
|
+
return 0
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
if __name__ == "__main__":
|
|
377
|
+
sys.exit(main())
|
|
@@ -28,6 +28,8 @@ WORKTREE = "worktree"
|
|
|
28
28
|
# Runtime files go in the package; test-only files go in tests/shared.
|
|
29
29
|
ASSETS: dict[str, str] = {
|
|
30
30
|
"inst/schema/*.json": "src/framework/_assets/schema",
|
|
31
|
+
# The docs site format (docs/site, scripts/build_docs.py).
|
|
32
|
+
"inst/schema/docs-site.md": "docs",
|
|
31
33
|
"inst/templates/init.sql": "src/framework/_assets",
|
|
32
34
|
"inst/hooks/pre-commit": "src/framework/_assets/hooks",
|
|
33
35
|
"inst/conformance/README.md": "tests/shared/conformance",
|
|
@@ -37,7 +37,12 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
37
37
|
create.add_argument(
|
|
38
38
|
"--interactive",
|
|
39
39
|
action="store_true",
|
|
40
|
-
help=
|
|
40
|
+
help=(
|
|
41
|
+
"with --name, ask for the type when --type is missing (the project is "
|
|
42
|
+
"registered and gets a git repository either way, unless your defaults turn "
|
|
43
|
+
"git off); with --from-layers, initialize git and register the project "
|
|
44
|
+
"instead of running headless"
|
|
45
|
+
),
|
|
41
46
|
)
|
|
42
47
|
|
|
43
48
|
bootstrap = commands.add_parser("bootstrap", help="prepare a cloned project")
|
|
@@ -12,23 +12,31 @@ set -u
|
|
|
12
12
|
|
|
13
13
|
cd "$(git rev-parse --show-toplevel)" || exit 1
|
|
14
14
|
|
|
15
|
+
# Each runner returns 127 when its runtime or the framework package is
|
|
16
|
+
# missing, so the next runtime gets a turn and the message below can say why
|
|
17
|
+
|
|
15
18
|
run_r() {
|
|
16
19
|
command -v Rscript >/dev/null 2>&1 || return 127
|
|
20
|
+
# framework 1.x from CRAN has no shared hook entry point
|
|
21
|
+
probe='ok <- requireNamespace("framework", quietly = TRUE) && exists(".hook_run", envir = asNamespace("framework"))'
|
|
22
|
+
Rscript -e "$probe; quit(status = if (ok) 0 else 127)" >/dev/null 2>&1 || return 127
|
|
17
23
|
Rscript -e 'quit(status = framework:::.hook_run("pre-commit"))'
|
|
18
24
|
}
|
|
19
25
|
|
|
20
26
|
run_python() {
|
|
21
27
|
if [ -x .venv/bin/python ]; then
|
|
22
|
-
.venv/bin/python
|
|
28
|
+
set -- .venv/bin/python
|
|
23
29
|
elif [ -x .venv/Scripts/python.exe ]; then
|
|
24
|
-
.venv/Scripts/python.exe
|
|
30
|
+
set -- .venv/Scripts/python.exe
|
|
25
31
|
elif [ -f uv.lock ] && command -v uv >/dev/null 2>&1; then
|
|
26
|
-
uv run --frozen python
|
|
32
|
+
set -- uv run --frozen python
|
|
27
33
|
elif command -v python3 >/dev/null 2>&1; then
|
|
28
|
-
|
|
34
|
+
set -- python3
|
|
29
35
|
else
|
|
30
36
|
return 127
|
|
31
37
|
fi
|
|
38
|
+
"$@" -c 'import framework' >/dev/null 2>&1 || return 127
|
|
39
|
+
"$@" -m framework hook pre-commit
|
|
32
40
|
}
|
|
33
41
|
|
|
34
42
|
has_rproj() {
|
|
@@ -52,8 +60,8 @@ if [ "$status" -eq 127 ] && { [ -f pyproject.toml ] || [ -f uv.lock ] || [ -d .v
|
|
|
52
60
|
fi
|
|
53
61
|
|
|
54
62
|
if [ "$status" -eq 127 ]; then
|
|
55
|
-
echo "Framework hook:
|
|
56
|
-
echo "Install one, or run 'git commit --no-verify' if you are sure this commit is safe." >&2
|
|
63
|
+
echo "Framework hook: found neither R with framework 2.0 or later nor Python with framework-py." >&2
|
|
64
|
+
echo "Install one of them, or run 'git commit --no-verify' if you are sure this commit is safe." >&2
|
|
57
65
|
exit 1
|
|
58
66
|
fi
|
|
59
67
|
exit "$status"
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://framework.pub/schema/docs-reference.v1.json",
|
|
4
|
+
"title": "Framework docs reference",
|
|
5
|
+
"description": "The generated function or command reference for one product's docs site (docs/site/reference.json). Text fields hold CommonMark with GFM tables.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema", "product", "version", "categories", "entries"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema": { "const": "docs-reference.v1" },
|
|
11
|
+
"product": { "enum": ["r", "py", "cli"] },
|
|
12
|
+
"version": { "type": "string", "minLength": 1, "description": "The release this reference was built from, such as 2.0.0 or 0.1.0.dev1." },
|
|
13
|
+
"categories": {
|
|
14
|
+
"type": "array",
|
|
15
|
+
"items": { "$ref": "#/$defs/category" }
|
|
16
|
+
},
|
|
17
|
+
"entries": {
|
|
18
|
+
"type": "array",
|
|
19
|
+
"items": { "$ref": "#/$defs/entry" }
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"$defs": {
|
|
23
|
+
"id": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]*$",
|
|
26
|
+
"description": "URL-safe and unique within the product: /reference/{id}."
|
|
27
|
+
},
|
|
28
|
+
"markdown": { "type": "string" },
|
|
29
|
+
"category": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"required": ["id", "title", "position"],
|
|
32
|
+
"additionalProperties": false,
|
|
33
|
+
"properties": {
|
|
34
|
+
"id": { "$ref": "#/$defs/id" },
|
|
35
|
+
"title": { "type": "string", "minLength": 1 },
|
|
36
|
+
"description": { "type": "string", "description": "One line, shown under the title in the Reference dropdown." },
|
|
37
|
+
"position": { "type": "integer" },
|
|
38
|
+
"common": {
|
|
39
|
+
"type": "array",
|
|
40
|
+
"items": { "type": "string" },
|
|
41
|
+
"description": "Entry names listed first, under a Common heading."
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"titled": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"required": ["title", "content"],
|
|
48
|
+
"additionalProperties": false,
|
|
49
|
+
"properties": {
|
|
50
|
+
"title": { "type": "string", "minLength": 1 },
|
|
51
|
+
"content": { "$ref": "#/$defs/markdown" }
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"entry": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"required": ["id", "name", "kind", "category", "summary"],
|
|
57
|
+
"additionalProperties": false,
|
|
58
|
+
"properties": {
|
|
59
|
+
"id": { "$ref": "#/$defs/id" },
|
|
60
|
+
"name": { "type": "string", "minLength": 1, "description": "As users write it: data_read, AuditResult, or \"project bootstrap\"." },
|
|
61
|
+
"kind": { "enum": ["function", "class", "command"] },
|
|
62
|
+
"category": { "type": "string", "description": "A category id." },
|
|
63
|
+
"title": { "type": "string", "description": "A short heading. Defaults to name." },
|
|
64
|
+
"summary": { "type": "string", "minLength": 1, "description": "One line, used in lists and search results." },
|
|
65
|
+
"usage": { "type": "string", "description": "The signature or command line, shown as code." },
|
|
66
|
+
"description": { "$ref": "#/$defs/markdown", "description": "A paragraph or two (roxygen @description, a docstring's opening paragraphs)." },
|
|
67
|
+
"details": { "$ref": "#/$defs/markdown", "description": "The long body (roxygen @details)." },
|
|
68
|
+
"parameters": {
|
|
69
|
+
"type": "array",
|
|
70
|
+
"description": "Arguments, flags, or a class's fields, in signature order.",
|
|
71
|
+
"items": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"required": ["name", "description"],
|
|
74
|
+
"additionalProperties": false,
|
|
75
|
+
"properties": {
|
|
76
|
+
"name": { "type": "string", "minLength": 1, "description": "x, *, --path, or -p, --path." },
|
|
77
|
+
"type": { "type": "string" },
|
|
78
|
+
"default": { "type": "string", "description": "As code, such as NULL, None, or \".\"." },
|
|
79
|
+
"required": { "type": "boolean" },
|
|
80
|
+
"env": { "type": "string", "description": "An environment variable that sets it (commands)." },
|
|
81
|
+
"description": { "$ref": "#/$defs/markdown" }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"returns": { "$ref": "#/$defs/markdown" },
|
|
86
|
+
"note": { "$ref": "#/$defs/markdown" },
|
|
87
|
+
"sections": {
|
|
88
|
+
"type": "array",
|
|
89
|
+
"items": {
|
|
90
|
+
"type": "object",
|
|
91
|
+
"required": ["title", "content"],
|
|
92
|
+
"additionalProperties": false,
|
|
93
|
+
"properties": {
|
|
94
|
+
"title": { "type": "string", "minLength": 1 },
|
|
95
|
+
"content": { "$ref": "#/$defs/markdown" },
|
|
96
|
+
"subsections": { "type": "array", "items": { "$ref": "#/$defs/titled" } }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
"examples": {
|
|
101
|
+
"type": "array",
|
|
102
|
+
"items": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"required": ["code"],
|
|
105
|
+
"additionalProperties": false,
|
|
106
|
+
"properties": {
|
|
107
|
+
"title": { "type": "string" },
|
|
108
|
+
"code": { "type": "string", "minLength": 1 },
|
|
109
|
+
"runnable": { "type": "boolean", "default": true, "description": "False for R's \\dontrun and for examples that need a server or credentials." }
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"see_also": {
|
|
114
|
+
"type": "array",
|
|
115
|
+
"items": {
|
|
116
|
+
"oneOf": [
|
|
117
|
+
{ "type": "string", "minLength": 1, "description": "An entry name in this product, or a link target such as py:ref:data_read." },
|
|
118
|
+
{
|
|
119
|
+
"type": "object",
|
|
120
|
+
"required": ["title", "url"],
|
|
121
|
+
"additionalProperties": false,
|
|
122
|
+
"properties": {
|
|
123
|
+
"title": { "type": "string", "minLength": 1 },
|
|
124
|
+
"url": { "type": "string", "pattern": "^https://" }
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"aliases": { "type": "array", "items": { "type": "string" } },
|
|
131
|
+
"lifecycle": { "enum": ["stable", "deprecated", "experimental"], "default": "stable" },
|
|
132
|
+
"replaced_by": { "type": "string", "description": "The entry name to use instead, for deprecated entries." },
|
|
133
|
+
"since": { "type": "string", "description": "The version that added it." }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|