roboherd 0.1.2__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.
Potentially problematic release.
This version of roboherd might be problematic. Click here for more details.
- roboherd/__init__.py +0 -0
- roboherd/__main__.py +106 -0
- roboherd/annotations/__init__.py +50 -0
- roboherd/annotations/bovine.py +41 -0
- roboherd/annotations/common.py +12 -0
- roboherd/cow/__init__.py +211 -0
- roboherd/cow/handlers.py +54 -0
- roboherd/cow/profile.py +113 -0
- roboherd/cow/test_handlers.py +76 -0
- roboherd/cow/test_init.py +56 -0
- roboherd/cow/test_profile.py +49 -0
- roboherd/cow/test_util.py +17 -0
- roboherd/cow/types.py +58 -0
- roboherd/cow/util.py +23 -0
- roboherd/examples/__init__.py +0 -0
- roboherd/examples/dev_null.py +13 -0
- roboherd/examples/json_echo.py +56 -0
- roboherd/examples/meta.py +5 -0
- roboherd/examples/moocow.py +39 -0
- roboherd/examples/number.py +72 -0
- roboherd/examples/rooster.py +22 -0
- roboherd/examples/scarecrow.py +21 -0
- roboherd/herd/__init__.py +94 -0
- roboherd/herd/builder.py +21 -0
- roboherd/herd/manager/__init__.py +46 -0
- roboherd/herd/manager/config.py +54 -0
- roboherd/herd/manager/test_config.py +65 -0
- roboherd/herd/manager/test_manager.py +42 -0
- roboherd/herd/processor.py +39 -0
- roboherd/herd/scheduler.py +50 -0
- roboherd/herd/test_herd.py +37 -0
- roboherd/herd/test_scheduler.py +17 -0
- roboherd/herd/types.py +12 -0
- roboherd/register.py +38 -0
- roboherd/test_util.py +24 -0
- roboherd/util.py +116 -0
- roboherd-0.1.2.dist-info/METADATA +23 -0
- roboherd-0.1.2.dist-info/RECORD +40 -0
- roboherd-0.1.2.dist-info/WHEEL +4 -0
- roboherd-0.1.2.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from dynaconf import Dynaconf
|
|
4
|
+
|
|
5
|
+
from roboherd.cow import RoboCow
|
|
6
|
+
|
|
7
|
+
from .config import CowConfig, HerdConfig
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture
|
|
11
|
+
def test_config():
|
|
12
|
+
config = Dynaconf()
|
|
13
|
+
config.update(
|
|
14
|
+
{
|
|
15
|
+
"cow": {
|
|
16
|
+
"moocow": {
|
|
17
|
+
"bot": "moocow:bot",
|
|
18
|
+
},
|
|
19
|
+
"rooster": {
|
|
20
|
+
"bot": "rooster:bot",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
)
|
|
25
|
+
return config
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_from_name_and_dict():
|
|
29
|
+
name = "cow"
|
|
30
|
+
value = {
|
|
31
|
+
"bot": "module:attribute",
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
cow = CowConfig.from_name_and_dict(name, value)
|
|
35
|
+
|
|
36
|
+
assert cow.name == name
|
|
37
|
+
assert cow.module == "module"
|
|
38
|
+
assert cow.attribute == "attribute"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_load_config(test_config):
|
|
42
|
+
herd = HerdConfig.from_settings(test_config)
|
|
43
|
+
|
|
44
|
+
assert len(herd.cows) == 2
|
|
45
|
+
|
|
46
|
+
moocow = herd.for_name("moocow")
|
|
47
|
+
|
|
48
|
+
assert moocow.name == "moocow"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_load_from_cow_config():
|
|
52
|
+
name = "cow"
|
|
53
|
+
value = {
|
|
54
|
+
"bot": "roboherd.examples.moocow:moocow",
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
cow = CowConfig.from_name_and_dict(name, value)
|
|
58
|
+
|
|
59
|
+
assert isinstance(cow.load(), RoboCow)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_names(test_config):
|
|
63
|
+
herd = HerdConfig.from_settings(test_config)
|
|
64
|
+
|
|
65
|
+
assert herd.names == {"moocow", "rooster"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from dynaconf import Dynaconf
|
|
3
|
+
|
|
4
|
+
from almabtrieb.model import ActorInformation
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from . import HerdManager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture
|
|
11
|
+
def test_config():
|
|
12
|
+
config = Dynaconf()
|
|
13
|
+
config.update(
|
|
14
|
+
{
|
|
15
|
+
"cow": {
|
|
16
|
+
"moocow": {
|
|
17
|
+
"bot": "roboherd.examples.moocow:moocow",
|
|
18
|
+
},
|
|
19
|
+
"rooster": {
|
|
20
|
+
"bot": "roboherd.examples.rooster:bot",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
)
|
|
25
|
+
return config
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.mark.parametrize(
|
|
29
|
+
"actor_info,result_lenth",
|
|
30
|
+
[
|
|
31
|
+
([], 2),
|
|
32
|
+
([ActorInformation(name="bot:moocow", id="http://host.test/actor")], 1),
|
|
33
|
+
([ActorInformation(name="moocow", id="http://host.test/actor")], 2),
|
|
34
|
+
([ActorInformation(name="bot:other", id="http://host.test/actor")], 2),
|
|
35
|
+
],
|
|
36
|
+
)
|
|
37
|
+
def test_cows_to_create(test_config, actor_info, result_lenth):
|
|
38
|
+
manager = HerdManager.from_settings(test_config)
|
|
39
|
+
|
|
40
|
+
result = manager.cows_to_create(actor_info)
|
|
41
|
+
|
|
42
|
+
assert len(result) == result_lenth
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
from almabtrieb import Almabtrieb
|
|
7
|
+
|
|
8
|
+
from roboherd.cow import RoboCow
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class HerdProcessor:
|
|
16
|
+
connection: Almabtrieb
|
|
17
|
+
incoming_handlers: List[RoboCow]
|
|
18
|
+
|
|
19
|
+
def create_tasks(self, task_group: asyncio.TaskGroup):
|
|
20
|
+
tasks = []
|
|
21
|
+
if len(self.incoming_handlers) > 0:
|
|
22
|
+
tasks.append(task_group.create_task(self.process_incoming(self.connection)))
|
|
23
|
+
|
|
24
|
+
return tasks
|
|
25
|
+
|
|
26
|
+
async def process_incoming(self, connection):
|
|
27
|
+
actor_id_to_cow_map = {}
|
|
28
|
+
for cow in self.incoming_handlers:
|
|
29
|
+
actor_id_to_cow_map[cow.actor_id] = cow
|
|
30
|
+
|
|
31
|
+
async for msg in connection.incoming():
|
|
32
|
+
actor_id = msg["actor"]
|
|
33
|
+
|
|
34
|
+
cow = actor_id_to_cow_map.get(actor_id)
|
|
35
|
+
logger.info(cow)
|
|
36
|
+
if cow:
|
|
37
|
+
await cow.handlers.handle(
|
|
38
|
+
msg, "incoming", connection, actor_id, cow=cow
|
|
39
|
+
)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import List, Tuple
|
|
5
|
+
|
|
6
|
+
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|
7
|
+
from apscheduler.triggers.cron import CronTrigger
|
|
8
|
+
|
|
9
|
+
from fast_depends import inject
|
|
10
|
+
|
|
11
|
+
from almabtrieb import Almabtrieb
|
|
12
|
+
|
|
13
|
+
from roboherd.cow import RoboCow, CronEntry
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class HerdScheduler:
|
|
20
|
+
entries: List[Tuple[RoboCow, CronEntry]]
|
|
21
|
+
connection: Almabtrieb
|
|
22
|
+
|
|
23
|
+
def create_task(self, task_group: asyncio.TaskGroup):
|
|
24
|
+
if len(self.entries) == 0:
|
|
25
|
+
logger.info("No tasks to schedule")
|
|
26
|
+
return
|
|
27
|
+
task_group.create_task(self.run())
|
|
28
|
+
|
|
29
|
+
async def run(self):
|
|
30
|
+
if len(self.entries) == 0:
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
scheduler = AsyncIOScheduler()
|
|
34
|
+
|
|
35
|
+
for cow, entry in self.entries:
|
|
36
|
+
trigger = CronTrigger.from_crontab(entry.crontab)
|
|
37
|
+
scheduler.add_job(
|
|
38
|
+
inject(entry.func),
|
|
39
|
+
trigger=trigger,
|
|
40
|
+
kwargs={
|
|
41
|
+
"actor_id": cow.actor_id,
|
|
42
|
+
"connection": self.connection,
|
|
43
|
+
"cow": cow,
|
|
44
|
+
},
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
scheduler.start()
|
|
48
|
+
|
|
49
|
+
while True:
|
|
50
|
+
await asyncio.sleep(60 * 60)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from roboherd.examples.moocow import moocow
|
|
4
|
+
from roboherd.examples.rooster import bot
|
|
5
|
+
from roboherd.examples.dev_null import bot as dev_null
|
|
6
|
+
|
|
7
|
+
from roboherd.cow import CronEntry
|
|
8
|
+
|
|
9
|
+
from . import RoboHerd
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.mark.parametrize("cow, length", [(moocow, 0), (bot, 1)])
|
|
13
|
+
def test_cron_entries(cow, length):
|
|
14
|
+
manager = RoboHerd(cows=[cow])
|
|
15
|
+
|
|
16
|
+
assert len(manager.cron_entries()) == length
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_cron_entries_result():
|
|
20
|
+
manager = RoboHerd(cows=[bot])
|
|
21
|
+
|
|
22
|
+
result = manager.cron_entries()[0]
|
|
23
|
+
|
|
24
|
+
assert result[0] == bot
|
|
25
|
+
assert isinstance(result[1], CronEntry)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_incoming_handlers_no_result():
|
|
29
|
+
manager = RoboHerd(cows=[dev_null])
|
|
30
|
+
|
|
31
|
+
assert len(manager.incoming_handlers()) == 0
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_incoming_handlers_result():
|
|
35
|
+
manager = RoboHerd(cows=[moocow])
|
|
36
|
+
|
|
37
|
+
assert len(manager.incoming_handlers()) == 1
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from unittest.mock import AsyncMock
|
|
3
|
+
|
|
4
|
+
from .scheduler import HerdScheduler
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
async def test_empty_scheduler_exits():
|
|
8
|
+
scheduler = HerdScheduler(entries=[], connection=AsyncMock())
|
|
9
|
+
|
|
10
|
+
await scheduler.run()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def test_scheduler_runs_with_task_group_exits():
|
|
14
|
+
scheduler = HerdScheduler(entries=[], connection=AsyncMock())
|
|
15
|
+
|
|
16
|
+
async with asyncio.TaskGroup() as tg:
|
|
17
|
+
scheduler.create_task(tg)
|
roboherd/herd/types.py
ADDED
roboherd/register.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import aiohttp
|
|
2
|
+
import tomli_w
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def create_config(name, password, domain="dev.bovine.social"):
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
>>> create_config("alice", "pass")
|
|
10
|
+
{'base_url': 'https://dev.bovine.social',
|
|
11
|
+
'connection_string': 'wss://alice:pass@dev.bovine.social/mooqtt',
|
|
12
|
+
'echo': False}
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
"base_url": f"https://{domain}",
|
|
19
|
+
"connection_string": f"wss://{name}:{password}@{domain}/mooqtt",
|
|
20
|
+
"echo": False,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
async def register(config_file, name, password, fediverse):
|
|
25
|
+
async with aiohttp.ClientSession() as session:
|
|
26
|
+
result = await session.post(
|
|
27
|
+
"https://dev.bovine.social/register",
|
|
28
|
+
json={
|
|
29
|
+
"name": name,
|
|
30
|
+
"password": password,
|
|
31
|
+
"fediverse": fediverse,
|
|
32
|
+
},
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
assert result.status == 201, "Something went wrong"
|
|
36
|
+
|
|
37
|
+
with open(config_file, "wb") as f:
|
|
38
|
+
tomli_w.dump(create_config(name, password), f)
|
roboherd/test_util.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from roboherd.cow import RoboCow
|
|
4
|
+
|
|
5
|
+
from .util import import_cow
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_import_cow():
|
|
9
|
+
cow = import_cow("roboherd.examples.moocow:moocow")
|
|
10
|
+
|
|
11
|
+
assert isinstance(cow, RoboCow)
|
|
12
|
+
assert cow.information.handle == "moocow"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_import_cow_failed():
|
|
16
|
+
with pytest.raises(ImportError):
|
|
17
|
+
import_cow("robocow:nocow")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_import_cow_with_handle():
|
|
21
|
+
cow = import_cow("roboherd.examples.moocow:moocow?handle=horse")
|
|
22
|
+
|
|
23
|
+
assert isinstance(cow, RoboCow)
|
|
24
|
+
assert cow.information.handle == "horse"
|
roboherd/util.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import logging
|
|
3
|
+
import copy
|
|
4
|
+
import importlib
|
|
5
|
+
from importlib import import_module
|
|
6
|
+
from urllib.parse import parse_qs, urlparse
|
|
7
|
+
|
|
8
|
+
from almabtrieb import Almabtrieb
|
|
9
|
+
|
|
10
|
+
from roboherd.cow import RoboCow
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def parse_connection_string(connection_string: str) -> dict:
|
|
16
|
+
"""
|
|
17
|
+
Parse a connection string into a dictionary of connection parameters.
|
|
18
|
+
|
|
19
|
+
```pycon
|
|
20
|
+
>>> parse_connection_string("ws://user:pass@host/ws")
|
|
21
|
+
{'host': 'host',
|
|
22
|
+
'port': 80,
|
|
23
|
+
'username': 'user',
|
|
24
|
+
'password': 'pass',
|
|
25
|
+
'websocket_path': '/ws'}
|
|
26
|
+
|
|
27
|
+
>>> parse_connection_string("wss://user:pass@host/ws")
|
|
28
|
+
{'host': 'host',
|
|
29
|
+
'port': 443,
|
|
30
|
+
'username': 'user',
|
|
31
|
+
'password': 'pass',
|
|
32
|
+
'websocket_path': '/ws'}
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
parsed = urlparse(connection_string)
|
|
38
|
+
|
|
39
|
+
default_port = 80 if parsed.scheme == "ws" else 443
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
"host": parsed.hostname,
|
|
43
|
+
"port": parsed.port or default_port,
|
|
44
|
+
"username": parsed.username,
|
|
45
|
+
"password": parsed.password,
|
|
46
|
+
"websocket_path": parsed.path,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def load_cow(module_name: str, attribute: str) -> RoboCow:
|
|
51
|
+
"""Loads a cow from module name and attribute"""
|
|
52
|
+
module = import_module(module_name)
|
|
53
|
+
importlib.reload(module)
|
|
54
|
+
|
|
55
|
+
cow = getattr(module, attribute)
|
|
56
|
+
|
|
57
|
+
return copy.deepcopy(cow)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def import_cow(name: str) -> RoboCow:
|
|
61
|
+
"""Imports a cow from a string of the form
|
|
62
|
+
`module.name:attribute`. Here attribute should
|
|
63
|
+
be of type [roboherd.cow.RoboCow][roboherd.cow.RoboCow].
|
|
64
|
+
|
|
65
|
+
```pycon
|
|
66
|
+
>>> cow = import_cow("roboherd.examples.moocow:moocow")
|
|
67
|
+
>>> cow.information.handle
|
|
68
|
+
'moocow'
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
"""
|
|
72
|
+
try:
|
|
73
|
+
query = None
|
|
74
|
+
module_name, attribute = name.split(":")
|
|
75
|
+
if "?" in attribute:
|
|
76
|
+
attribute, query = attribute.split("?")
|
|
77
|
+
|
|
78
|
+
cow = load_cow(module_name, attribute)
|
|
79
|
+
|
|
80
|
+
if query:
|
|
81
|
+
parsed_query = parse_qs(query)
|
|
82
|
+
handle = parsed_query.get("handle", [None])[0]
|
|
83
|
+
if handle:
|
|
84
|
+
cow.information.handle = handle
|
|
85
|
+
|
|
86
|
+
return cow
|
|
87
|
+
|
|
88
|
+
except Exception as e:
|
|
89
|
+
logger.error("Failed to import cow with name: %s", name)
|
|
90
|
+
logger.error("names should have the form module:attribute")
|
|
91
|
+
logger.exception(e)
|
|
92
|
+
|
|
93
|
+
raise ImportError("Failed to load module")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def create_connection(ctx):
|
|
97
|
+
connection_string = ctx.obj["connection_string"]
|
|
98
|
+
base_url = ctx.obj["base_url"]
|
|
99
|
+
|
|
100
|
+
if not connection_string:
|
|
101
|
+
click.echo("ERROR: No connection string provided")
|
|
102
|
+
click.echo(
|
|
103
|
+
"either provide one through --connection_string or set it in your configuration file"
|
|
104
|
+
)
|
|
105
|
+
exit(1)
|
|
106
|
+
|
|
107
|
+
if not base_url:
|
|
108
|
+
click.echo("ERROR: No base url for cows provided")
|
|
109
|
+
click.echo(
|
|
110
|
+
"either provide one through --base_url or set it in your configuration file"
|
|
111
|
+
)
|
|
112
|
+
exit(1)
|
|
113
|
+
|
|
114
|
+
ctx.obj["connection"] = Almabtrieb.from_connection_string(
|
|
115
|
+
connection_string, echo=ctx.obj["settings"].get("echo", False)
|
|
116
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: roboherd
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A Fediverse bot framework
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: almabtrieb[mqtt]>=0.1.0a1
|
|
7
|
+
Requires-Dist: apscheduler>=3.11.0
|
|
8
|
+
Requires-Dist: click>=8.1.8
|
|
9
|
+
Requires-Dist: cron-descriptor>=1.4.5
|
|
10
|
+
Requires-Dist: dynaconf>=3.2.6
|
|
11
|
+
Requires-Dist: fast-depends>=2.4.12
|
|
12
|
+
Requires-Dist: tomli-w>=1.1.0
|
|
13
|
+
Requires-Dist: watchfiles>=1.0.4
|
|
14
|
+
Provides-Extra: bovine
|
|
15
|
+
Requires-Dist: bovine>=0.5.15; extra == 'bovine'
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# Roboherd
|
|
19
|
+
|
|
20
|
+
Roboherd is a framework for building Fediverse bots
|
|
21
|
+
using the [Cattle Drive Protocol](https://bovine.codeberg.page/cattle_grid/cattle_drive/).
|
|
22
|
+
|
|
23
|
+
For more information, see the [documentation](https://bovine.codeberg.page/roboherd/) or the [repository](https://codeberg.org/bovine/roboherd/).
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
roboherd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
roboherd/__main__.py,sha256=y2Ge8zajHn4rB4SlunLzmH5MNMCxklrhTaDdMbujjq8,2873
|
|
3
|
+
roboherd/register.py,sha256=Gqa5aT2supVJMj7z21btMYRma3_WW0oK5gjZftr048s,976
|
|
4
|
+
roboherd/test_util.py,sha256=Yor_YgXtvK3WnZu6jr7DvNtTLdR9YWHCA5QufHo_w2s,535
|
|
5
|
+
roboherd/util.py,sha256=23X_-CiJoW6xd36YhTzIkXoS3y_eN5ILJDCC0GnBhm0,3065
|
|
6
|
+
roboherd/annotations/__init__.py,sha256=hdW1HypuV6duCYxEhR2a9eidgcrC5-oAM-hHyzVh10E,1425
|
|
7
|
+
roboherd/annotations/bovine.py,sha256=3LnS19pJ6Z1pSB50IaTxDnvaPlU_4qp_lFyQjAtJ5P0,1366
|
|
8
|
+
roboherd/annotations/common.py,sha256=xr8FanMMPA1DDnCdguY4DLG9DbqmziaZxNR4-ww2ewk,239
|
|
9
|
+
roboherd/cow/__init__.py,sha256=j6a1i_kAndo71Ufoy1bZr1FC9othg-cz-6x9z5yQ75E,5866
|
|
10
|
+
roboherd/cow/handlers.py,sha256=k5Tc1M--wqmZ2EZvzIfID4dp8XE0rN18puMTKkNVjjE,1491
|
|
11
|
+
roboherd/cow/profile.py,sha256=XEFU8wJYVQxrlbAhUWZSDhfxBn1IXm_6f8YBlpHvUjM,3060
|
|
12
|
+
roboherd/cow/test_handlers.py,sha256=SwrZZcU_BD2BpJUFg18mFEsyUqu7N81-MkjIaGv7whQ,1673
|
|
13
|
+
roboherd/cow/test_init.py,sha256=Te-4Z8lPpMlSv99OkzPLMtB607NSOfdRD3EB-6_3n1A,1370
|
|
14
|
+
roboherd/cow/test_profile.py,sha256=edWKVL8VmOb8XtE1OQmwDLuw4mcDtxMKBfA9Kbd71qU,1423
|
|
15
|
+
roboherd/cow/test_util.py,sha256=8FLRtVdSMmIo6NSpCpB9mS0PwOCpGgUeDAA1q6Gv0P4,430
|
|
16
|
+
roboherd/cow/types.py,sha256=GtDnvpiH0w-Ux6BkhjNOgRdZ9YOITPB16BZd51MzKfA,1709
|
|
17
|
+
roboherd/cow/util.py,sha256=oBytl3cOhYgZCCnihM3wkMxG8xQZUlohVSxCaaHDsYc,493
|
|
18
|
+
roboherd/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
roboherd/examples/dev_null.py,sha256=6SZ9wlcawSBqhdq6Zv4xPXH5eKH2g1VaRwv3hSCDucE,308
|
|
20
|
+
roboherd/examples/json_echo.py,sha256=vOwbCX7apXh87RNFdwDWZXaIpJuJO5Tcsck_9qjRpqw,1354
|
|
21
|
+
roboherd/examples/meta.py,sha256=tFhHe-DJn2J13Mm22f-gFm5I8_3uQwLU4mAkrK2xzEM,170
|
|
22
|
+
roboherd/examples/moocow.py,sha256=OkceeaRqkjR31Nue8anHtL_DMqd7finLzs-gUaKsVBo,1083
|
|
23
|
+
roboherd/examples/number.py,sha256=U71mcmLVqtOkl6vkudHKlJmxmLVCI11q1ekQOoQfTag,1987
|
|
24
|
+
roboherd/examples/rooster.py,sha256=505MzfGM6iqQtffncLOA9Oj5aDeep-_oWS7yEzJbh4o,580
|
|
25
|
+
roboherd/examples/scarecrow.py,sha256=o-54QN63LmxfufslmCDvx1ilWQ7KVMsUBtl1tPMbkEs,617
|
|
26
|
+
roboherd/herd/__init__.py,sha256=DUvzuyB-JWD-awhNzseoMvluugeD02PZF-hJrGVNtJc,2981
|
|
27
|
+
roboherd/herd/builder.py,sha256=MSVPRF0Jsxure9kdyCoYJHQ7nYilGAD0_uQaGQ-rQyE,619
|
|
28
|
+
roboherd/herd/processor.py,sha256=NkROTAPs6ZoYW_0TSDnNkAfos4cTnaFgwtW3wOFSgQY,1022
|
|
29
|
+
roboherd/herd/scheduler.py,sha256=fkR-74bFZ73DmlJje_dQSytxnFFLV5hCa067mXdwvXs,1266
|
|
30
|
+
roboherd/herd/test_herd.py,sha256=sQkzGCWdFveLklhaOJUybtl7odO-QOSDdd-_gan1py8,845
|
|
31
|
+
roboherd/herd/test_scheduler.py,sha256=wLisqRMSl734P_rjbqMNH5WTQKepwihgr7ZC32nEj80,424
|
|
32
|
+
roboherd/herd/types.py,sha256=_EidQbglm0jpsKX1EsL6U2qm_J5wCPhwUi6Avac22Ow,210
|
|
33
|
+
roboherd/herd/manager/__init__.py,sha256=n4QFJXQafHhOJZyDO-mAAWE_hoXcE9vYwFGOPqFOiJM,1409
|
|
34
|
+
roboherd/herd/manager/config.py,sha256=qfcED9PfzKzDCWaYHefYj8AImcsOFs5daGpJwUrXlV4,1313
|
|
35
|
+
roboherd/herd/manager/test_config.py,sha256=cSf6cqFFr5Hbubuc_tDHyDPbVnsKbRg12WUONn6RxIc,1260
|
|
36
|
+
roboherd/herd/manager/test_manager.py,sha256=U6xLOry1K74hd_l8ZtO56v8h1TLNn-UYYfsZJuxuCDA,1031
|
|
37
|
+
roboherd-0.1.2.dist-info/METADATA,sha256=cNehEq5m5RHO7ItfG40HFXYyPCn60hNw3cYJBgToz6E,798
|
|
38
|
+
roboherd-0.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
39
|
+
roboherd-0.1.2.dist-info/entry_points.txt,sha256=WebdVUmh8Ot-FupKJY6Du8LuFbmezt9yoy2UICqV3bE,52
|
|
40
|
+
roboherd-0.1.2.dist-info/RECORD,,
|