forge-mvc-workflow 1.0.0b6__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.
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.4
2
+ Name: forge-mvc-workflow
3
+ Version: 1.0.0b6
4
+ Summary: Forge workflow — statuts et transitions applicatives.
5
+ Author: Roger Cauchon
6
+ License-Expression: LicenseRef-Forge-Proprietary
7
+ Project-URL: Homepage, https://github.com/caucrogeGit/Forge
8
+ Project-URL: Repository, https://github.com/caucrogeGit/Forge
9
+ Project-URL: Documentation, https://caucrogegit.github.io/Forge/
10
+ Keywords: python,mvc,forge,workflow,statuts
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Requires-Python: >=3.12
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: forge-mvc<2,>=1.0.0b5
19
+ Requires-Dist: markupsafe>=2.0
20
+
21
+ # forge-mvc-workflow
22
+
23
+ Module workflow pour Forge — statuts et transitions applicatives.
24
+
25
+ Extrait du core Forge depuis la version 2.6.0 (ADR-004).
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ pip install forge-mvc-workflow
31
+ # ou en mode developpement
32
+ pip install -e packages/forge-mvc-workflow/
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ ```python
38
+ from forge_mvc_workflow import (
39
+ WorkflowStatus,
40
+ WorkflowTransition,
41
+ make_status,
42
+ make_transition,
43
+ validate_statuses,
44
+ validate_transitions,
45
+ can_transition,
46
+ get_available_transitions,
47
+ make_workflow_jinja_helpers,
48
+ )
49
+
50
+ # Definir des statuts
51
+ statuses = validate_statuses([
52
+ make_status("brouillon", label="Brouillon", color="gray"),
53
+ make_status("confirme", label="Confirme", color="green"),
54
+ make_status("annule", label="Annule", color="red"),
55
+ ])
56
+
57
+ # Definir des transitions autorisees
58
+ transitions = validate_transitions([
59
+ make_transition("brouillon", "confirme"),
60
+ make_transition("brouillon", "annule"),
61
+ make_transition("confirme", "annule"),
62
+ ], statuses=statuses)
63
+
64
+ # Verifier une transition
65
+ if can_transition(transitions, "brouillon", "confirme"):
66
+ ...
67
+
68
+ # Obtenir les transitions disponibles depuis un statut
69
+ available = get_available_transitions(transitions, "brouillon")
70
+ ```
71
+
72
+ ## Helpers Jinja2
73
+
74
+ ```python
75
+ env.globals.update(make_workflow_jinja_helpers())
76
+ ```
77
+
78
+ Dans un template :
79
+
80
+ ```jinja2
81
+ {{ workflow_status_badge(reservation.status) }}
82
+ {{ workflow_status_label(reservation.status) }}
83
+ ```
84
+
85
+ ## Cas d'usage
86
+
87
+ - Statut d'une reservation (brouillon → confirmee → terminee → annulee)
88
+ - Statut d'un document (redaction → validation → publie)
89
+ - Statut d'une commande (panier → payee → expediee → livree)
90
+
91
+ ## API publique
92
+
93
+ - `WorkflowStatus` — dataclass statut (name, label, color)
94
+ - `WorkflowTransition` — dataclass transition (from_status, to_status)
95
+ - `make_status(name, label, color)` — constructeur valide
96
+ - `make_transition(from_status, to_status)` — constructeur valide
97
+ - `validate_statuses(list)` — valide une liste de statuts
98
+ - `validate_transitions(list, statuses)` — valide les transitions par rapport aux statuts
99
+ - `can_transition(transitions, from_name, to_name)` — teste une transition
100
+ - `get_available_transitions(transitions, from_name)` — liste les transitions disponibles
101
+ - `make_workflow_jinja_helpers()` — dict de helpers Jinja2
@@ -0,0 +1,81 @@
1
+ # forge-mvc-workflow
2
+
3
+ Module workflow pour Forge — statuts et transitions applicatives.
4
+
5
+ Extrait du core Forge depuis la version 2.6.0 (ADR-004).
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install forge-mvc-workflow
11
+ # ou en mode developpement
12
+ pip install -e packages/forge-mvc-workflow/
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```python
18
+ from forge_mvc_workflow import (
19
+ WorkflowStatus,
20
+ WorkflowTransition,
21
+ make_status,
22
+ make_transition,
23
+ validate_statuses,
24
+ validate_transitions,
25
+ can_transition,
26
+ get_available_transitions,
27
+ make_workflow_jinja_helpers,
28
+ )
29
+
30
+ # Definir des statuts
31
+ statuses = validate_statuses([
32
+ make_status("brouillon", label="Brouillon", color="gray"),
33
+ make_status("confirme", label="Confirme", color="green"),
34
+ make_status("annule", label="Annule", color="red"),
35
+ ])
36
+
37
+ # Definir des transitions autorisees
38
+ transitions = validate_transitions([
39
+ make_transition("brouillon", "confirme"),
40
+ make_transition("brouillon", "annule"),
41
+ make_transition("confirme", "annule"),
42
+ ], statuses=statuses)
43
+
44
+ # Verifier une transition
45
+ if can_transition(transitions, "brouillon", "confirme"):
46
+ ...
47
+
48
+ # Obtenir les transitions disponibles depuis un statut
49
+ available = get_available_transitions(transitions, "brouillon")
50
+ ```
51
+
52
+ ## Helpers Jinja2
53
+
54
+ ```python
55
+ env.globals.update(make_workflow_jinja_helpers())
56
+ ```
57
+
58
+ Dans un template :
59
+
60
+ ```jinja2
61
+ {{ workflow_status_badge(reservation.status) }}
62
+ {{ workflow_status_label(reservation.status) }}
63
+ ```
64
+
65
+ ## Cas d'usage
66
+
67
+ - Statut d'une reservation (brouillon → confirmee → terminee → annulee)
68
+ - Statut d'un document (redaction → validation → publie)
69
+ - Statut d'une commande (panier → payee → expediee → livree)
70
+
71
+ ## API publique
72
+
73
+ - `WorkflowStatus` — dataclass statut (name, label, color)
74
+ - `WorkflowTransition` — dataclass transition (from_status, to_status)
75
+ - `make_status(name, label, color)` — constructeur valide
76
+ - `make_transition(from_status, to_status)` — constructeur valide
77
+ - `validate_statuses(list)` — valide une liste de statuts
78
+ - `validate_transitions(list, statuses)` — valide les transitions par rapport aux statuts
79
+ - `can_transition(transitions, from_name, to_name)` — teste une transition
80
+ - `get_available_transitions(transitions, from_name)` — liste les transitions disponibles
81
+ - `make_workflow_jinja_helpers()` — dict de helpers Jinja2
@@ -0,0 +1,54 @@
1
+ """Forge workflow — statuts et transitions applicatives.
2
+
3
+ Module officiel Forge livre separement depuis Forge 2.6.0 (ADR-004).
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from forge_mvc_workflow.jinja import (
9
+ make_workflow_jinja_helpers,
10
+ workflow_status_badge,
11
+ workflow_status_badge_class,
12
+ workflow_status_color,
13
+ workflow_status_label,
14
+ )
15
+ from forge_mvc_workflow.status import (
16
+ WorkflowStatus,
17
+ WorkflowStatusError,
18
+ find_status,
19
+ make_status,
20
+ normalize_status_name,
21
+ validate_status_name,
22
+ validate_statuses,
23
+ )
24
+ from forge_mvc_workflow.transitions import (
25
+ WorkflowTransition,
26
+ WorkflowTransitionError,
27
+ can_transition,
28
+ get_available_transitions,
29
+ make_transition,
30
+ validate_transitions,
31
+ )
32
+
33
+ __version__ = "1.0.0b6"
34
+
35
+ __all__ = [
36
+ "WorkflowStatus",
37
+ "WorkflowStatusError",
38
+ "find_status",
39
+ "make_status",
40
+ "normalize_status_name",
41
+ "validate_status_name",
42
+ "validate_statuses",
43
+ "WorkflowTransition",
44
+ "WorkflowTransitionError",
45
+ "can_transition",
46
+ "get_available_transitions",
47
+ "make_transition",
48
+ "validate_transitions",
49
+ "make_workflow_jinja_helpers",
50
+ "workflow_status_badge",
51
+ "workflow_status_badge_class",
52
+ "workflow_status_color",
53
+ "workflow_status_label",
54
+ ]
@@ -0,0 +1,70 @@
1
+ """Helpers Jinja d'affichage de statuts workflow pour Forge."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from markupsafe import Markup, escape
6
+
7
+ from .status import WorkflowStatus
8
+
9
+
10
+ _BASE = "inline-flex items-center rounded-full px-2 py-1 text-xs font-medium"
11
+
12
+ _BADGE_CLASSES: dict[str, str] = {
13
+ "gray": f"{_BASE} bg-gray-100 text-gray-700",
14
+ "blue": f"{_BASE} bg-blue-100 text-blue-700",
15
+ "green": f"{_BASE} bg-green-100 text-green-700",
16
+ "yellow": f"{_BASE} bg-yellow-100 text-yellow-700",
17
+ "red": f"{_BASE} bg-red-100 text-red-700",
18
+ "purple": f"{_BASE} bg-purple-100 text-purple-700",
19
+ }
20
+
21
+ _DEFAULT_COLOR = "gray"
22
+
23
+
24
+ def workflow_status_label(status: WorkflowStatus | str | None) -> str:
25
+ """Return the display label for a status.
26
+
27
+ Accepts a WorkflowStatus, a plain string, or None.
28
+ Falls back to the name if label is empty, or to an empty string for None.
29
+ """
30
+ if isinstance(status, WorkflowStatus):
31
+ return status.label or status.name
32
+ if status is None:
33
+ return ""
34
+ return str(status)
35
+
36
+
37
+ def workflow_status_color(status: WorkflowStatus | str | None) -> str:
38
+ """Return the color string for a status, defaulting to 'gray'."""
39
+ if isinstance(status, WorkflowStatus):
40
+ return status.color or _DEFAULT_COLOR
41
+ return _DEFAULT_COLOR
42
+
43
+
44
+ def workflow_status_badge_class(status: WorkflowStatus | str | None) -> str:
45
+ """Return Tailwind CSS classes for a status badge.
46
+
47
+ Unmapped colors fall back to the gray palette.
48
+ """
49
+ color = workflow_status_color(status)
50
+ return _BADGE_CLASSES.get(color, _BADGE_CLASSES[_DEFAULT_COLOR])
51
+
52
+
53
+ def workflow_status_badge(status: WorkflowStatus | str | None) -> Markup:
54
+ """Return a safe HTML <span> badge for a status.
55
+
56
+ Marked as Markup so Jinja2 does not double-escape it.
57
+ """
58
+ label = workflow_status_label(status)
59
+ classes = workflow_status_badge_class(status)
60
+ return Markup(f'<span class="{classes}">{escape(label)}</span>')
61
+
62
+
63
+ def make_workflow_jinja_helpers() -> dict[str, object]:
64
+ """Return a dict of workflow helpers ready for injection into a Jinja2 env."""
65
+ return {
66
+ "workflow_status_label": workflow_status_label,
67
+ "workflow_status_color": workflow_status_color,
68
+ "workflow_status_badge_class": workflow_status_badge_class,
69
+ "workflow_status_badge": workflow_status_badge,
70
+ }
@@ -0,0 +1,114 @@
1
+ """Statuts génériques de workflow pour Forge."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from dataclasses import dataclass
7
+
8
+
9
+ _VALID_NAME_RE = re.compile(r"^[a-z][a-z0-9_]*$")
10
+ _SAFE_NORMALIZE_RE = re.compile(r"[\s\-]+")
11
+ _UNSAFE_CHARS_RE = re.compile(r"[^a-z0-9_\s\-]")
12
+
13
+
14
+ class WorkflowStatusError(ValueError):
15
+ pass
16
+
17
+
18
+ @dataclass
19
+ class WorkflowStatus:
20
+ name: str
21
+ label: str = ""
22
+ color: str = ""
23
+ is_initial: bool = False
24
+ is_final: bool = False
25
+
26
+ def __post_init__(self) -> None:
27
+ self.name = validate_status_name(self.name)
28
+ if not self.label:
29
+ self.label = self.name
30
+
31
+
32
+ def normalize_status_name(value: str) -> str:
33
+ """Convert a raw string to a valid snake_case status name.
34
+
35
+ Only spaces and hyphens are converted to underscores. Any other
36
+ non-alphanumeric character causes a WorkflowStatusError.
37
+ """
38
+ if not isinstance(value, str):
39
+ raise WorkflowStatusError("Le nom de statut doit être une chaîne.")
40
+ lowered = value.strip().lower()
41
+ if _UNSAFE_CHARS_RE.search(lowered):
42
+ raise WorkflowStatusError(
43
+ f"Le nom de statut '{value}' contient des caractères non autorisés. "
44
+ "Utilisez uniquement des lettres, des chiffres, des espaces et des tirets."
45
+ )
46
+ normalized = _SAFE_NORMALIZE_RE.sub("_", lowered)
47
+ normalized = re.sub(r"_+", "_", normalized).strip("_")
48
+ return normalized
49
+
50
+
51
+ def validate_status_name(value: str) -> str:
52
+ """Validate and return a normalized status name, or raise WorkflowStatusError."""
53
+ if not isinstance(value, str):
54
+ raise WorkflowStatusError("Le nom de statut doit être une chaîne.")
55
+ if not value or not value.strip():
56
+ raise WorkflowStatusError("Le nom de statut ne peut pas être vide.")
57
+ normalized = normalize_status_name(value)
58
+ if not normalized:
59
+ raise WorkflowStatusError(
60
+ f"Le nom de statut '{value}' est invalide après normalisation."
61
+ )
62
+ if not _VALID_NAME_RE.match(normalized):
63
+ raise WorkflowStatusError(
64
+ f"Le nom de statut '{value}' contient des caractères non autorisés. "
65
+ "Utilisez uniquement des lettres minuscules, des chiffres et des tirets bas."
66
+ )
67
+ return normalized
68
+
69
+
70
+ def make_status(
71
+ name: str,
72
+ label: str = "",
73
+ color: str = "",
74
+ is_initial: bool = False,
75
+ is_final: bool = False,
76
+ ) -> WorkflowStatus:
77
+ """Create a WorkflowStatus with validated and normalized name."""
78
+ return WorkflowStatus(
79
+ name=name,
80
+ label=label,
81
+ color=color,
82
+ is_initial=is_initial,
83
+ is_final=is_final,
84
+ )
85
+
86
+
87
+ def validate_statuses(statuses: list[WorkflowStatus]) -> list[WorkflowStatus]:
88
+ """Validate a list of statuses: no duplicates, at most one initial."""
89
+ seen_names: set[str] = set()
90
+ initial_count = 0
91
+ for status in statuses:
92
+ if status.name in seen_names:
93
+ raise WorkflowStatusError(
94
+ f"Statut en doublon : '{status.name}'."
95
+ )
96
+ seen_names.add(status.name)
97
+ if status.is_initial:
98
+ initial_count += 1
99
+ if initial_count > 1:
100
+ raise WorkflowStatusError(
101
+ f"{initial_count} statuts marqués comme initiaux. "
102
+ "Au plus un statut peut être initial."
103
+ )
104
+ return statuses
105
+
106
+
107
+ def find_status(
108
+ statuses: list[WorkflowStatus], name: str
109
+ ) -> WorkflowStatus | None:
110
+ """Return the status with matching name, or None if not found."""
111
+ for status in statuses:
112
+ if status.name == name:
113
+ return status
114
+ return None
@@ -0,0 +1,87 @@
1
+ """Transitions génériques de workflow pour Forge."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from .status import WorkflowStatus, WorkflowStatusError, validate_status_name
8
+
9
+
10
+ class WorkflowTransitionError(ValueError):
11
+ pass
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class WorkflowTransition:
16
+ from_status: str
17
+ to_status: str
18
+
19
+ def __post_init__(self) -> None:
20
+ try:
21
+ object.__setattr__(self, "from_status", validate_status_name(self.from_status))
22
+ object.__setattr__(self, "to_status", validate_status_name(self.to_status))
23
+ except WorkflowStatusError as exc:
24
+ raise WorkflowTransitionError(str(exc)) from exc
25
+ if self.from_status == self.to_status:
26
+ raise WorkflowTransitionError(
27
+ f"Une transition ne peut pas pointer vers le même statut : '{self.from_status}'."
28
+ )
29
+
30
+
31
+ def make_transition(from_status: str, to_status: str) -> WorkflowTransition:
32
+ """Create a validated WorkflowTransition."""
33
+ return WorkflowTransition(from_status=from_status, to_status=to_status)
34
+
35
+
36
+ def validate_transitions(
37
+ transitions: list[WorkflowTransition],
38
+ statuses: list[WorkflowStatus] | None = None,
39
+ ) -> list[WorkflowTransition]:
40
+ """Validate a list of transitions.
41
+
42
+ Checks:
43
+ - No duplicate (from_status, to_status) pairs.
44
+ - If statuses is provided, all referenced names must exist in that list.
45
+ """
46
+ seen: set[tuple[str, str]] = set()
47
+ for t in transitions:
48
+ key = (t.from_status, t.to_status)
49
+ if key in seen:
50
+ raise WorkflowTransitionError(
51
+ f"Transition en doublon : '{t.from_status}' → '{t.to_status}'."
52
+ )
53
+ seen.add(key)
54
+
55
+ if statuses is not None:
56
+ known = {s.name for s in statuses}
57
+ for t in transitions:
58
+ if t.from_status not in known:
59
+ raise WorkflowTransitionError(
60
+ f"Statut source inconnu : '{t.from_status}'."
61
+ )
62
+ if t.to_status not in known:
63
+ raise WorkflowTransitionError(
64
+ f"Statut cible inconnu : '{t.to_status}'."
65
+ )
66
+
67
+ return transitions
68
+
69
+
70
+ def can_transition(
71
+ transitions: list[WorkflowTransition],
72
+ from_name: str,
73
+ to_name: str,
74
+ ) -> bool:
75
+ """Return True if a transition from from_name to to_name is defined."""
76
+ from_norm = validate_status_name(from_name)
77
+ to_norm = validate_status_name(to_name)
78
+ return any(t.from_status == from_norm and t.to_status == to_norm for t in transitions)
79
+
80
+
81
+ def get_available_transitions(
82
+ transitions: list[WorkflowTransition],
83
+ from_name: str,
84
+ ) -> list[WorkflowTransition]:
85
+ """Return all transitions starting from from_name."""
86
+ from_norm = validate_status_name(from_name)
87
+ return [t for t in transitions if t.from_status == from_norm]
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.4
2
+ Name: forge-mvc-workflow
3
+ Version: 1.0.0b6
4
+ Summary: Forge workflow — statuts et transitions applicatives.
5
+ Author: Roger Cauchon
6
+ License-Expression: LicenseRef-Forge-Proprietary
7
+ Project-URL: Homepage, https://github.com/caucrogeGit/Forge
8
+ Project-URL: Repository, https://github.com/caucrogeGit/Forge
9
+ Project-URL: Documentation, https://caucrogegit.github.io/Forge/
10
+ Keywords: python,mvc,forge,workflow,statuts
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Requires-Python: >=3.12
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: forge-mvc<2,>=1.0.0b5
19
+ Requires-Dist: markupsafe>=2.0
20
+
21
+ # forge-mvc-workflow
22
+
23
+ Module workflow pour Forge — statuts et transitions applicatives.
24
+
25
+ Extrait du core Forge depuis la version 2.6.0 (ADR-004).
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ pip install forge-mvc-workflow
31
+ # ou en mode developpement
32
+ pip install -e packages/forge-mvc-workflow/
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ ```python
38
+ from forge_mvc_workflow import (
39
+ WorkflowStatus,
40
+ WorkflowTransition,
41
+ make_status,
42
+ make_transition,
43
+ validate_statuses,
44
+ validate_transitions,
45
+ can_transition,
46
+ get_available_transitions,
47
+ make_workflow_jinja_helpers,
48
+ )
49
+
50
+ # Definir des statuts
51
+ statuses = validate_statuses([
52
+ make_status("brouillon", label="Brouillon", color="gray"),
53
+ make_status("confirme", label="Confirme", color="green"),
54
+ make_status("annule", label="Annule", color="red"),
55
+ ])
56
+
57
+ # Definir des transitions autorisees
58
+ transitions = validate_transitions([
59
+ make_transition("brouillon", "confirme"),
60
+ make_transition("brouillon", "annule"),
61
+ make_transition("confirme", "annule"),
62
+ ], statuses=statuses)
63
+
64
+ # Verifier une transition
65
+ if can_transition(transitions, "brouillon", "confirme"):
66
+ ...
67
+
68
+ # Obtenir les transitions disponibles depuis un statut
69
+ available = get_available_transitions(transitions, "brouillon")
70
+ ```
71
+
72
+ ## Helpers Jinja2
73
+
74
+ ```python
75
+ env.globals.update(make_workflow_jinja_helpers())
76
+ ```
77
+
78
+ Dans un template :
79
+
80
+ ```jinja2
81
+ {{ workflow_status_badge(reservation.status) }}
82
+ {{ workflow_status_label(reservation.status) }}
83
+ ```
84
+
85
+ ## Cas d'usage
86
+
87
+ - Statut d'une reservation (brouillon → confirmee → terminee → annulee)
88
+ - Statut d'un document (redaction → validation → publie)
89
+ - Statut d'une commande (panier → payee → expediee → livree)
90
+
91
+ ## API publique
92
+
93
+ - `WorkflowStatus` — dataclass statut (name, label, color)
94
+ - `WorkflowTransition` — dataclass transition (from_status, to_status)
95
+ - `make_status(name, label, color)` — constructeur valide
96
+ - `make_transition(from_status, to_status)` — constructeur valide
97
+ - `validate_statuses(list)` — valide une liste de statuts
98
+ - `validate_transitions(list, statuses)` — valide les transitions par rapport aux statuts
99
+ - `can_transition(transitions, from_name, to_name)` — teste une transition
100
+ - `get_available_transitions(transitions, from_name)` — liste les transitions disponibles
101
+ - `make_workflow_jinja_helpers()` — dict de helpers Jinja2
@@ -0,0 +1,11 @@
1
+ README.md
2
+ pyproject.toml
3
+ forge_mvc_workflow/__init__.py
4
+ forge_mvc_workflow/jinja.py
5
+ forge_mvc_workflow/status.py
6
+ forge_mvc_workflow/transitions.py
7
+ forge_mvc_workflow.egg-info/PKG-INFO
8
+ forge_mvc_workflow.egg-info/SOURCES.txt
9
+ forge_mvc_workflow.egg-info/dependency_links.txt
10
+ forge_mvc_workflow.egg-info/requires.txt
11
+ forge_mvc_workflow.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ forge-mvc<2,>=1.0.0b5
2
+ markupsafe>=2.0
@@ -0,0 +1 @@
1
+ forge_mvc_workflow
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0.3"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "forge-mvc-workflow"
7
+ version = "1.0.0b6"
8
+ description = "Forge workflow — statuts et transitions applicatives."
9
+ readme = { file = "README.md", content-type = "text/markdown" }
10
+ requires-python = ">=3.12"
11
+ authors = [
12
+ { name = "Roger Cauchon" },
13
+ ]
14
+ license = "LicenseRef-Forge-Proprietary"
15
+ keywords = ["python", "mvc", "forge", "workflow", "statuts"]
16
+ dependencies = [
17
+ "forge-mvc>=1.0.0b5,<2",
18
+ "markupsafe>=2.0",
19
+ ]
20
+ classifiers = [
21
+ "Development Status :: 4 - Beta",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
26
+ ]
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/caucrogeGit/Forge"
30
+ Repository = "https://github.com/caucrogeGit/Forge"
31
+ Documentation = "https://caucrogegit.github.io/Forge/"
32
+
33
+ [tool.setuptools.packages.find]
34
+ where = ["."]
35
+ include = ["forge_mvc_workflow*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+