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
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: framework-workflow
3
+ description: Use when writing or editing any Python notebook or script in this Framework project. Every notebook and script starts with fw.scaffold(). Covers project setup, the analysis workflow, and creating notebooks and scripts with make_notebook() and make_script().
4
+ ---
5
+
6
+ # Framework Workflow
7
+
8
+ This project uses Framework for reproducible data analysis in Python. Import
9
+ it as `import framework as fw`.
10
+
11
+ ## Start with scaffold()
12
+
13
+ Begin every notebook and script with:
14
+
15
+ ```python
16
+ import framework as fw
17
+
18
+ fw.scaffold()
19
+ ```
20
+
21
+ `scaffold()` finds the project root from any subfolder, loads `.env` and the
22
+ settings, sets the random seed when the project asks for one, puts the
23
+ project's `functions/` folder on the import path, and runs `scaffold.py`.
24
+ Without it, settings paths and environment variables are not available.
25
+
26
+ - Import helpers from `functions/` with a normal `import`. `scaffold()` has
27
+ already put the folder on the path, so do not edit `sys.path` yourself.
28
+ - Do not reseed after `scaffold()` when the project sets a seed, unless an
29
+ operation needs its own seed. Say why in a comment if so.
30
+ - Paths in the settings are relative to the project root. Framework resolves
31
+ them for you, so do not change the working directory.
32
+
33
+ ## Analysis workflow
34
+
35
+ 1. Read raw data with `fw.data_read()`.
36
+ 2. Clean it and save intermediate data with `fw.data_save()`.
37
+ 3. Analyze the final, analysis-ready data.
38
+ 4. Save results with `fw.save_table()`, `fw.save_figure()`, or `fw.save_model()`.
39
+
40
+ The `framework-data` skill covers data rules, and the `framework-outputs`
41
+ skill covers results, caching, and publishing.
42
+
43
+ ## New notebooks and scripts
44
+
45
+ Create files through Framework so they go in the right folder with the
46
+ project's template:
47
+
48
+ ```python
49
+ fw.make_notebook("01-data-cleaning") # notebooks/01-data-cleaning.qmd
50
+ fw.make_script("data-processing") # scripts/data-processing.py
51
+ fw.make_notebook("talk", stub="revealjs")
52
+ ```
53
+
54
+ ## Habits
55
+
56
+ - Raw data is never modified. Write cleaned versions to the intermediate folder.
57
+ - Notebooks explain each transformation.
58
+ - Commit notebooks and code, not rendered outputs.
@@ -0,0 +1,34 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": ["# {{ title }}\n", "\n", "{{ author }}, {{ date }}"]
7
+ },
8
+ {
9
+ "cell_type": "code",
10
+ "execution_count": null,
11
+ "metadata": {},
12
+ "outputs": [],
13
+ "source": ["import framework as fw\n", "\n", "fw.scaffold()"]
14
+ },
15
+ {
16
+ "cell_type": "markdown",
17
+ "metadata": {},
18
+ "source": ["## Analysis"]
19
+ },
20
+ {
21
+ "cell_type": "code",
22
+ "execution_count": null,
23
+ "metadata": {},
24
+ "outputs": [],
25
+ "source": []
26
+ }
27
+ ],
28
+ "metadata": {
29
+ "kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"},
30
+ "language_info": {"name": "python"}
31
+ },
32
+ "nbformat": 4,
33
+ "nbformat_minor": 5
34
+ }
@@ -0,0 +1,43 @@
1
+ ---
2
+ title: "{{ title }}"
3
+ author: "{{ author }}"
4
+ date: "{{ date }}"
5
+ format:
6
+ html:
7
+ toc: true
8
+ toc-depth: 2
9
+ code-fold: false
10
+ execute:
11
+ echo: true
12
+ warning: true
13
+ jupyter: python3
14
+ ---
15
+
16
+ ```{python}
17
+ #| label: setup
18
+ #| include: false
19
+
20
+ import framework as fw
21
+
22
+ fw.scaffold()
23
+ ```
24
+
25
+ ## Introduction
26
+
27
+ Brief description of this notebook's purpose.
28
+
29
+ ```{python}
30
+ # Your code here
31
+ ```
32
+
33
+ ## Analysis
34
+
35
+ ```{python}
36
+ # Analysis code
37
+ ```
38
+
39
+ ## Results
40
+
41
+ ```{python}
42
+ # Results code
43
+ ```
@@ -0,0 +1,15 @@
1
+ ---
2
+ title: "{{ title }}"
3
+ author: "{{ author }}"
4
+ date: "{{ date }}"
5
+ jupyter: python3
6
+ ---
7
+
8
+ ```{python}
9
+ #| label: setup
10
+ #| include: false
11
+
12
+ import framework as fw
13
+
14
+ fw.scaffold()
15
+ ```
@@ -0,0 +1,34 @@
1
+ ---
2
+ title: "{{ title }}"
3
+ author: "{{ author }}"
4
+ date: "{{ date }}"
5
+ format:
6
+ revealjs:
7
+ theme: default
8
+ transition: slide
9
+ slide-number: true
10
+ execute:
11
+ echo: true
12
+ jupyter: python3
13
+ ---
14
+
15
+ ```{python}
16
+ #| label: setup
17
+ #| include: false
18
+
19
+ import framework as fw
20
+
21
+ fw.scaffold()
22
+ ```
23
+
24
+ ## Introduction
25
+
26
+ - Point 1
27
+ - Point 2
28
+ - Point 3
29
+
30
+ ## Analysis
31
+
32
+ ```{python}
33
+ # data = fw.data_read("inputs.final.analysis")
34
+ ```
@@ -0,0 +1,18 @@
1
+ """{{ title }}
2
+
3
+ Author: {{ author }}
4
+ Date: {{ date }}
5
+ """
6
+
7
+ import framework as fw
8
+
9
+ fw.scaffold()
10
+
11
+
12
+ def main() -> None:
13
+ # data = fw.data_read("inputs.raw.example")
14
+ pass
15
+
16
+
17
+ if __name__ == "__main__":
18
+ main()
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.5
2
+ Name: framework-py
3
+ Version: 0.1.0.dev0
4
+ Summary: Structured data science projects: a data catalog, integrity tracking, caching, outputs, and AI guardrails
5
+ Project-URL: Homepage, https://framework.table1.org
6
+ Project-URL: Source, https://github.com/frameworktools/framework-py
7
+ Project-URL: Issues, https://github.com/frameworktools/framework-py/issues
8
+ Author-email: Erik Westlund <erik@table1.org>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Classifier: Development Status :: 2 - Pre-Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Scientific/Engineering
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: httpx>=0.27
24
+ Requires-Dist: jsonschema>=4.18
25
+ Requires-Dist: keyring>=25
26
+ Requires-Dist: pandas>=2.2
27
+ Requires-Dist: pyarrow>=16
28
+ Requires-Dist: pyyaml>=6.0
29
+ Requires-Dist: secretstorage>=3.2; sys_platform == 'linux'
30
+ Provides-Extra: duckdb
31
+ Requires-Dist: duckdb>=1.0; extra == 'duckdb'
32
+ Provides-Extra: excel
33
+ Requires-Dist: openpyxl>=3.1; extra == 'excel'
34
+ Provides-Extra: mysql
35
+ Requires-Dist: pymysql>=1.1; extra == 'mysql'
36
+ Provides-Extra: postgres
37
+ Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
38
+ Provides-Extra: s3
39
+ Requires-Dist: boto3>=1.34; extra == 's3'
40
+ Provides-Extra: sas
41
+ Requires-Dist: pyreadstat>=1.2; extra == 'sas'
42
+ Provides-Extra: spss
43
+ Requires-Dist: pyreadstat>=1.2; extra == 'spss'
44
+ Provides-Extra: sqlserver
45
+ Requires-Dist: pyodbc>=5.0; extra == 'sqlserver'
46
+ Description-Content-Type: text/markdown
47
+
48
+ # framework-py
49
+
50
+ Framework for Python: structured data science projects with a data catalog,
51
+ data integrity tracking, caching, saved outputs, and guardrails for AI coding
52
+ assistants.
53
+
54
+ framework-py is the Python implementation of
55
+ [Framework](https://framework.table1.org). It reads and writes the same
56
+ project files as the R package (`settings.yml`, the data catalog, and
57
+ `framework.db`), so either language can work with a project's data. It works
58
+ without Framework Cloud and without the `fw` command-line tool.
59
+
60
+ Status: in development. The plan is in [docs/plan.md](docs/plan.md).
61
+
62
+ ## Install
63
+
64
+ ```sh
65
+ uv add framework-py
66
+ # or
67
+ pip install framework-py
68
+ ```
69
+
70
+ Optional extras add database drivers, S3 storage, and more file formats:
71
+ `postgres`, `mysql`, `duckdb`, `sqlserver`, `s3`, `excel`, `spss`, and `sas`
72
+ (version 8 SAS transport files).
73
+
74
+ ```sh
75
+ uv add "framework-py[postgres,s3]"
76
+ ```
77
+
78
+ ## Use
79
+
80
+ ```python
81
+ import framework as fw
82
+
83
+ fw.settings("directories.notebooks")
84
+ info = fw.data_info("inputs.raw.survey")
85
+ fw.data_list()
86
+ ```
87
+
88
+ After cloning a project, prepare it with:
89
+
90
+ ```sh
91
+ uv run python -m framework bootstrap
92
+ ```
93
+
94
+ ## Development
95
+
96
+ ```sh
97
+ uv sync
98
+ uv run pytest
99
+ uv run ruff check && uv run ruff format --check
100
+ uv run mypy
101
+ uv run python scripts/check_parity.py
102
+ uv run python scripts/sync_assets.py --check
103
+ ```
104
+
105
+ Shared files (JSON Schemas, the `framework.db` schema, conformance fixtures,
106
+ and the R export list) are copied from the R package with
107
+ `scripts/sync_assets.py`. Change them in the R repository, then copy them
108
+ again.
109
+
110
+ `parity.toml` maps every function the R package exports to its Python
111
+ equivalent, or gives the reason there is none.
@@ -0,0 +1,63 @@
1
+ framework/__init__.py,sha256=Mq-Oq0FPzKPL7RbTGbwDaKV3do5DYLsBHkUSREabvoU,5652
2
+ framework/__main__.py,sha256=FBtbnP45klyRrdCk3ANTh2xNg72IKGQopslqZuGLqq0,3337
3
+ framework/_log.py,sha256=MhVL7izCJUvdg7F9430j8gPFiFyx3VcJ0qyJmrn2uUk,885
4
+ framework/_paths.py,sha256=Kr-AZmSaKhKd_Wsxzgi3VnBPGD5FN3_GEg-bPUmKDkA,1736
5
+ framework/_secretservice.py,sha256=zv5UvEekQic466qRmVpLG_hfEkisikHfab8e6kyzS2k,2450
6
+ framework/_time.py,sha256=mv-BS3NjOo0hr6bwYRhUBY-3sG6SJmzaboaMD62QcJw,306
7
+ framework/_wincred.py,sha256=oJGS37Dcw_PZVmiAGRu4H_lhcOC5-FqGGYfxVUGPPmU,3891
8
+ framework/_yaml.py,sha256=ZJqh9ywmBEbD0VpV0FODcyAkvCsUTyj2W0MisFNRyT0,2372
9
+ framework/ai.py,sha256=8IEemkQo-muwuP8FkyjuCl0Bv_CANkQTCiK47aTjqZs,8006
10
+ framework/bootstrap.py,sha256=w-LLg_12KczC03FGD7bL6OZrjqTgFQNC5FPS8vFMIKg,7666
11
+ framework/cache.py,sha256=enPWWQoozoEtrMgNxImTBUnW13Zny0n2X_gEcWAgUXI,11792
12
+ framework/catalog.py,sha256=3ykZuLOXfnWDffXj0kSKsj4rmQ7saOer3athpCLPeAs,6795
13
+ framework/cloud.py,sha256=OcPBn8c0xtTwW1-_Owav_vnPE9XswOCmfZ3COAbksZo,32664
14
+ framework/config_home.py,sha256=pwZTfxN28qzqXCqWKLIqpALYyMKCoHpvEhrDedqlMY4,6716
15
+ framework/connections.py,sha256=0A1wi7PPeFFgmDRZHBiCnDWNIMxpBQyPYAuDDSM5-U0,15548
16
+ framework/console.py,sha256=L5CtNh8mX7oeZZfD1IT3GLG9xX-S0McUIV2DeOBBS00,1119
17
+ framework/create.py,sha256=ZDWsidhqZsKZq038MvAWFI1ASj0GU4RQ9qYvqhZf0PE,22489
18
+ framework/data.py,sha256=-lGRPEagLJ7oKLeKpVjd2sJAR6ehN6kWr3A3bQjk008,18816
19
+ framework/db.py,sha256=dUj_xTEhbrYtlrFLkzwZ8hm95rov_8vELYgge0AcLPk,7604
20
+ framework/dotenv.py,sha256=c3XWhb8H6zD6FhHwah1llCm-aG2CBlFGto2LdrEVRes,2592
21
+ framework/errors.py,sha256=BIrWiMUuxmCBNVGxg-9vUAbht3N8On7iyacp0bnTUNk,1103
22
+ framework/global_config.py,sha256=1MvaC_UuBJxzOUUWnTBUGSxi5Bc_xqntuseg-8Idchg,9096
23
+ framework/hooks.py,sha256=xRoFCECCwlNyk0bedf8D8nxG9cE6uKfZC_GfgAkcDCU,13457
24
+ framework/integrity.py,sha256=2hXk95tlNGs6HVTFA0AqcPWQg2AiO6Hy0APMIXHpCTU,586
25
+ framework/notebooks.py,sha256=Id6b-heolpKh6QGZ5N_Ork4pWQrt2DqNTFuFCAJHEPY,14511
26
+ framework/outputs.py,sha256=U81GqxfZzBopkjJWf6XVxdhfBTh0I69COPzE9ucM8Y4,12197
27
+ framework/packages.py,sha256=TPGrACLNoXl6Qwc5fXXTAbQRpsZYt8fhi00VhLLg9J8,9319
28
+ framework/project.py,sha256=0fqWystQkqbumt_V8EbbRyIkPYlvhWQzOXaMC3BPwmI,1581
29
+ framework/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ framework/quarto.py,sha256=8qCnfCCzPvCVVNyAXCeEqdbJioROB5MO7tti_2WK5Ns,5456
31
+ framework/scaffold.py,sha256=BB59l4M5IJo6p_fOvS263FgKNez6Coz5B67ijWymP9w,9528
32
+ framework/scratch.py,sha256=HO8wfKqET3TuP-Mp1dQKTD0rNlYTifrUQwCvwf5-ApI,3877
33
+ framework/security.py,sha256=dQMxo71rur5ToqbLisykvMEqg6YXD7NJMgfTzK8-ltU,24460
34
+ framework/settings.py,sha256=Fl9U5ftXqqVKkiBxeLBDjkdixXt5CeTTLbfLCapS798,15901
35
+ framework/storage.py,sha256=FoNzVjCPeK7CJ8_ZTBFCgF9hPaIPnf0Hkw8kTsysCNs,18913
36
+ framework/_assets/init.sql,sha256=-aIiuIcCmBniGKq-CHdRtUiV9e_CLXoqxxd05snmgOE,1156
37
+ framework/_assets/hooks/pre-commit,sha256=vx5kfbJo2sAUnvEcZdm_wllw9_sNX4o5xlzYJo8-jbA,1836
38
+ framework/_assets/schema/layer.v1.json,sha256=mfHx1nMwQQOisK_Qtiynt_ZrmV-0uAKzk-NvVh_4Szk,13519
39
+ framework/_assets/schema/layers.v1.json,sha256=3HTKwXKz7b-wm8BzHy254J_iopxULGCU7x4p4ZcEh_Y,19654
40
+ framework/_assets/schema/spec.v1.json,sha256=1uNYKapiiubthiNM-h5ycAAobid18dtIdO1clYmXE4c,13414
41
+ framework/spec/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
+ framework/spec/check.py,sha256=lJ5wtGiQ2tTFmfa-1X5z06i6VPbqctW2bsW7nNtweBQ,7863
43
+ framework/spec/merge.py,sha256=ffuhG9dP41x0t1NjH_9eoUTZmlH4r5blvkMXVJS1vnI,6271
44
+ framework/spec/resolve.py,sha256=sYIcgg6mak05qgPDp1c1Zss6xG_YQS64yS3T4mibs-g,8633
45
+ framework/templates/defaults.yml,sha256=KMduXZw8-m8qB3jhYd3z8Zb1tPq7bnoqdbvu4wtcQH8,3893
46
+ framework/templates/gitignore/course,sha256=CGPnIM_-ZAU7-WOGyKhaWSTdqUR3atAUmxqrbiFd5RU,2869
47
+ framework/templates/gitignore/presentation,sha256=CAl2vPcA1Lgv1CXsVmuKf7DtIVs-g6WEWnPnOuGmMdc,2743
48
+ framework/templates/gitignore/project,sha256=D8jFTFhIyJyTIh1xrPE5wFkvuJlSkqaqeJYafnmRPts,2622
49
+ framework/templates/gitignore/sensitive,sha256=mz0KwMHq1UJhOGJTFzTbzJug6C6p2wbec9ADl2veqhE,3550
50
+ framework/templates/skills/framework-data/SKILL.md,sha256=jdy9b0OgEWEvySgBTEVwEr6haSHYqRgCRRaT9Xd9zso,1802
51
+ framework/templates/skills/framework-outputs/SKILL.md,sha256=TPxr8mr8Xmf3cdsA8gsd2E6qnADraDz8TMlz2JaMyVw,2407
52
+ framework/templates/skills/framework-packages/SKILL.md,sha256=hReYddtMTK9dzqLiGOrSxbE8YA6OFia_E8tqbkSgNxI,1170
53
+ framework/templates/skills/framework-sensitive-data/SKILL.md,sha256=o3TY43t5Ln6wCUacZ7rvOwVuRgDl2yVUgScsufnwFzk,1536
54
+ framework/templates/skills/framework-workflow/SKILL.md,sha256=-UIc81zJrO-0FfKlt2QK5Zfh0YzqbqNzBPskBpXe_1Q,2139
55
+ framework/templates/stubs/notebook-default.ipynb,sha256=2p3Zj6bQEHlUL1eEVAbPfTTxqGBs84C67tA3MkCzVQs,685
56
+ framework/templates/stubs/notebook-default.qmd,sha256=8Js1S2BcHYeQWmJS-kt31gBvIjKzQeGEy2M2lQCVA1Y,472
57
+ framework/templates/stubs/notebook-minimal.qmd,sha256=2U2k8judffhrh_6m6r6OAbQBCYEtViAjeKRBqBYCJTM,178
58
+ framework/templates/stubs/notebook-revealjs.qmd,sha256=_ztT_gHoudpB5DIjlbvtB5zQ8163DeV8xbWcT-HgJrI,409
59
+ framework/templates/stubs/script-default.py,sha256=vbWqgW0_rHot17mQC3tYb92g_9L-cbTOeL3y99BRrSk,216
60
+ framework_py-0.1.0.dev0.dist-info/METADATA,sha256=bRUaDUI5tJy07M93rsCcxQhgP234sSCgPUR8N3gi35U,3481
61
+ framework_py-0.1.0.dev0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
62
+ framework_py-0.1.0.dev0.dist-info/licenses/LICENSE,sha256=giOOgSwALkZimC7uhLZQK8Untez641bLW1-2-Cjvmq0,1072
63
+ framework_py-0.1.0.dev0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright (c) 2026 Erik Westlund
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.