kanban-core 2.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 (34) hide show
  1. kanban_core-2.1.0/.gitignore +164 -0
  2. kanban_core-2.1.0/PKG-INFO +177 -0
  3. kanban_core-2.1.0/README.md +150 -0
  4. kanban_core-2.1.0/pyproject.toml +171 -0
  5. kanban_core-2.1.0/src/kanban/__init__.py +26 -0
  6. kanban_core-2.1.0/src/kanban/events.py +128 -0
  7. kanban_core-2.1.0/src/kanban/exceptions.py +52 -0
  8. kanban_core-2.1.0/src/kanban/facade.py +262 -0
  9. kanban_core-2.1.0/src/kanban/models.py +102 -0
  10. kanban_core-2.1.0/src/kanban/permissions.py +64 -0
  11. kanban_core-2.1.0/src/kanban/policies.py +66 -0
  12. kanban_core-2.1.0/src/kanban/repositories.py +116 -0
  13. kanban_core-2.1.0/src/kanban/services/__init__.py +19 -0
  14. kanban_core-2.1.0/src/kanban/services/automation.py +20 -0
  15. kanban_core-2.1.0/src/kanban/services/board_admin.py +261 -0
  16. kanban_core-2.1.0/src/kanban/services/card_workflow.py +245 -0
  17. kanban_core-2.1.0/src/kanban/services/core.py +294 -0
  18. kanban_core-2.1.0/src/kanban/services/search.py +47 -0
  19. kanban_core-2.1.0/src/kanban/spi/__init__.py +49 -0
  20. kanban_core-2.1.0/src/kanban/spi/automation.py +49 -0
  21. kanban_core-2.1.0/src/kanban/unit_of_work.py +28 -0
  22. kanban_core-2.1.0/src/kanban_adapters/__init__.py +7 -0
  23. kanban_core-2.1.0/src/kanban_adapters/events.py +54 -0
  24. kanban_core-2.1.0/src/kanban_adapters/repositories.py +50 -0
  25. kanban_core-2.1.0/src/kanban_adapters/synchronous.py +52 -0
  26. kanban_core-2.1.0/tests/__init__.py +1 -0
  27. kanban_core-2.1.0/tests/conftest.py +16 -0
  28. kanban_core-2.1.0/tests/services/__init__.py +1 -0
  29. kanban_core-2.1.0/tests/test_automation_spi.py +239 -0
  30. kanban_core-2.1.0/tests/test_basic_functionality.py +63 -0
  31. kanban_core-2.1.0/tests/test_events.py +475 -0
  32. kanban_core-2.1.0/tests/test_models.py +50 -0
  33. kanban_core-2.1.0/tests/test_permissions.py +63 -0
  34. kanban_core-2.1.0/tests/test_repositories.py +161 -0
