django-sqlite-ops 0.1.0a1__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.
Files changed (177) hide show
  1. django_sqlite_ops-0.1.0a1/.gitignore +8 -0
  2. django_sqlite_ops-0.1.0a1/CHANGELOG.md +30 -0
  3. django_sqlite_ops-0.1.0a1/LICENSE +21 -0
  4. django_sqlite_ops-0.1.0a1/PKG-INFO +987 -0
  5. django_sqlite_ops-0.1.0a1/README.md +956 -0
  6. django_sqlite_ops-0.1.0a1/django_sqlite_ops/__init__.py +1 -0
  7. django_sqlite_ops-0.1.0a1/django_sqlite_ops/apps.py +19 -0
  8. django_sqlite_ops-0.1.0a1/django_sqlite_ops/boot/__init__.py +4 -0
  9. django_sqlite_ops-0.1.0a1/django_sqlite_ops/boot/__main__.py +6 -0
  10. django_sqlite_ops-0.1.0a1/django_sqlite_ops/boot/cli.py +1066 -0
  11. django_sqlite_ops-0.1.0a1/django_sqlite_ops/boot/decide.py +350 -0
  12. django_sqlite_ops-0.1.0a1/django_sqlite_ops/boot/litestream.py +542 -0
  13. django_sqlite_ops-0.1.0a1/django_sqlite_ops/checks.py +400 -0
  14. django_sqlite_ops-0.1.0a1/django_sqlite_ops/compat/__init__.py +1 -0
  15. django_sqlite_ops-0.1.0a1/django_sqlite_ops/compat/_channels_lite_aio.py +49 -0
  16. django_sqlite_ops-0.1.0a1/django_sqlite_ops/compat/channels_lite.py +132 -0
  17. django_sqlite_ops-0.1.0a1/django_sqlite_ops/database.py +118 -0
  18. django_sqlite_ops-0.1.0a1/django_sqlite_ops/doctor.py +1051 -0
  19. django_sqlite_ops-0.1.0a1/django_sqlite_ops/health.py +1129 -0
  20. django_sqlite_ops-0.1.0a1/django_sqlite_ops/management/__init__.py +0 -0
  21. django_sqlite_ops-0.1.0a1/django_sqlite_ops/management/commands/__init__.py +0 -0
  22. django_sqlite_ops-0.1.0a1/django_sqlite_ops/management/commands/sqlite_doctor.py +161 -0
  23. django_sqlite_ops-0.1.0a1/django_sqlite_ops/restore_check.py +709 -0
  24. django_sqlite_ops-0.1.0a1/django_sqlite_ops/wal.py +287 -0
  25. django_sqlite_ops-0.1.0a1/docs/DECISIONS.md +32 -0
  26. django_sqlite_ops-0.1.0a1/docs/DESIGN.md +614 -0
  27. django_sqlite_ops-0.1.0a1/docs/profiles/fd-limits.md +125 -0
  28. django_sqlite_ops-0.1.0a1/docs/profiles/single-server-multiproc.md +215 -0
  29. django_sqlite_ops-0.1.0a1/docs/profiles/single-server.md +303 -0
  30. django_sqlite_ops-0.1.0a1/docs/reference/Dockerfile +11 -0
  31. django_sqlite_ops-0.1.0a1/docs/reference/README.md +7 -0
  32. django_sqlite_ops-0.1.0a1/docs/reference/compose.yaml +31 -0
  33. django_sqlite_ops-0.1.0a1/docs/reference/entrypoint.sh +19 -0
  34. django_sqlite_ops-0.1.0a1/docs/reference/guard_spike.py +44 -0
  35. django_sqlite_ops-0.1.0a1/docs/reference/vfs_workaround_spike.py +24 -0
  36. django_sqlite_ops-0.1.0a1/docs/research/bench-2026-10-08.md +176 -0
  37. django_sqlite_ops-0.1.0a1/docs/research/cache-size-2026-10-12.md +1072 -0
  38. django_sqlite_ops-0.1.0a1/docs/research/cross-review-gpt-6-astra.md +126 -0
  39. django_sqlite_ops-0.1.0a1/docs/research/fd-soak-2026-10-09.md +173 -0
  40. django_sqlite_ops-0.1.0a1/docs/research/handoff-marco-layer-wording.md +44 -0
  41. django_sqlite_ops-0.1.0a1/docs/research/handoff-marco-nats-flush-cancel.md +68 -0
  42. django_sqlite_ops-0.1.0a1/docs/research/lab-2026-10-08.md +195 -0
  43. django_sqlite_ops-0.1.0a1/docs/research/lab-2026-10-25-l4-two-way.md +42 -0
  44. django_sqlite_ops-0.1.0a1/docs/research/lab-2026-10-25-long-outage.md +114 -0
  45. django_sqlite_ops-0.1.0a1/docs/research/lab-2026-10-26-multiproc-nats.md +136 -0
  46. django_sqlite_ops-0.1.0a1/docs/research/litestream-django-docker.md +104 -0
  47. django_sqlite_ops-0.1.0a1/docs/research/litestream-django-scenarios.md +136 -0
  48. django_sqlite_ops-0.1.0a1/docs/research/sqlite-django-package-review-v0.md +130 -0
  49. django_sqlite_ops-0.1.0a1/docs/research/sqlite-django-package-review-v1.md +97 -0
  50. django_sqlite_ops-0.1.0a1/docs/research/turso-django.md +61 -0
  51. django_sqlite_ops-0.1.0a1/lab/README.md +217 -0
  52. django_sqlite_ops-0.1.0a1/lab/_benchstat.py +99 -0
  53. django_sqlite_ops-0.1.0a1/lab/_cachestat.py +191 -0
  54. django_sqlite_ops-0.1.0a1/lab/_checks.py +511 -0
  55. django_sqlite_ops-0.1.0a1/lab/_lab.py +368 -0
  56. django_sqlite_ops-0.1.0a1/lab/_soakstat.py +321 -0
  57. django_sqlite_ops-0.1.0a1/lab/app/lab_hooks/sitecustomize.py +23 -0
  58. django_sqlite_ops-0.1.0a1/lab/app/lab_tools/chanlab_run.py +239 -0
  59. django_sqlite_ops-0.1.0a1/lab/app/lab_tools/inspect_data.py +77 -0
  60. django_sqlite_ops-0.1.0a1/lab/app/lab_tools/loadgen.py +470 -0
  61. django_sqlite_ops-0.1.0a1/lab/app/lab_tools/procs.py +39 -0
  62. django_sqlite_ops-0.1.0a1/lab/app/lab_tools/replica.py +51 -0
  63. django_sqlite_ops-0.1.0a1/lab/app/lab_tools/s3_objects.py +63 -0
  64. django_sqlite_ops-0.1.0a1/lab/app/manage.py +9 -0
  65. django_sqlite_ops-0.1.0a1/lab/app/notes/__init__.py +0 -0
  66. django_sqlite_ops-0.1.0a1/lab/app/notes/apps.py +14 -0
  67. django_sqlite_ops-0.1.0a1/lab/app/notes/chanlab.py +260 -0
  68. django_sqlite_ops-0.1.0a1/lab/app/notes/management/__init__.py +0 -0
  69. django_sqlite_ops-0.1.0a1/lab/app/notes/management/commands/__init__.py +0 -0
  70. django_sqlite_ops-0.1.0a1/lab/app/notes/management/commands/lab_seed.py +27 -0
  71. django_sqlite_ops-0.1.0a1/lab/app/notes/metrics.py +216 -0
  72. django_sqlite_ops-0.1.0a1/lab/app/notes/migrations/0001_initial.py +26 -0
  73. django_sqlite_ops-0.1.0a1/lab/app/notes/migrations/__init__.py +0 -0
  74. django_sqlite_ops-0.1.0a1/lab/app/notes/models.py +7 -0
  75. django_sqlite_ops-0.1.0a1/lab/app/notes/timing.py +39 -0
  76. django_sqlite_ops-0.1.0a1/lab/app/notes/views.py +102 -0
  77. django_sqlite_ops-0.1.0a1/lab/app/proj/__init__.py +0 -0
  78. django_sqlite_ops-0.1.0a1/lab/app/proj/asgi.py +38 -0
  79. django_sqlite_ops-0.1.0a1/lab/app/proj/settings_tail.py +45 -0
  80. django_sqlite_ops-0.1.0a1/lab/app/proj/urls_tail.py +27 -0
  81. django_sqlite_ops-0.1.0a1/lab/build_context.py +153 -0
  82. django_sqlite_ops-0.1.0a1/lab/cache_mech.py +285 -0
  83. django_sqlite_ops-0.1.0a1/lab/cache_probe.py +514 -0
  84. django_sqlite_ops-0.1.0a1/lab/cache_report.py +254 -0
  85. django_sqlite_ops-0.1.0a1/lab/conftest.py +69 -0
  86. django_sqlite_ops-0.1.0a1/lab/lab-compose.yaml +178 -0
  87. django_sqlite_ops-0.1.0a1/lab/multiproc-override.yaml +16 -0
  88. django_sqlite_ops-0.1.0a1/lab/nats.Dockerfile +9 -0
  89. django_sqlite_ops-0.1.0a1/lab/profile-override.yaml +20 -0
  90. django_sqlite_ops-0.1.0a1/lab/soak_report.py +240 -0
  91. django_sqlite_ops-0.1.0a1/lab/test_bench.py +275 -0
  92. django_sqlite_ops-0.1.0a1/lab/test_scenarios.py +1001 -0
  93. django_sqlite_ops-0.1.0a1/lab/test_soak.py +261 -0
  94. django_sqlite_ops-0.1.0a1/pyproject.toml +94 -0
  95. django_sqlite_ops-0.1.0a1/scripts/ci-local.sh +48 -0
  96. django_sqlite_ops-0.1.0a1/scripts/lab.sh +138 -0
  97. django_sqlite_ops-0.1.0a1/scripts/release_check.py +191 -0
  98. django_sqlite_ops-0.1.0a1/tests/_channels_lite.py +28 -0
  99. django_sqlite_ops-0.1.0a1/tests/_litestream.py +74 -0
  100. django_sqlite_ops-0.1.0a1/tests/_nodjango.py +36 -0
  101. django_sqlite_ops-0.1.0a1/tests/conftest.py +13 -0
  102. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/README.md +46 -0
  103. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/databases_bad_yaml/rc +1 -0
  104. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/databases_bad_yaml/stderr +1 -0
  105. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/databases_bad_yaml/stdout +0 -0
  106. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/databases_config_missing/rc +1 -0
  107. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/databases_config_missing/stderr +1 -0
  108. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/databases_config_missing/stdout +0 -0
  109. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/databases_empty/rc +1 -0
  110. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/databases_empty/stderr +0 -0
  111. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/databases_empty/stdout +1 -0
  112. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/databases_json/rc +1 -0
  113. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/databases_json/stderr +0 -0
  114. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/databases_json/stdout +22 -0
  115. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/local_meta_after_replicate/meta/ltx/0/0000000000000019-0000000000000019.ltx +0 -0
  116. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_all_levels/rc +1 -0
  117. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_all_levels/stderr +0 -0
  118. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_all_levels/stdout +114 -0
  119. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_all_levels_text/rc +1 -0
  120. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_all_levels_text/stderr +0 -0
  121. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_all_levels_text/stdout +17 -0
  122. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_bad_yaml/rc +1 -0
  123. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_bad_yaml/stderr +1 -0
  124. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_bad_yaml/stdout +0 -0
  125. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_config_missing/rc +1 -0
  126. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_config_missing/stderr +1 -0
  127. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_config_missing/stdout +0 -0
  128. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_db_not_in_config/rc +1 -0
  129. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_db_not_in_config/stderr +1 -0
  130. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_db_not_in_config/stdout +0 -0
  131. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_l0_default/rc +1 -0
  132. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_l0_default/stderr +0 -0
  133. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_l0_default/stdout +9 -0
  134. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_no_l0_all_levels/rc +1 -0
  135. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_no_l0_all_levels/stderr +0 -0
  136. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_no_l0_all_levels/stdout +107 -0
  137. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_no_l0_default/rc +1 -0
  138. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_no_l0_default/stderr +0 -0
  139. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_no_l0_default/stdout +1 -0
  140. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_permission_denied/rc +1 -0
  141. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_permission_denied/stderr +1 -0
  142. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_permission_denied/stdout +0 -0
  143. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_replica_empty_dir/rc +1 -0
  144. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_replica_empty_dir/stderr +0 -0
  145. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_replica_empty_dir/stdout +1 -0
  146. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_replica_missing/rc +1 -0
  147. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_replica_missing/stderr +0 -0
  148. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_replica_missing/stdout +1 -0
  149. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/ltx_sample/0000000000000001-0000000000000001.ltx +0 -0
  150. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/make_fixtures.sh +151 -0
  151. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/restore_no_backups/rc +1 -0
  152. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/restore_no_backups/stderr +1 -0
  153. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/restore_no_backups/stdout +0 -0
  154. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/restore_ok_json/rc +1 -0
  155. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/restore_ok_json/stderr +0 -0
  156. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/restore_ok_json/stdout +8 -0
  157. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/restore_output_exists/rc +1 -0
  158. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/restore_output_exists/stderr +1 -0
  159. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/restore_output_exists/stdout +0 -0
  160. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/version/rc +1 -0
  161. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/version/stderr +0 -0
  162. django_sqlite_ops-0.1.0a1/tests/fixtures/litestream-0.5.17/version/stdout +1 -0
  163. django_sqlite_ops-0.1.0a1/tests/test_boot_cli.py +2609 -0
  164. django_sqlite_ops-0.1.0a1/tests/test_boot_decide.py +794 -0
  165. django_sqlite_ops-0.1.0a1/tests/test_boot_litestream.py +672 -0
  166. django_sqlite_ops-0.1.0a1/tests/test_checks.py +984 -0
  167. django_sqlite_ops-0.1.0a1/tests/test_compat_channels_lite.py +692 -0
  168. django_sqlite_ops-0.1.0a1/tests/test_database.py +315 -0
  169. django_sqlite_ops-0.1.0a1/tests/test_doc_links.py +119 -0
  170. django_sqlite_ops-0.1.0a1/tests/test_doctor.py +953 -0
  171. django_sqlite_ops-0.1.0a1/tests/test_environment.py +33 -0
  172. django_sqlite_ops-0.1.0a1/tests/test_health.py +2177 -0
  173. django_sqlite_ops-0.1.0a1/tests/test_lab.py +1722 -0
  174. django_sqlite_ops-0.1.0a1/tests/test_profiles.py +286 -0
  175. django_sqlite_ops-0.1.0a1/tests/test_readme.py +251 -0
  176. django_sqlite_ops-0.1.0a1/tests/test_release_check.py +255 -0
  177. django_sqlite_ops-0.1.0a1/tests/test_restore_check.py +1626 -0
