edda-framework 0.1.0__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 (133) hide show
  1. edda_framework-0.1.0/.github/workflows/ci.yml +72 -0
  2. edda_framework-0.1.0/.github/workflows/docs.yml +46 -0
  3. edda_framework-0.1.0/.github/workflows/release.yml +35 -0
  4. edda_framework-0.1.0/.gitignore +101 -0
  5. edda_framework-0.1.0/.python-version +1 -0
  6. edda_framework-0.1.0/Justfile +131 -0
  7. edda_framework-0.1.0/LICENSE +21 -0
  8. edda_framework-0.1.0/PKG-INFO +748 -0
  9. edda_framework-0.1.0/README.md +700 -0
  10. edda_framework-0.1.0/demo_app.py +1819 -0
  11. edda_framework-0.1.0/docs/core-features/durable-execution/replay.md +630 -0
  12. edda_framework-0.1.0/docs/core-features/events/cloudevents-http-binding.md +327 -0
  13. edda_framework-0.1.0/docs/core-features/events/wait-event.md +508 -0
  14. edda_framework-0.1.0/docs/core-features/hooks.md +234 -0
  15. edda_framework-0.1.0/docs/core-features/retry.md +621 -0
  16. edda_framework-0.1.0/docs/core-features/saga-compensation.md +410 -0
  17. edda_framework-0.1.0/docs/core-features/transactional-outbox.md +1027 -0
  18. edda_framework-0.1.0/docs/core-features/workflows-activities.md +783 -0
  19. edda_framework-0.1.0/docs/examples/ecommerce.md +202 -0
  20. edda_framework-0.1.0/docs/examples/events.md +324 -0
  21. edda_framework-0.1.0/docs/examples/fastapi-integration.md +492 -0
  22. edda_framework-0.1.0/docs/examples/saga.md +172 -0
  23. edda_framework-0.1.0/docs/examples/simple.md +191 -0
  24. edda_framework-0.1.0/docs/getting-started/concepts.md +647 -0
  25. edda_framework-0.1.0/docs/getting-started/first-workflow.md +512 -0
  26. edda_framework-0.1.0/docs/getting-started/installation.md +272 -0
  27. edda_framework-0.1.0/docs/getting-started/quick-start.md +216 -0
  28. edda_framework-0.1.0/docs/index.md +206 -0
  29. edda_framework-0.1.0/docs/markdown.md +98 -0
  30. edda_framework-0.1.0/docs/viewer-ui/images/cloudevents-cli-trigger.png +0 -0
  31. edda_framework-0.1.0/docs/viewer-ui/images/compensation-execution.png +0 -0
  32. edda_framework-0.1.0/docs/viewer-ui/images/conditional-branching-diagram.png +0 -0
  33. edda_framework-0.1.0/docs/viewer-ui/images/detail-overview-panel.png +0 -0
  34. edda_framework-0.1.0/docs/viewer-ui/images/execution-history-panel.png +0 -0
  35. edda_framework-0.1.0/docs/viewer-ui/images/form-generation-example.png +0 -0
  36. edda_framework-0.1.0/docs/viewer-ui/images/hybrid-diagram-example.png +0 -0
  37. edda_framework-0.1.0/docs/viewer-ui/images/nested-pydantic-form.png +0 -0
  38. edda_framework-0.1.0/docs/viewer-ui/images/start-workflow-dialog.png +0 -0
  39. edda_framework-0.1.0/docs/viewer-ui/images/status-badges-example.png +0 -0
  40. edda_framework-0.1.0/docs/viewer-ui/images/wait-event-visualization.png +0 -0
  41. edda_framework-0.1.0/docs/viewer-ui/images/workflow-list-view.png +0 -0
  42. edda_framework-0.1.0/docs/viewer-ui/setup.md +306 -0
  43. edda_framework-0.1.0/docs/viewer-ui/visualization.md +249 -0
  44. edda_framework-0.1.0/edda/__init__.py +56 -0
  45. edda_framework-0.1.0/edda/activity.py +505 -0
  46. edda_framework-0.1.0/edda/app.py +996 -0
  47. edda_framework-0.1.0/edda/compensation.py +326 -0
  48. edda_framework-0.1.0/edda/context.py +489 -0
  49. edda_framework-0.1.0/edda/events.py +505 -0
  50. edda_framework-0.1.0/edda/exceptions.py +64 -0
  51. edda_framework-0.1.0/edda/hooks.py +284 -0
  52. edda_framework-0.1.0/edda/locking.py +322 -0
  53. edda_framework-0.1.0/edda/outbox/__init__.py +15 -0
  54. edda_framework-0.1.0/edda/outbox/relayer.py +274 -0
  55. edda_framework-0.1.0/edda/outbox/transactional.py +112 -0
  56. edda_framework-0.1.0/edda/pydantic_utils.py +316 -0
  57. edda_framework-0.1.0/edda/replay.py +799 -0
  58. edda_framework-0.1.0/edda/retry.py +207 -0
  59. edda_framework-0.1.0/edda/serialization/__init__.py +9 -0
  60. edda_framework-0.1.0/edda/serialization/base.py +83 -0
  61. edda_framework-0.1.0/edda/serialization/json.py +102 -0
  62. edda_framework-0.1.0/edda/storage/__init__.py +9 -0
  63. edda_framework-0.1.0/edda/storage/models.py +194 -0
  64. edda_framework-0.1.0/edda/storage/protocol.py +737 -0
  65. edda_framework-0.1.0/edda/storage/sqlalchemy_storage.py +1809 -0
  66. edda_framework-0.1.0/edda/viewer_ui/__init__.py +20 -0
  67. edda_framework-0.1.0/edda/viewer_ui/app.py +1399 -0
  68. edda_framework-0.1.0/edda/viewer_ui/components.py +1105 -0
  69. edda_framework-0.1.0/edda/viewer_ui/data_service.py +880 -0
  70. edda_framework-0.1.0/edda/visualizer/__init__.py +11 -0
  71. edda_framework-0.1.0/edda/visualizer/ast_analyzer.py +383 -0
  72. edda_framework-0.1.0/edda/visualizer/mermaid_generator.py +355 -0
  73. edda_framework-0.1.0/edda/workflow.py +218 -0
  74. edda_framework-0.1.0/examples/__init__.py +1 -0
  75. edda_framework-0.1.0/examples/cancellable_workflow.py +238 -0
  76. edda_framework-0.1.0/examples/compensation_workflow.py +173 -0
  77. edda_framework-0.1.0/examples/event_waiting_app.py +135 -0
  78. edda_framework-0.1.0/examples/event_waiting_workflow.py +125 -0
  79. edda_framework-0.1.0/examples/event_waiting_workflow_complete.py +187 -0
  80. edda_framework-0.1.0/examples/observability_with_logfire.py +295 -0
  81. edda_framework-0.1.0/examples/pydantic_saga.py +192 -0
  82. edda_framework-0.1.0/examples/retry_example.py +340 -0
  83. edda_framework-0.1.0/examples/retry_with_compensation.py +407 -0
  84. edda_framework-0.1.0/examples/simple_workflow.py +112 -0
  85. edda_framework-0.1.0/examples/typeddict_example.py +233 -0
  86. edda_framework-0.1.0/examples/with_outbox.py +320 -0
  87. edda_framework-0.1.0/pyproject.toml +185 -0
  88. edda_framework-0.1.0/tests/__init__.py +1 -0
  89. edda_framework-0.1.0/tests/conftest.py +333 -0
  90. edda_framework-0.1.0/tests/test_activity.py +437 -0
  91. edda_framework-0.1.0/tests/test_activity_retry.py +483 -0
  92. edda_framework-0.1.0/tests/test_app.py +436 -0
  93. edda_framework-0.1.0/tests/test_ast_analyzer.py +321 -0
  94. edda_framework-0.1.0/tests/test_atomic_wait_event.py +260 -0
  95. edda_framework-0.1.0/tests/test_binary_data.py +309 -0
  96. edda_framework-0.1.0/tests/test_cloudevents_http_binding.py +314 -0
  97. edda_framework-0.1.0/tests/test_compensation.py +404 -0
  98. edda_framework-0.1.0/tests/test_compensation_crash_recovery.py.wip +347 -0
  99. edda_framework-0.1.0/tests/test_concurrent_outbox.py +218 -0
  100. edda_framework-0.1.0/tests/test_context.py +353 -0
  101. edda_framework-0.1.0/tests/test_ctx_session.py +249 -0
  102. edda_framework-0.1.0/tests/test_distributed_event_delivery.py +294 -0
  103. edda_framework-0.1.0/tests/test_events.py +306 -0
  104. edda_framework-0.1.0/tests/test_lock_race_condition.py +146 -0
  105. edda_framework-0.1.0/tests/test_lock_timeout_customization.py +266 -0
  106. edda_framework-0.1.0/tests/test_locking.py +307 -0
  107. edda_framework-0.1.0/tests/test_multidb_storage.py +173 -0
  108. edda_framework-0.1.0/tests/test_outbox.py +575 -0
  109. edda_framework-0.1.0/tests/test_pydantic_activity.py +278 -0
  110. edda_framework-0.1.0/tests/test_pydantic_enum.py +320 -0
  111. edda_framework-0.1.0/tests/test_pydantic_events.py +324 -0
  112. edda_framework-0.1.0/tests/test_pydantic_saga.py +501 -0
  113. edda_framework-0.1.0/tests/test_pydantic_utils.py +234 -0
  114. edda_framework-0.1.0/tests/test_received_event.py +221 -0
  115. edda_framework-0.1.0/tests/test_replay.py +492 -0
  116. edda_framework-0.1.0/tests/test_retry_policy.py +460 -0
  117. edda_framework-0.1.0/tests/test_saga_parameter_extraction.py +327 -0
  118. edda_framework-0.1.0/tests/test_serialization.py +150 -0
  119. edda_framework-0.1.0/tests/test_skip_locked.py +264 -0
  120. edda_framework-0.1.0/tests/test_stale_workflow_recovery.py +439 -0
  121. edda_framework-0.1.0/tests/test_storage.py +339 -0
  122. edda_framework-0.1.0/tests/test_storage_mysql.py +197 -0
  123. edda_framework-0.1.0/tests/test_storage_postgresql.py +199 -0
  124. edda_framework-0.1.0/tests/test_transactions.py +374 -0
  125. edda_framework-0.1.0/tests/test_viewer_pydantic_form.py +1247 -0
  126. edda_framework-0.1.0/tests/test_viewer_start_saga.py +314 -0
  127. edda_framework-0.1.0/tests/test_wait_timer.py +357 -0
  128. edda_framework-0.1.0/tests/test_workflow.py +448 -0
  129. edda_framework-0.1.0/tests/test_workflow_auto_register.py +215 -0
  130. edda_framework-0.1.0/tests/test_workflow_cancellation.py +441 -0
  131. edda_framework-0.1.0/uv.lock +2492 -0
  132. edda_framework-0.1.0/viewer_app.py +148 -0
  133. edda_framework-0.1.0/zensical.toml +340 -0