@@ -0,0 +1,164 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+ .coverage*
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ .pybuilder/
77
+ target/
78
+
79
+ # Jupyter Notebook
80
+ .ipynb_checkpoints
81
+
82
+ # IPython
83
+ profile_default/
84
+ ipython_config.py
85
+
86
+ # pyenv
87
+ # For a library or package, you might want to ignore these files since the code is
88
+ # intended to run in multiple environments; otherwise, check them in:
89
+ # .python-version
90
+
91
+ # pipenv
92
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
+ # install all needed dependencies.
96
+ #Pipfile.lock
97
+
98
+ # poetry
99
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
100
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
101
+ # commonly ignored for libraries.
102
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
103
+ #poetry.lock
104
+
105
+ # pdm
106
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
107
+ #pdm.lock
108
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
109
+ # in version control.
110
+ # https://pdm.fming.dev/#use-with-ide
111
+ .pdm.toml
112
+
113
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
114
+ __pypackages__/
115
+
116
+ # Celery stuff
117
+ celerybeat-schedule
118
+ celerybeat.pid
119
+
120
+ # SageMath parsed files
121
+ *.sage.py
122
+
123
+ # Environments
124
+ .env
125
+ .venv
126
+ env/
127
+ venv/
128
+ ENV/
129
+ env.bak/
130
+ venv.bak/
131
+
132
+ # Spyder project settings
133
+ .spyderproject
134
+ .spyproject
135
+
136
+ # Rope project settings
137
+ .ropeproject
138
+
139
+ # mkdocs documentation
140
+ /site
141
+
142
+ # mypy
143
+ .mypy_cache/
144
+ .dmypy.json
145
+ dmypy.json
146
+
147
+ # Pyre type checker
148
+ .pyre/
149
+
150
+ # pytype static type analyzer
151
+ .pytype/
152
+
153
+ # Cython debug symbols
154
+ cython_debug/
155
+
156
+ # PyCharm
157
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
158
+ # be added to the global gitignore or merged into this project gitignore. For a PyCharm
159
+ # project, it is advised to generate a fresh project using the JetBrains template.
160
+ # Do not edit any of the settings files in the project root directory.
161
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
162
+ # be added to the global gitignore or merged into this project gitignore. For a PyCharm
163
+ # project, it is advised to generate a fresh project using the JetBrains template.
164
+ .idea/
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.5
2
+ Name: kanban-core
3
+ Version: 2.1.0
4
+ Summary: A comprehensive, extensible kanban board implementation
5
+ Project-URL: Homepage, https://github.com/adieyal/kanban-core
6
+ Project-URL: Repository, https://github.com/adieyal/kanban-core
7
+ Project-URL: Issues, https://github.com/adieyal/kanban-core/issues
8
+ Author: Adi Eyal
9
+ License: MIT
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Requires-Python: >=3.10
20
+ Provides-Extra: dev
21
+ Requires-Dist: mypy>=1.0; extra == 'dev'
22
+ Requires-Dist: pre-commit>=3.0; extra == 'dev'
23
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
24
+ Requires-Dist: pytest>=7.0; extra == 'dev'
25
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # kanban-core
29
+
30
+ [![Tests](https://github.com/adieyal/kanban-core/actions/workflows/test.yml/badge.svg)](https://github.com/adieyal/kanban-core/actions/workflows/test.yml)
31
+
32
+ A framework-independent Kanban business module for Python 3.10+.
33
+
34
+ **This is not a Kanban board app.** It's the engine underneath one — boards,
35
+ columns, cards, moves, workflow rules, permissions, automation and events —
36
+ with no UI and no database of its own. You supply persistence, authorization
37
+ and an interface; kanban-core guarantees the board behaves correctly
38
+ underneath them.
39
+
40
+ Board mechanics — moving cards, enforcing WIP limits, tracking state — get
41
+ rewritten in nearly every Kanban tool because it's hard to find an
42
+ implementation with no UI or database bolted on. kanban-core is that missing
43
+ piece.
44
+
45
+ Your application talks to one public business API, `KanbanFacade`. The module
46
+ owns its business state and invariants; persistence, authorization, search,
47
+ automation and event delivery are supplied from outside at composition time.
48
+
49
+ ![kanban-core system boundary: applications call KanbanFacade, while infrastructure supplies replaceable contracts](imgs/01-framework-kanban-boundary.png)
50
+
51
+ ## What's included
52
+
53
+ - Boards, columns and cards, with move validation, WIP limits, card ordering
54
+ and dependency-cycle checks
55
+ - Configurable workflow policies for deployment-specific rules
56
+ - Permission checks via an injected policy — not hardcoded into the module
57
+ - Search over cards via a pluggable searcher
58
+ - Automation that reacts to events and proposes actions
59
+ - An event system for audit trails, outboxes and integrations
60
+ - Deterministic testing support via injected clock and ID factory
61
+ - Zero dependencies outside the Python standard library
62
+
63
+ ## Installation
64
+
65
+ ```bash
66
+ pip install kanban-core
67
+ ```
68
+
69
+ ## Quick example
70
+
71
+ ```python
72
+ from kanban import KanbanFacade
73
+ from kanban_adapters import (
74
+ EventBus,
75
+ InMemoryKanbanRepository,
76
+ SynchronousKanbanUnitOfWork,
77
+ )
78
+
79
+ repository = InMemoryKanbanRepository()
80
+ events = EventBus()
81
+
82
+ # Commits the board state and the events produced by the same operation.
83
+ uow = SynchronousKanbanUnitOfWork(repository, events)
84
+
85
+ kanban = KanbanFacade(repository, uow)
86
+
87
+ board = kanban.create_board("alice", "Launch")
88
+ todo = board.column_named("To Do")
89
+ in_progress = board.column_named("In Progress")
90
+ assert todo is not None and in_progress is not None
91
+
92
+ card = kanban.add_card("alice", board.id, todo.id, "Publish release notes")
93
+ kanban.move_card("alice", board.id, card.id, in_progress.id)
94
+ ```
95
+
96
+ `BoardView`, `ColumnView` and `CardView` are immutable observations. Change
97
+ business state only through `KanbanFacade`.
98
+
99
+ Swap `InMemoryKanbanRepository` for a Postgres- or Django-backed
100
+ implementation and nothing above this line changes — that boundary is the
101
+ whole point of the module. See [Extending Kanban Core](docs/extensibility.md).
102
+
103
+ ### Why both a repository and a unit of work?
104
+
105
+ They have different responsibilities:
106
+
107
+ - `KanbanRepository` **reads and stores board snapshots**.
108
+ - `KanbanUnitOfWork` **commits one business operation**, including both the new
109
+ board snapshot and the events produced by that operation.
110
+
111
+ The distinction matters most in production. A database-backed unit of work can save
112
+ the board and append its events to an outbox in one transaction, so you do not end
113
+ up with committed state but a lost event.
114
+
115
+ A useful shorthand is:
116
+
117
+ > **Repository reads state. Unit of work commits an operation.**
118
+
119
+ `SynchronousKanbanUnitOfWork` is the reference implementation for tests and simple
120
+ single-process applications. It saves through the repository and then publishes
121
+ through the supplied event publisher.
122
+
123
+ ## Demo UI
124
+
125
+ The repository includes a small FastAPI example in [`kanban-demo-ui`](kanban-demo-ui/).
126
+ It is an application of the module, not part of the business package. The example
127
+ composes `KanbanFacade` with the in-memory repository, synchronous unit of work and
128
+ event bus, then keeps HTTP routes and browser behavior outside Kanban Core.
129
+
130
+ Install and run it from the repository root:
131
+
132
+ ```bash
133
+ python -m pip install -e .
134
+ python -m pip install -r kanban-demo-ui/requirements.txt
135
+ python kanban-demo-ui/run_demo.py
136
+ ```
137
+
138
+ The browser uses native HTML drag-and-drop. It updates the card list optimistically,
139
+ then sends the requested move through the facade; the server remains authoritative
140
+ for WIP limits, workflow rules, ordering and event generation. The demo deliberately
141
+ uses in-memory state and has no authentication, CSRF protection or durable outbox.
142
+
143
+ ## Documentation
144
+
145
+ - **[Quickstart](docs/quickstart.md)** — build and use a board in a few minutes.
146
+ - **[Core concepts](docs/concepts.md)** — the facade boundary, immutable views,
147
+ ports and composition.
148
+ - **[Extending Kanban Core](docs/extensibility.md)** — persistence, permissions,
149
+ workflow rules, search, automation and events.
150
+ - **[Integration patterns](docs/integration-patterns.md)** — compose the module in
151
+ web requests, workers and commands.
152
+ - **[API reference](docs/api-reference.md)** — supported facade, views, exceptions
153
+ and SPI contracts.
154
+ - **[Architecture](docs/architecture.md)** — why the module is designed this way.
155
+
156
+ ## Public contract
157
+
158
+ Only names in `kanban.__all__` are supported application API:
159
+
160
+ - `KanbanFacade` is the only business-operation entry point.
161
+ - `BoardView`, `ColumnView` and `CardView` are immutable return values.
162
+ - public exceptions are exported from `kanban`.
163
+ - infrastructure and extension contracts live under `kanban.spi`.
164
+
165
+ Internal models and application services are implementation details.
166
+
167
+ ## Development
168
+
169
+ ```bash
170
+ python -m pip install -e ".[dev]"
171
+ make check
172
+ make test
173
+ ```
174
+
175
+ ## License
176
+
177
+ MIT
@@ -0,0 +1,150 @@
1
+ # kanban-core
2
+
3
+ [![Tests](https://github.com/adieyal/kanban-core/actions/workflows/test.yml/badge.svg)](https://github.com/adieyal/kanban-core/actions/workflows/test.yml)
4
+
5
+ A framework-independent Kanban business module for Python 3.10+.
6
+
7
+ **This is not a Kanban board app.** It's the engine underneath one — boards,
8
+ columns, cards, moves, workflow rules, permissions, automation and events —
9
+ with no UI and no database of its own. You supply persistence, authorization
10
+ and an interface; kanban-core guarantees the board behaves correctly
11
+ underneath them.
12
+
13
+ Board mechanics — moving cards, enforcing WIP limits, tracking state — get
14
+ rewritten in nearly every Kanban tool because it's hard to find an
15
+ implementation with no UI or database bolted on. kanban-core is that missing
16
+ piece.
17
+
18
+ Your application talks to one public business API, `KanbanFacade`. The module
19
+ owns its business state and invariants; persistence, authorization, search,
20
+ automation and event delivery are supplied from outside at composition time.
21
+
22
+ ![kanban-core system boundary: applications call KanbanFacade, while infrastructure supplies replaceable contracts](imgs/01-framework-kanban-boundary.png)
23
+
24
+ ## What's included
25
+
26
+ - Boards, columns and cards, with move validation, WIP limits, card ordering
27
+ and dependency-cycle checks
28
+ - Configurable workflow policies for deployment-specific rules
29
+ - Permission checks via an injected policy — not hardcoded into the module
30
+ - Search over cards via a pluggable searcher
31
+ - Automation that reacts to events and proposes actions
32
+ - An event system for audit trails, outboxes and integrations
33
+ - Deterministic testing support via injected clock and ID factory
34
+ - Zero dependencies outside the Python standard library
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ pip install kanban-core
40
+ ```
41
+
42
+ ## Quick example
43
+
44
+ ```python
45
+ from kanban import KanbanFacade
46
+ from kanban_adapters import (
47
+ EventBus,
48
+ InMemoryKanbanRepository,
49
+ SynchronousKanbanUnitOfWork,
50
+ )
51
+
52
+ repository = InMemoryKanbanRepository()
53
+ events = EventBus()
54
+
55
+ # Commits the board state and the events produced by the same operation.
56
+ uow = SynchronousKanbanUnitOfWork(repository, events)
57
+
58
+ kanban = KanbanFacade(repository, uow)
59
+
60
+ board = kanban.create_board("alice", "Launch")
61
+ todo = board.column_named("To Do")
62
+ in_progress = board.column_named("In Progress")
63
+ assert todo is not None and in_progress is not None
64
+
65
+ card = kanban.add_card("alice", board.id, todo.id, "Publish release notes")
66
+ kanban.move_card("alice", board.id, card.id, in_progress.id)
67
+ ```
68
+
69
+ `BoardView`, `ColumnView` and `CardView` are immutable observations. Change
70
+ business state only through `KanbanFacade`.
71
+
72
+ Swap `InMemoryKanbanRepository` for a Postgres- or Django-backed
73
+ implementation and nothing above this line changes — that boundary is the
74
+ whole point of the module. See [Extending Kanban Core](docs/extensibility.md).
75
+
76
+ ### Why both a repository and a unit of work?
77
+
78
+ They have different responsibilities:
79
+
80
+ - `KanbanRepository` **reads and stores board snapshots**.
81
+ - `KanbanUnitOfWork` **commits one business operation**, including both the new
82
+ board snapshot and the events produced by that operation.
83
+
84
+ The distinction matters most in production. A database-backed unit of work can save
85
+ the board and append its events to an outbox in one transaction, so you do not end
86
+ up with committed state but a lost event.
87
+
88
+ A useful shorthand is:
89
+
90
+ > **Repository reads state. Unit of work commits an operation.**
91
+
92
+ `SynchronousKanbanUnitOfWork` is the reference implementation for tests and simple
93
+ single-process applications. It saves through the repository and then publishes
94
+ through the supplied event publisher.
95
+
96
+ ## Demo UI
97
+
98
+ The repository includes a small FastAPI example in [`kanban-demo-ui`](kanban-demo-ui/).
99
+ It is an application of the module, not part of the business package. The example
100
+ composes `KanbanFacade` with the in-memory repository, synchronous unit of work and
101
+ event bus, then keeps HTTP routes and browser behavior outside Kanban Core.
102
+
103
+ Install and run it from the repository root:
104
+
105
+ ```bash
106
+ python -m pip install -e .
107
+ python -m pip install -r kanban-demo-ui/requirements.txt
108
+ python kanban-demo-ui/run_demo.py
109
+ ```
110
+
111
+ The browser uses native HTML drag-and-drop. It updates the card list optimistically,
112
+ then sends the requested move through the facade; the server remains authoritative
113
+ for WIP limits, workflow rules, ordering and event generation. The demo deliberately
114
+ uses in-memory state and has no authentication, CSRF protection or durable outbox.
115
+
116
+ ## Documentation
117
+
118
+ - **[Quickstart](docs/quickstart.md)** — build and use a board in a few minutes.
119
+ - **[Core concepts](docs/concepts.md)** — the facade boundary, immutable views,
120
+ ports and composition.
121
+ - **[Extending Kanban Core](docs/extensibility.md)** — persistence, permissions,
122
+ workflow rules, search, automation and events.
123
+ - **[Integration patterns](docs/integration-patterns.md)** — compose the module in
124
+ web requests, workers and commands.
125
+ - **[API reference](docs/api-reference.md)** — supported facade, views, exceptions
126
+ and SPI contracts.
127
+ - **[Architecture](docs/architecture.md)** — why the module is designed this way.
128
+
129
+ ## Public contract
130
+
131
+ Only names in `kanban.__all__` are supported application API:
132
+
133
+ - `KanbanFacade` is the only business-operation entry point.
134
+ - `BoardView`, `ColumnView` and `CardView` are immutable return values.
135
+ - public exceptions are exported from `kanban`.
136
+ - infrastructure and extension contracts live under `kanban.spi`.
137
+
138
+ Internal models and application services are implementation details.
139
+
140
+ ## Development
141
+
142
+ ```bash
143
+ python -m pip install -e ".[dev]"
144
+ make check
145
+ make test
146
+ ```
147
+
148
+ ## License
149
+
150
+ MIT
@@ -0,0 +1,171 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "kanban-core"
7
+ version = "2.1.0"
8
+ description = "A comprehensive, extensible kanban board implementation"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ authors = [{ name = "Adi Eyal" }]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Operating System :: OS Independent",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.10",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Topic :: Software Development :: Libraries :: Python Modules",
22
+ ]
23
+ requires-python = ">=3.10"
24
+ dependencies = []
25
+
26
+ [project.optional-dependencies]
27
+ dev = [
28
+ "pytest>=7.0",
29
+ "pytest-cov>=4.0",
30
+ "ruff>=0.1.0",
31
+ "pre-commit>=3.0",
32
+ "mypy>=1.0",
33
+ ]
34
+
35
+ [project.urls]
36
+ Homepage = "https://github.com/adieyal/kanban-core"
37
+ Repository = "https://github.com/adieyal/kanban-core"
38
+ Issues = "https://github.com/adieyal/kanban-core/issues"
39
+
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/kanban", "src/kanban_adapters"]
42
+
43
+ [tool.hatch.build.targets.sdist]
44
+ include = [
45
+ "/src",
46
+ "/tests",
47
+ "/README.md",
48
+ "/LICENSE",
49
+ ]
50
+
51
+ # Ruff configuration
52
+ [tool.ruff]
53
+ target-version = "py310"
54
+ line-length = 88
55
+ src = ["src", "tests"]
56
+
57
+ [tool.ruff.lint]
58
+ select = [
59
+ "E", # pycodestyle errors
60
+ "W", # pycodestyle warnings
61
+ "F", # pyflakes
62
+ "I", # isort
63
+ "B", # flake8-bugbear
64
+ "C4", # flake8-comprehensions
65
+ "UP", # pyupgrade
66
+ "ARG", # flake8-unused-arguments
67
+ "SIM", # flake8-simplify
68
+ ]
69
+ ignore = [
70
+ "E501", # line too long (handled by formatter)
71
+ "B008", # do not perform function calls in argument defaults
72
+ "B904", # raise ... from None
73
+ ]
74
+
75
+ [tool.ruff.lint.per-file-ignores]
76
+ "tests/**/*" = ["ARG", "S101"] # Allow unused args and assert statements in tests
77
+
78
+ [tool.ruff.lint.isort]
79
+ known-first-party = ["kanban", "kanban_adapters"]
80
+
81
+ [tool.ruff.format]
82
+ quote-style = "double"
83
+ indent-style = "space"
84
+ skip-magic-trailing-comma = false
85
+ line-ending = "auto"
86
+
87
+ # Pytest configuration
88
+ [tool.pytest.ini_options]
89
+ testpaths = ["tests"]
90
+ python_files = ["test_*.py", "*_test.py"]
91
+ python_classes = ["Test*"]
92
+ python_functions = ["test_*"]
93
+ addopts = [
94
+ "--strict-markers",
95
+ "--strict-config",
96
+ "--verbose",
97
+ ]
98
+ filterwarnings = [
99
+ "error",
100
+ "ignore::UserWarning",
101
+ ]
102
+
103
+ [tool.importlinter]
104
+ root_packages = ["kanban", "kanban_adapters"]
105
+
106
+ [[tool.importlinter.contracts]]
107
+ name = "Adapters use only the kanban SPI"
108
+ type = "forbidden"
109
+ source_modules = ["kanban_adapters"]
110
+ forbidden_modules = [
111
+ "kanban",
112
+ ]
113
+ ignore_imports = [
114
+ "kanban_adapters.events -> kanban.spi",
115
+ "kanban_adapters.repositories -> kanban.spi",
116
+ "kanban_adapters.synchronous -> kanban.spi",
117
+ ]
118
+
119
+ # Coverage configuration
120
+ [tool.coverage.run]
121
+ source = ["src"]
122
+ branch = true
123
+ omit = [
124
+ "*/tests/*",
125
+ "*/test_*",
126
+ "*/__init__.py",
127
+ ]
128
+
129
+ [tool.coverage.report]
130
+ show_missing = true
131
+ skip_covered = false
132
+ skip_empty = true
133
+ precision = 2
134
+ exclude_lines = [
135
+ "pragma: no cover",
136
+ "def __repr__",
137
+ "if self.debug:",
138
+ "if settings.DEBUG",
139
+ "raise AssertionError",
140
+ "raise NotImplementedError",
141
+ "if 0:",
142
+ "if __name__ == .__main__.:",
143
+ "class .*\\bProtocol\\):",
144
+ "@(abc\\.)?abstractmethod",
145
+ "\\.\\.\\.", # Ellipsis
146
+ "pass",
147
+ ]
148
+
149
+ [tool.coverage.html]
150
+ directory = "htmlcov"
151
+
152
+ [tool.coverage.xml]
153
+ output = "coverage.xml"
154
+
155
+ # MyPy configuration
156
+ [tool.mypy]
157
+ python_version = "3.10"
158
+ check_untyped_defs = true
159
+ disallow_any_generics = true
160
+ disallow_incomplete_defs = true
161
+ disallow_untyped_defs = true
162
+ no_implicit_optional = true
163
+ warn_redundant_casts = true
164
+ warn_unused_ignores = true
165
+ warn_return_any = true
166
+ strict_equality = true
167
+
168
+ [dependency-groups]
169
+ dev = [
170
+ "import-linter>=2.13",
171
+ ]
@@ -0,0 +1,26 @@
1
+ """Public API for the kanban business module."""
2
+
3
+ from .exceptions import (
4
+ ConcurrencyError,
5
+ InvalidPersistenceStateError,
6
+ KanbanError,
7
+ NotFoundError,
8
+ PermissionError,
9
+ ValidationError,
10
+ )
11
+ from .facade import BoardView, CardView, ColumnView, KanbanFacade
12
+
13
+ __version__ = "2.1.0"
14
+
15
+ __all__ = [
16
+ "BoardView",
17
+ "CardView",
18
+ "ColumnView",
19
+ "ConcurrencyError",
20
+ "InvalidPersistenceStateError",
21
+ "KanbanError",
22
+ "KanbanFacade",
23
+ "NotFoundError",
24
+ "PermissionError",
25
+ "ValidationError",
26
+ ]