@@ -0,0 +1,8 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.sqlite3*
4
+ .DS_Store
5
+ .work/
6
+ uv.lock
7
+ lab/.build/
8
+ lab/.out/
@@ -0,0 +1,30 @@
1
+ # Changelog
2
+
3
+ [Keep a Changelog](https://keepachangelog.com/ko/1.1.0/) 형식을 따르고, 버전은 [PEP 440](https://peps.python.org/pep-0440/) 으로 매긴다. 릴리스할 때 `[Unreleased]` 의 항목을 새 버전 절로 옮긴다(`scripts/release_check.py` 가 확인한다).
4
+
5
+ > ⚠️ **실험적 프로젝트다.** 1.0 전에는 API·동작·지원 범위가 예고 없이 바뀔 수 있다. 사전 릴리스(`a`·`b`·`rc`)는 버전을 명시해 설치한다(`pip install "django-sqlite-ops==0.1.0a1"`). `--pre` 는 모든 의존성의 사전 릴리스까지 허용하므로 쓰지 않는다. 운영 환경에서는 버전을 고정한다.
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0a1] - 2026-10-26
10
+
11
+ 첫 실험적 사전 릴리스다([D-28](docs/DECISIONS.md)). v0.1 범위([DESIGN §3-1](docs/DESIGN.md#3-범위))를 담는다. 지원 범위: Python 3.13·3.14, Django 5.2·6.1, SQLite 3.37.2 이상, Litestream 0.5.17. boot CLI 는 POSIX(Linux·macOS)만 지원한다.
12
+
13
+ ### Added
14
+
15
+ - **권장 설정** `sqlite_database()`: WAL·`busy_timeout`·`synchronous` 등 실측 근거가 있는 PRAGMA 표 하나를 정본으로, 표준 `DATABASES` 항목을 만든다. 프로필, `pragmas`·`options` 덮어쓰기, 잘못된 입력 거부 (#1)
16
+ - **boot CLI** `python -m django_sqlite_ops.boot`: Django 를 import 하지 않고 기동 전에 DB 상태를 판정해 Litestream 복원·신규 생성·기존 DB 도입을 결정한다. 판정할 수 없으면 사유 코드와 함께 거부(exit 2)하고, 파일 잠금으로 동시 실행을 막는다 (#2·#3·#4)
17
+ - **시스템 체크** `sqlite_ops.*`: DB 를 열지 않고 설정만으로 `SQLITE_OPS` 오류, `transaction_mode`·WAL 미설정, VFS 별칭의 `CONN_MAX_AGE`, ASGI 에서의 `CONN_MAX_AGE=None` 을 알린다 (#5)
18
+ - **`sqlite_doctor` 관리 명령**: 실제 연결로 PRAGMA·WAL·Litestream 복제 상태를 진단한다. 종료 코드 0/1/2 (#6)
19
+ - **`sqlite_doctor --restore-test`**: 복제본을 별도 디렉터리에 복원해 무결성 검사·TXID 비교로 복원 가능성을 검증한다. 운영 DB 와 Litestream 메타 경로는 건드리지 않는다 (#52·#54)
20
+ - **복제 헬스 뷰**: 로컬과 복제본의 TXID 를 직접 비교해 `caught_up`·`backlog`·`unknown`(사유 코드 `stale`·`remote_error` 등)을 JSON 으로 낸다 (#7·#65·#72)
21
+ - **배포 프로필 문서**: 단일 서버, 단일 서버 다중 프로세스, fd 한도 (#9·#32)
22
+ - **compat 패치**: channels-lite aio 레이어 결함의 버전 게이트 몽키패치 (#8)
23
+ - **회귀 랩**: Docker(SeaweedFS·toxiproxy)로 실패 경계 L1–L8 을 재현하는 수동 실행 랩(`scripts/lab.sh`, GitHub Actions `lab.yml`). 기본 CI 밖이며 wheel 에는 들어가지 않는다(sdist 에는 소스로 들어간다) (#10)
24
+
25
+ ### Known limitations
26
+
27
+ - 미검증 항목은 [DESIGN §12](docs/DESIGN.md#12-미검증확인-필요) 에 있다. 실제 클라우드 S3·R2·Tigris, Windows 의 boot CLI, ASGI 에서의 VFS 별칭 동작은 검증하지 않았다.
28
+
29
+ [Unreleased]: https://github.com/itda-work/django-sqlite-ops/compare/v0.1.0a1...HEAD
30
+ [0.1.0a1]: https://github.com/itda-work/django-sqlite-ops/releases/tag/v0.1.0a1
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 itda-work
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.