priomesh-python 0.1.0__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.
- priomesh/__init__.py +26 -0
- priomesh/models.py +24 -0
- priomesh/policies/__init__.py +4 -0
- priomesh/policies/priority.py +30 -0
- priomesh/policies/subqueue.py +52 -0
- priomesh/queue.py +88 -0
- priomesh_python-0.1.0.dist-info/METADATA +181 -0
- priomesh_python-0.1.0.dist-info/RECORD +11 -0
- priomesh_python-0.1.0.dist-info/WHEEL +5 -0
- priomesh_python-0.1.0.dist-info/licenses/LICENSE +21 -0
- priomesh_python-0.1.0.dist-info/top_level.txt +1 -0
priomesh/__init__.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from .models import Job, Priority, SubQueue
|
|
2
|
+
from .policies import (
|
|
3
|
+
AbsolutePriorityPolicy,
|
|
4
|
+
AgingWeightedRandomSubQueuePolicy,
|
|
5
|
+
FifoSubQueuePolicy,
|
|
6
|
+
PriorityPolicy,
|
|
7
|
+
RandomPriorityPolicy,
|
|
8
|
+
RandomSubQueuePolicy,
|
|
9
|
+
SubQueuePolicy,
|
|
10
|
+
)
|
|
11
|
+
from .queue import PriorityFairQueue, QueueShutDown
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"AbsolutePriorityPolicy",
|
|
15
|
+
"AgingWeightedRandomSubQueuePolicy",
|
|
16
|
+
"FifoSubQueuePolicy",
|
|
17
|
+
"Job",
|
|
18
|
+
"Priority",
|
|
19
|
+
"PriorityFairQueue",
|
|
20
|
+
"QueueShutDown",
|
|
21
|
+
"PriorityPolicy",
|
|
22
|
+
"RandomPriorityPolicy",
|
|
23
|
+
"RandomSubQueuePolicy",
|
|
24
|
+
"SubQueue",
|
|
25
|
+
"SubQueuePolicy",
|
|
26
|
+
]
|
priomesh/models.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Job, Priority, SubQueue
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from enum import IntEnum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Priority(IntEnum):
|
|
7
|
+
CRITICAL = 1
|
|
8
|
+
HIGH = 2
|
|
9
|
+
NORMAL = 3
|
|
10
|
+
LOW = 4
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class Job:
|
|
15
|
+
name: str
|
|
16
|
+
priority: Priority
|
|
17
|
+
subqueue: str = "default"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=False)
|
|
21
|
+
class SubQueue:
|
|
22
|
+
name: str
|
|
23
|
+
waiting_rounds: int
|
|
24
|
+
pending_jobs: int = 1
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import random
|
|
2
|
+
from collections.abc import Sequence
|
|
3
|
+
from typing import Protocol
|
|
4
|
+
|
|
5
|
+
from ..models import Priority
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PriorityPolicy(Protocol):
|
|
9
|
+
def choose(self, active_priorities: Sequence[Priority]) -> Priority:
|
|
10
|
+
"""Choose one currently active priority."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AbsolutePriorityPolicy:
|
|
14
|
+
"""Always choose the highest-priority active level."""
|
|
15
|
+
|
|
16
|
+
def choose(self, active_priorities: Sequence[Priority]) -> Priority:
|
|
17
|
+
return min(active_priorities)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RandomPriorityPolicy:
|
|
21
|
+
"""Randomly choose an active priority, favoring higher priorities."""
|
|
22
|
+
|
|
23
|
+
def choose(self, active_priorities: Sequence[Priority]) -> Priority:
|
|
24
|
+
ordered_priorities = sorted(active_priorities)
|
|
25
|
+
priority_weights = {
|
|
26
|
+
priority: len(ordered_priorities) - index
|
|
27
|
+
for index, priority in enumerate(ordered_priorities)
|
|
28
|
+
}
|
|
29
|
+
weights = [priority_weights[priority] for priority in active_priorities]
|
|
30
|
+
return random.choices(active_priorities, weights=weights, k=1)[0]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import random
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from typing import Protocol
|
|
5
|
+
from ..models import SubQueue
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SubQueuePolicy(Protocol):
|
|
9
|
+
def choose(self, subqueues: Sequence[SubQueue]) -> SubQueue:
|
|
10
|
+
"""Choose one currently active subqueue."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AgingWeightedRandomSubQueuePolicy:
|
|
14
|
+
"""Favor larger backlogs while increasing chances for waiting subqueues.
|
|
15
|
+
|
|
16
|
+
Each active subqueue's weight is sqrt(pending jobs) * (waiting rounds + 1).
|
|
17
|
+
The square root gives deeper queues more service opportunities without
|
|
18
|
+
making a small queue's chance negligible. Aging raises the weight of a
|
|
19
|
+
subqueue each time it is not selected and resets it when it is selected.
|
|
20
|
+
Selection remains probabilistic; this policy does not impose a wait limit.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def choose(self, subqueues: Sequence[SubQueue]) -> SubQueue:
|
|
24
|
+
weights = [
|
|
25
|
+
math.sqrt(subqueue.pending_jobs) * (subqueue.waiting_rounds + 1)
|
|
26
|
+
for subqueue in subqueues
|
|
27
|
+
]
|
|
28
|
+
selected = random.choices(
|
|
29
|
+
subqueues,
|
|
30
|
+
weights=weights,
|
|
31
|
+
k=1,
|
|
32
|
+
)[0]
|
|
33
|
+
for subqueue in subqueues:
|
|
34
|
+
if subqueue is selected:
|
|
35
|
+
subqueue.waiting_rounds = 0
|
|
36
|
+
else:
|
|
37
|
+
subqueue.waiting_rounds += 1
|
|
38
|
+
return selected
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class RandomSubQueuePolicy:
|
|
42
|
+
"""Choose an active subqueue uniformly at random."""
|
|
43
|
+
|
|
44
|
+
def choose(self, subqueues: Sequence[SubQueue]) -> SubQueue:
|
|
45
|
+
return random.choice(subqueues)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class FifoSubQueuePolicy:
|
|
49
|
+
"""Choose the subqueue at the front of the rotation."""
|
|
50
|
+
|
|
51
|
+
def choose(self, subqueues: Sequence[SubQueue]) -> SubQueue:
|
|
52
|
+
return subqueues[0]
|
priomesh/queue.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from collections import deque
|
|
3
|
+
from .models import Priority, SubQueue, Job
|
|
4
|
+
|
|
5
|
+
from .policies import (
|
|
6
|
+
PriorityPolicy,
|
|
7
|
+
SubQueuePolicy,
|
|
8
|
+
RandomPriorityPolicy,
|
|
9
|
+
AgingWeightedRandomSubQueuePolicy
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class QueueShutDown(Exception):
|
|
14
|
+
"""Raised when an operation is attempted on a shut-down queue."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PriorityFairQueue:
|
|
18
|
+
"""Schedule jobs by priority and distribute service fairly among subqueues.
|
|
19
|
+
|
|
20
|
+
The aging-weighted-random policy increases a subqueue's selection weight
|
|
21
|
+
with each round it is not selected. This provides probabilistic fairness:
|
|
22
|
+
longer-waiting active subqueues become increasingly likely to be served.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
priority_policy: PriorityPolicy | None = None,
|
|
28
|
+
subqueue_policy: SubQueuePolicy | None = None,
|
|
29
|
+
) -> None:
|
|
30
|
+
self._priority_policy = priority_policy or RandomPriorityPolicy()
|
|
31
|
+
self._subqueue_policy = subqueue_policy or AgingWeightedRandomSubQueuePolicy()
|
|
32
|
+
self._queues: dict[Priority, dict[str, deque[Job]]] = {}
|
|
33
|
+
self._rotation: dict[Priority, list[SubQueue]] = {}
|
|
34
|
+
self._condition = asyncio.Condition()
|
|
35
|
+
self._shutdown = False
|
|
36
|
+
|
|
37
|
+
def empty(self) -> bool:
|
|
38
|
+
return not self._rotation
|
|
39
|
+
|
|
40
|
+
async def put(self, job: Job) -> None:
|
|
41
|
+
async with self._condition:
|
|
42
|
+
if self._shutdown:
|
|
43
|
+
raise QueueShutDown("cannot put into a shut-down queue")
|
|
44
|
+
level_queues = self._queues.setdefault(job.priority, {})
|
|
45
|
+
level_queues.setdefault(job.subqueue, deque()).append(job)
|
|
46
|
+
|
|
47
|
+
rotation = self._rotation.setdefault(job.priority, [])
|
|
48
|
+
subqueue = next(
|
|
49
|
+
(entry for entry in rotation if entry.name == job.subqueue),
|
|
50
|
+
None,
|
|
51
|
+
)
|
|
52
|
+
if subqueue is None:
|
|
53
|
+
rotation.append(SubQueue(name=job.subqueue, waiting_rounds=0))
|
|
54
|
+
else:
|
|
55
|
+
subqueue.pending_jobs += 1
|
|
56
|
+
self._condition.notify()
|
|
57
|
+
|
|
58
|
+
async def get(self) -> Job:
|
|
59
|
+
async with self._condition:
|
|
60
|
+
while True:
|
|
61
|
+
active_priorities = [p for p, r in self._rotation.items() if r]
|
|
62
|
+
if active_priorities:
|
|
63
|
+
priority = self._priority_policy.choose(active_priorities)
|
|
64
|
+
r = self._rotation[priority]
|
|
65
|
+
|
|
66
|
+
subqueue = self._subqueue_policy.choose(r)
|
|
67
|
+
r.remove(subqueue)
|
|
68
|
+
jobs = self._queues[priority][subqueue.name]
|
|
69
|
+
job = jobs.popleft()
|
|
70
|
+
subqueue.pending_jobs -= 1
|
|
71
|
+
if jobs:
|
|
72
|
+
r.append(subqueue)
|
|
73
|
+
else:
|
|
74
|
+
del self._queues[priority][subqueue.name]
|
|
75
|
+
if not r:
|
|
76
|
+
del self._queues[priority]
|
|
77
|
+
del self._rotation[priority]
|
|
78
|
+
# print(f"Processing job: {job.name}, priority: {job.priority}, subqueue: {job.subqueue}")
|
|
79
|
+
return job
|
|
80
|
+
|
|
81
|
+
if self._shutdown:
|
|
82
|
+
raise QueueShutDown("queue is shut down and empty")
|
|
83
|
+
await self._condition.wait()
|
|
84
|
+
|
|
85
|
+
async def shutdown(self) -> None:
|
|
86
|
+
async with self._condition:
|
|
87
|
+
self._shutdown = True
|
|
88
|
+
self._condition.notify_all() # 唤醒所有等待的协程,促使其检查关闭态并退出
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: priomesh-python
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A pluggable priority and fair-queue scheduling library
|
|
5
|
+
License: MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2026 PrioMesh contributors
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
|
|
27
|
+
Classifier: Development Status :: 3 - Alpha
|
|
28
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
29
|
+
Classifier: Operating System :: OS Independent
|
|
30
|
+
Classifier: Programming Language :: Python :: 3
|
|
31
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
32
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
33
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
36
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
37
|
+
Requires-Python: >=3.10
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
License-File: LICENSE
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# PrioMesh
|
|
43
|
+
|
|
44
|
+
PrioMesh is an early-stage Python library for priority-based task scheduling
|
|
45
|
+
across named sub-queues. It provides pluggable priority and sub-queue policies.
|
|
46
|
+
|
|
47
|
+
The current implementation is an in-memory, single-process prototype. Durable
|
|
48
|
+
storage, workers, and distributed execution are not implemented yet.
|
|
49
|
+
|
|
50
|
+
## Requirements
|
|
51
|
+
|
|
52
|
+
- Python 3.10 or newer
|
|
53
|
+
|
|
54
|
+
## Install
|
|
55
|
+
|
|
56
|
+
For development from a local checkout:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git clone https://github.com/PrioMesh/priomesh-python.git
|
|
60
|
+
cd priomesh-python
|
|
61
|
+
python -m pip install -e .
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Once the project is published to PyPI, end users will be able to install it
|
|
65
|
+
with:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
python -m pip install priomesh-python
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Until then, users can install directly from the GitHub repository:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
python -m pip install "git+https://github.com/PrioMesh/priomesh-python.git"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Quick start
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import asyncio
|
|
81
|
+
|
|
82
|
+
from priomesh import Job, Priority, PriorityFairQueue, QueueShutDown
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
async def main() -> None:
|
|
86
|
+
queue = PriorityFairQueue()
|
|
87
|
+
|
|
88
|
+
async def consume() -> None:
|
|
89
|
+
try:
|
|
90
|
+
while True:
|
|
91
|
+
job = await queue.get()
|
|
92
|
+
print(job)
|
|
93
|
+
except QueueShutDown:
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
consumer = asyncio.create_task(consume())
|
|
97
|
+
await queue.put(Job("job-1", Priority.HIGH, "tenant-a"))
|
|
98
|
+
await queue.put(Job("job-2", Priority.HIGH, "tenant-b"))
|
|
99
|
+
await queue.shutdown()
|
|
100
|
+
await consumer
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
asyncio.run(main())
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`get()` first selects an active priority level, then selects an active
|
|
107
|
+
sub-queue within that level. Jobs within each priority/sub-queue pair are
|
|
108
|
+
returned in FIFO order. Calling `get()` on an empty queue waits until a job is
|
|
109
|
+
put; `empty()` checks whether the queue currently has any jobs.
|
|
110
|
+
|
|
111
|
+
Call `shutdown()` to stop accepting jobs. It performs a graceful shutdown:
|
|
112
|
+
`put()` raises `QueueShutDown`, while `get()` continues returning jobs that
|
|
113
|
+
were already queued. Once the queue is drained, waiting and future `get()`
|
|
114
|
+
calls raise `QueueShutDown`, allowing consumers to exit. The example above
|
|
115
|
+
shows a consumer draining the queue and stopping cleanly.
|
|
116
|
+
|
|
117
|
+
## Scheduling policies
|
|
118
|
+
|
|
119
|
+
Priority and sub-queue policies are independent and can be combined:
|
|
120
|
+
|
|
121
|
+
| Policy | Behavior | Use when |
|
|
122
|
+
| --- | --- | --- |
|
|
123
|
+
| `RandomPriorityPolicy` (default) | Randomly selects an active priority, weighting higher priorities more heavily. With all four levels active, the weights are 4:3:2:1 from `CRITICAL` to `LOW`. | Higher priorities should usually run sooner, while lower priorities should still be eligible. |
|
|
124
|
+
| `AbsolutePriorityPolicy` | Always selects the highest-priority active level. | Priority order must be strict. A continuously active higher priority can indefinitely delay lower priorities. |
|
|
125
|
+
| `AgingWeightedRandomSubQueuePolicy` (default) | Randomly selects a sub-queue with weight `sqrt(pending_jobs) * (waiting_rounds + 1)`. | Larger backlogs should receive more opportunities, while sub-queues skipped in prior selections gain weight. |
|
|
126
|
+
| `FifoSubQueuePolicy` | Selects the sub-queue at the front of the rotation; a sub-queue with remaining work goes to the back after serving one job. | Predictable round-robin service among active sub-queues at the selected priority is preferred. |
|
|
127
|
+
| `RandomSubQueuePolicy` | Selects uniformly at random among active sub-queues. | Sub-queues should have equal selection probability, regardless of backlog. |
|
|
128
|
+
|
|
129
|
+
The aging policy is probabilistic: its weights affect relative selection
|
|
130
|
+
chances, but it does not guarantee a fixed service ratio or a maximum wait.
|
|
131
|
+
`waiting_rounds` counts selections of other sub-queues at the same priority,
|
|
132
|
+
not elapsed time. When a sub-queue is selected, its waiting count resets.
|
|
133
|
+
|
|
134
|
+
Policies can be replaced when constructing the queue:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from priomesh import (
|
|
138
|
+
AbsolutePriorityPolicy,
|
|
139
|
+
AgingWeightedRandomSubQueuePolicy,
|
|
140
|
+
FifoSubQueuePolicy,
|
|
141
|
+
PriorityFairQueue,
|
|
142
|
+
RandomPriorityPolicy,
|
|
143
|
+
RandomSubQueuePolicy,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Strict priority order, with deterministic round-robin among sub-queues.
|
|
147
|
+
queue = PriorityFairQueue(
|
|
148
|
+
priority_policy=AbsolutePriorityPolicy(),
|
|
149
|
+
subqueue_policy=FifoSubQueuePolicy(),
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Probabilistic priority and uniform random sub-queue selection.
|
|
153
|
+
queue = PriorityFairQueue(
|
|
154
|
+
priority_policy=RandomPriorityPolicy(),
|
|
155
|
+
subqueue_policy=RandomSubQueuePolicy(),
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# These are also the defaults; this is equivalent to PriorityFairQueue().
|
|
159
|
+
queue = PriorityFairQueue(
|
|
160
|
+
priority_policy=RandomPriorityPolicy(),
|
|
161
|
+
subqueue_policy=AgingWeightedRandomSubQueuePolicy(),
|
|
162
|
+
)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Run the example from a checkout
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
python -m pip install -e .
|
|
169
|
+
python examples/basic.py
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Project status
|
|
173
|
+
|
|
174
|
+
PrioMesh is under active early development. The current queue is in-memory and
|
|
175
|
+
does not provide persistence, worker execution, retries, or cross-process
|
|
176
|
+
scheduling. A Go implementation is planned after the scheduling semantics
|
|
177
|
+
stabilize.
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
PrioMesh is licensed under the MIT License. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
priomesh/__init__.py,sha256=lxHKW2avoW1kHn3AaH9iRLSN2oiXLPNmAIOvxY3K3qI,605
|
|
2
|
+
priomesh/models.py,sha256=ifsb9z5nTzkFW1aYDYw6IZf0eotBjwla8IKMG1YzCyo,380
|
|
3
|
+
priomesh/queue.py,sha256=3uGobGydcIZAfP4N26rDsN4Dp2CpbvGvpJ4zjBZzPdo,3417
|
|
4
|
+
priomesh/policies/__init__.py,sha256=IGMFLCYv-dXdMwmPf1E66VW80UIRu9v_gQitvcSgHpI,215
|
|
5
|
+
priomesh/policies/priority.py,sha256=DRxO5QKEVw8wE23KuwqnEs_krh5jjS6JMwd1AxiPnic,1017
|
|
6
|
+
priomesh/policies/subqueue.py,sha256=XBTvfL3z9tEC80lKUGn0ZA7BHK9Tb2DEgHExealneXg,1692
|
|
7
|
+
priomesh_python-0.1.0.dist-info/licenses/LICENSE,sha256=f_fNKxIw-cXfGU3A-cJdTaQ_doqT1olySCAYT774wvY,1078
|
|
8
|
+
priomesh_python-0.1.0.dist-info/METADATA,sha256=SO_pJvVFWi1sGyVw2vQK32vREhEcLEESovFzO_E__JU,6928
|
|
9
|
+
priomesh_python-0.1.0.dist-info/WHEEL,sha256=a-udL2m4V_6Yn8b-uKzWb12RjRxwwMo6ohYFM24tpaY,91
|
|
10
|
+
priomesh_python-0.1.0.dist-info/top_level.txt,sha256=BnatVdwy2qyvmjYO1H9zAH2D8DFYPExzWyxxUw9g_Pg,9
|
|
11
|
+
priomesh_python-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PrioMesh contributors
|
|
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.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
priomesh
|