@@ -0,0 +1,72 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
15
+
16
+ services:
17
+ postgres:
18
+ image: postgres:17
19
+ env:
20
+ POSTGRES_USER: edda
21
+ POSTGRES_PASSWORD: edda_test_password
22
+ POSTGRES_DB: edda_test
23
+ options: >-
24
+ --health-cmd pg_isready
25
+ --health-interval 10s
26
+ --health-timeout 5s
27
+ --health-retries 5
28
+ ports:
29
+ - 5432:5432
30
+
31
+ mysql:
32
+ image: mysql:9
33
+ env:
34
+ MYSQL_ROOT_PASSWORD: root_password
35
+ MYSQL_DATABASE: edda_test
36
+ MYSQL_USER: edda
37
+ MYSQL_PASSWORD: edda_test_password
38
+ options: >-
39
+ --health-cmd="mysqladmin ping"
40
+ --health-interval=10s
41
+ --health-timeout=5s
42
+ --health-retries=3
43
+ ports:
44
+ - 3306:3306
45
+
46
+ steps:
47
+ - name: Checkout code
48
+ uses: actions/checkout@v4
49
+
50
+ - name: Set up uv
51
+ uses: astral-sh/setup-uv@v3
52
+
53
+ - name: Set up Python ${{ matrix.python-version }}
54
+ run: uv python install ${{ matrix.python-version }}
55
+
56
+ - name: Install dependencies (all database drivers)
57
+ run: uv sync --extra dev --extra postgresql --extra mysql
58
+
59
+ - name: Run tests (all databases)
60
+ env:
61
+ EDDA_TEST_POSTGRES_URL: postgresql+asyncpg://edda:edda_test_password@localhost:5432/edda_test
62
+ EDDA_TEST_MYSQL_URL: mysql+aiomysql://edda:edda_test_password@localhost:3306/edda_test
63
+ run: uv run pytest -v --tb=short
64
+
65
+ - name: Type check
66
+ run: uv run mypy edda
67
+
68
+ - name: Lint with ruff
69
+ run: uv run ruff check edda tests
70
+
71
+ - name: Check formatting
72
+ run: uv run black --check edda tests
@@ -0,0 +1,46 @@
1
+ name: Deploy Documentation
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+ pages: write
12
+ id-token: write
13
+
14
+ jobs:
15
+ build:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - name: Checkout code
19
+ uses: actions/checkout@v4
20
+
21
+ - name: Set up Python
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: "3.11"
25
+
26
+ - name: Install Zensical
27
+ run: pip install zensical
28
+
29
+ - name: Build documentation
30
+ run: zensical build --clean
31
+
32
+ - name: Upload artifact
33
+ uses: actions/upload-pages-artifact@v4
34
+ with:
35
+ path: 'site/' # Zensical's default output directory
36
+
37
+ deploy:
38
+ needs: build
39
+ runs-on: ubuntu-latest
40
+ environment:
41
+ name: github-pages
42
+ url: ${{ steps.deployment.outputs.page_url }}
43
+ steps:
44
+ - name: Deploy to GitHub Pages
45
+ id: deployment
46
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,35 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ id-token: write # Required for PyPI Trusted Publishing (OIDC)
11
+ contents: write # Required for creating releases
12
+
13
+ jobs:
14
+ publish:
15
+ runs-on: ubuntu-latest
16
+ environment: pypi
17
+
18
+ steps:
19
+ - name: Checkout code
20
+ uses: actions/checkout@v4
21
+
22
+ - name: Set up uv
23
+ uses: astral-sh/setup-uv@v3
24
+
25
+ - name: Set up Python
26
+ run: uv python install 3.11
27
+
28
+ - name: Install dependencies
29
+ run: uv sync
30
+
31
+ - name: Build package
32
+ run: uv build
33
+
34
+ - name: Publish to PyPI
35
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,101 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ share/python-wheels/
20
+ *.egg-info/
21
+ .installed.cfg
22
+ *.egg
23
+ MANIFEST
24
+
25
+ # Virtual Environment
26
+ .venv/
27
+ venv/
28
+ ENV/
29
+ env/
30
+ .env
31
+
32
+ # Testing
33
+ .pytest_cache/
34
+ .coverage
35
+ .coverage.*
36
+ htmlcov/
37
+ .tox/
38
+ .nox/
39
+ coverage.xml
40
+ *.cover
41
+ .hypothesis/
42
+
43
+ # Type checking
44
+ .mypy_cache/
45
+ .dmypy.json
46
+ dmypy.json
47
+ .pyre/
48
+ .pytype/
49
+
50
+ # IDEs
51
+ .vscode/
52
+ .idea/
53
+ *.swp
54
+ *.swo
55
+ *~
56
+ .DS_Store
57
+
58
+ # Database files
59
+ *.db
60
+ *.sqlite
61
+ *.sqlite3
62
+
63
+ # Ruff
64
+ .ruff_cache/
65
+
66
+ # Distribution / packaging
67
+ pip-log.txt
68
+ pip-delete-this-directory.txt
69
+
70
+ # Jupyter Notebook
71
+ .ipynb_checkpoints
72
+
73
+ # Backup files
74
+ *.bak
75
+ *.backup
76
+
77
+ # Log files (root directory)
78
+ *.log
79
+
80
+ # Debug scripts (root directory only)
81
+ /debug_*.py
82
+
83
+ # Ad-hoc test scripts (root directory only - tests/ is tracked)
84
+ /test_*.py
85
+ /test_*.sh
86
+
87
+ # Temporary output files
88
+ *_output.txt
89
+ *_test_*.txt
90
+
91
+ # SQLite temporary directories
92
+ sqlite:/
93
+
94
+ # Local documentation (personal/development notes)
95
+ local-docs/
96
+
97
+ # MkDocs build output
98
+ site/
99
+
100
+ # Cache directories
101
+ .cache/
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,131 @@
1
+ # Edda - Development Commands
2
+ # Run `just` to see available commands
3
+
4
+ # Default recipe - show available commands
5
+ default:
6
+ @just --list
7
+
8
+ # Install development dependencies
9
+ install:
10
+ uv sync --extra dev
11
+
12
+ # Install all dependencies including database drivers
13
+ install-all:
14
+ uv sync --extra dev --extra postgresql --extra mysql
15
+
16
+ # Install viewer dependencies
17
+ install-viewer:
18
+ uv sync --extra viewer
19
+
20
+ # Run tests with coverage
21
+ test:
22
+ uv run python -m pytest
23
+
24
+ # Run tests without coverage (faster)
25
+ test-fast:
26
+ uv run python -m pytest --no-cov
27
+
28
+ # Run specific test file
29
+ test-file FILE:
30
+ uv run python -m pytest {{FILE}} --no-cov -v
31
+
32
+ # Format code with black
33
+ format:
34
+ uv run black edda tests
35
+
36
+ # Check code formatting
37
+ format-check:
38
+ uv run black --check edda tests
39
+
40
+ # Lint code with ruff
41
+ lint:
42
+ uv run ruff check edda tests
43
+
44
+ # Type check with mypy
45
+ type-check:
46
+ uv run mypy edda
47
+
48
+ # Run all checks (format, lint, type-check, test)
49
+ check: format-check lint type-check test
50
+
51
+ # Auto-fix issues (format + lint with auto-fix)
52
+ fix:
53
+ uv run black edda tests
54
+ uv run ruff check --fix edda tests
55
+
56
+ # Run demo application (installs dependencies if needed)
57
+ demo:
58
+ @echo "Stopping existing demo app on port 8001..."
59
+ @lsof -ti :8001 | xargs kill -15 2>/dev/null || true
60
+ @sleep 1
61
+ @lsof -ti :8001 | xargs kill -9 2>/dev/null || true
62
+ @echo "Ensuring server dependencies are installed..."
63
+ @uv sync --extra server --quiet
64
+ uv run tsuno demo_app:application --bind 127.0.0.1:8001
65
+
66
+ # Run demo application with PostgreSQL (requires local PostgreSQL)
67
+ # Usage: just demo-postgresql
68
+ # Requires: EDDA_POSTGRES_PASSWORD environment variable
69
+ demo-postgresql:
70
+ @echo "Stopping existing demo app on port 8001..."
71
+ @lsof -ti :8001 | xargs kill -15 2>/dev/null || true
72
+ @sleep 1
73
+ @lsof -ti :8001 | xargs kill -9 2>/dev/null || true
74
+ @echo "Ensuring server and PostgreSQL dependencies are installed..."
75
+ @uv sync --extra server --extra postgresql --quiet
76
+ @echo "Starting demo app with PostgreSQL..."
77
+ EDDA_DB_URL="postgresql+asyncpg://postgres:{{env_var('EDDA_POSTGRES_PASSWORD')}}@localhost:5432/edda" \
78
+ uv run tsuno demo_app:application --bind 127.0.0.1:8001
79
+
80
+ # Run viewer with demo_app using PostgreSQL (requires local PostgreSQL)
81
+ # Usage: just demo-postgresql-viewer [PORT]
82
+ # Requires: EDDA_POSTGRES_PASSWORD environment variable
83
+ demo-postgresql-viewer PORT='8080':
84
+ @echo "Stopping existing viewer on port {{PORT}}..."
85
+ @lsof -ti :{{PORT}} | xargs kill -15 2>/dev/null || true
86
+ @sleep 1
87
+ @lsof -ti :{{PORT}} | xargs kill -9 2>/dev/null || true
88
+ @echo "Ensuring viewer and PostgreSQL dependencies are installed..."
89
+ @uv sync --extra viewer --extra postgresql --quiet
90
+ @echo "Starting viewer with PostgreSQL and demo_app..."
91
+ EDDA_DB_URL="postgresql+asyncpg://postgres:{{env_var('EDDA_POSTGRES_PASSWORD')}}@localhost:5432/edda" \
92
+ uv run edda-viewer --port {{PORT}} --import-module demo_app
93
+
94
+ # Run viewer application (installs dependencies if needed)
95
+ # Usage: just viewer [DB] [PORT] [MODULES]
96
+ # Examples:
97
+ # just viewer # No modules, demo.db, port 8080
98
+ # just viewer my.db # Custom DB, no modules
99
+ # just viewer demo.db 8080 "-m demo_app" # With demo_app module
100
+ # just viewer demo.db 8080 "-m demo_app -m my_app" # Multiple modules
101
+ viewer DB='demo.db' PORT='8080' MODULES='':
102
+ @echo "Stopping existing viewer on port {{PORT}}..."
103
+ @lsof -ti :{{PORT}} | xargs kill -15 2>/dev/null || true
104
+ @sleep 1
105
+ @lsof -ti :{{PORT}} | xargs kill -9 2>/dev/null || true
106
+ @echo "Ensuring viewer dependencies are installed..."
107
+ @uv sync --extra viewer --quiet
108
+ uv run edda-viewer --db {{DB}} --port {{PORT}} {{MODULES}}
109
+
110
+ # Run viewer with demo_app (shortcut)
111
+ # Usage: just viewer-demo [DB] [PORT]
112
+ # Examples:
113
+ # just viewer-demo # demo.db, port 8080, with demo_app
114
+ # just viewer-demo my.db 9000 # Custom DB and port, with demo_app
115
+ viewer-demo DB='demo.db' PORT='8080':
116
+ just viewer {{DB}} {{PORT}} "--import-module demo_app"
117
+
118
+
119
+ # Clean build artifacts and caches
120
+ clean:
121
+ rm -rf .pytest_cache
122
+ rm -rf htmlcov
123
+ rm -rf .coverage
124
+ rm -rf .mypy_cache
125
+ rm -rf .ruff_cache
126
+ rm -rf dist
127
+ rm -rf build
128
+ rm -rf *.egg-info
129
+ find . -type d -name __pycache__ -exec rm -rf {} +
130
+ find . -type f -name "*.pyc" -delete
131
+ rm -f demo.db
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Yasushi Itoh
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.