roboherd 0.1.3__tar.gz → 0.1.4__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.

Potentially problematic release.


This version of roboherd might be problematic. Click here for more details.

Files changed (59) hide show
  1. {roboherd-0.1.3 → roboherd-0.1.4}/.woodpecker/test.yml +13 -1
  2. {roboherd-0.1.3 → roboherd-0.1.4}/CHANGES.md +6 -0
  3. {roboherd-0.1.3 → roboherd-0.1.4}/PKG-INFO +2 -1
  4. {roboherd-0.1.3 → roboherd-0.1.4}/pyproject.toml +3 -1
  5. {roboherd-0.1.3 → roboherd-0.1.4}/resources/docker/Dockerfile +1 -1
  6. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/__main__.py +4 -2
  7. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/annotations/__init__.py +10 -4
  8. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/annotations/bovine.py +8 -8
  9. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/annotations/common.py +2 -0
  10. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/cow/__init__.py +4 -0
  11. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/cow/profile.py +15 -5
  12. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/cow/types.py +10 -8
  13. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/cow/util.py +4 -1
  14. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/examples/json_echo.py +1 -1
  15. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/examples/number.py +1 -1
  16. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/examples/rooster.py +1 -1
  17. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/examples/scarecrow.py +1 -1
  18. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/herd/__init__.py +3 -3
  19. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/herd/manager/__init__.py +3 -1
  20. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/herd/manager/test_config.py +2 -2
  21. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/herd/manager/test_manager.py +1 -1
  22. roboherd-0.1.4/roboherd/test_validators.py +10 -0
  23. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/util.py +0 -1
  24. roboherd-0.1.4/roboherd/validators.py +6 -0
  25. {roboherd-0.1.3 → roboherd-0.1.4}/uv.lock +27 -1
  26. {roboherd-0.1.3 → roboherd-0.1.4}/.gitignore +0 -0
  27. {roboherd-0.1.3 → roboherd-0.1.4}/.woodpecker/create_release.yml +0 -0
  28. {roboherd-0.1.3 → roboherd-0.1.4}/.woodpecker/publish_docker.yml +0 -0
  29. {roboherd-0.1.3 → roboherd-0.1.4}/.woodpecker/publish_pypi.yml +0 -0
  30. {roboherd-0.1.3 → roboherd-0.1.4}/.woodpecker/website.yml +0 -0
  31. {roboherd-0.1.3 → roboherd-0.1.4}/README.md +0 -0
  32. {roboherd-0.1.3 → roboherd-0.1.4}/docs/annotations.md +0 -0
  33. {roboherd-0.1.3 → roboherd-0.1.4}/docs/assets/mastodon.png +0 -0
  34. {roboherd-0.1.3 → roboherd-0.1.4}/docs/cli.md +0 -0
  35. {roboherd-0.1.3 → roboherd-0.1.4}/docs/cow.md +0 -0
  36. {roboherd-0.1.3 → roboherd-0.1.4}/docs/herd.md +0 -0
  37. {roboherd-0.1.3 → roboherd-0.1.4}/docs/index.md +0 -0
  38. {roboherd-0.1.3 → roboherd-0.1.4}/docs/util.md +0 -0
  39. {roboherd-0.1.3 → roboherd-0.1.4}/mkdocs.yml +0 -0
  40. {roboherd-0.1.3 → roboherd-0.1.4}/resources/docker/build.sh +0 -0
  41. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/__init__.py +0 -0
  42. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/cow/handlers.py +0 -0
  43. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/cow/test_handlers.py +0 -0
  44. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/cow/test_init.py +0 -0
  45. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/cow/test_profile.py +0 -0
  46. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/cow/test_util.py +0 -0
  47. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/examples/__init__.py +0 -0
  48. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/examples/dev_null.py +0 -0
  49. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/examples/meta.py +0 -0
  50. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/examples/moocow.py +0 -0
  51. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/herd/builder.py +0 -0
  52. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/herd/manager/config.py +0 -0
  53. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/herd/processor.py +0 -0
  54. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/herd/scheduler.py +0 -0
  55. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/herd/test_herd.py +0 -0
  56. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/herd/test_scheduler.py +0 -0
  57. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/herd/types.py +0 -0
  58. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/register.py +0 -0
  59. {roboherd-0.1.3 → roboherd-0.1.4}/roboherd/test_util.py +0 -0
@@ -2,11 +2,14 @@ when:
2
2
  - branch: [main]
3
3
  event: [pull_request, push]
4
4
 
5
+ matrix:
6
+ extras: ["--all-extras", ""]
7
+
5
8
  steps:
6
9
  build:
7
10
  image: ghcr.io/astral-sh/uv:python3.11-alpine
8
11
  commands:
9
- - uv sync --all-extras
12
+ - uv sync ${extras}
10
13
  test_format:
11
14
  image: ghcr.io/astral-sh/uv:python3.11-alpine
12
15
  commands:
@@ -16,7 +19,16 @@ steps:
16
19
  image: ghcr.io/astral-sh/uv:python3.11-alpine
17
20
  commands:
18
21
  - uv run pytest
22
+ test_typing:
23
+ image: ghcr.io/astral-sh/uv:python3.11-alpine
24
+ commands:
25
+ - apk add libstdc++ libgcc
26
+ - uv run pyright roboherd
27
+ when:
28
+ - matrix: { extras: "--all-extras" }
19
29
  build_docs:
20
30
  image: ghcr.io/astral-sh/uv:python3.11-alpine
21
31
  commands:
22
32
  - uv run mkdocs build
33
+ when:
34
+ - matrix: { extras: "--all-extras" }
@@ -1,5 +1,11 @@
1
1
  # Changes to roboherd
2
2
 
3
+ ## 0.1.4 ([Milestone](https://codeberg.org/bovine/roboherd/milestone/10442))
4
+
5
+ - Type check the project
6
+ - No longer require bovine [roboherd#26](https://codeberg.org/bovine/roboherd/issues/26)
7
+ - Fixed typo in Dockerfile [roboherd#28](https://codeberg.org/bovine/roboherd/issues/28)
8
+
3
9
  ## 0.1.3 ([Milestone](https://codeberg.org/bovine/roboherd/milestone/10441))
4
10
 
5
11
  - Add information about developement [roboherd#24](https://codeberg.org/bovine/roboherd/issues/24)
@@ -1,8 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: roboherd
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: A Fediverse bot framework
5
5
  Requires-Python: >=3.11
6
+ Requires-Dist: aiohttp>=3.11.12
6
7
  Requires-Dist: almabtrieb[mqtt]>=0.1.0a1
7
8
  Requires-Dist: apscheduler>=3.11.0
8
9
  Requires-Dist: click>=8.1.8
@@ -1,10 +1,11 @@
1
1
  [project]
2
2
  name = "roboherd"
3
- version = "0.1.3"
3
+ version = "0.1.4"
4
4
  description = "A Fediverse bot framework"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
7
7
  dependencies = [
8
+ "aiohttp>=3.11.12",
8
9
  "almabtrieb[mqtt]>=0.1.0a1",
9
10
  "apscheduler>=3.11.0",
10
11
  "click>=8.1.8",
@@ -37,6 +38,7 @@ dev-dependencies = [
37
38
  "griffe-fieldz>=0.2.1",
38
39
  "mkdocs-click>=0.8.1",
39
40
  "ruff>=0.9.6",
41
+ "pyright>=1.1.394",
40
42
  ]
41
43
 
42
44
  [tool.pytest.ini_options]
@@ -1,7 +1,7 @@
1
1
  FROM helgekr/bovine:0.5.18-python3.13
2
2
 
3
3
  ARG tag
4
- RUN pip install roboherd==#{tag}
4
+ RUN pip install roboherd==${tag}
5
5
 
6
6
  RUN mkdir /app
7
7
  WORKDIR /app
@@ -10,6 +10,7 @@ from roboherd.herd import RoboHerd
10
10
  from roboherd.herd.manager import HerdManager
11
11
  from roboherd.util import create_connection
12
12
  from roboherd.register import register as run_register
13
+ from roboherd.validators import validators
13
14
 
14
15
  logging.basicConfig(level=logging.INFO)
15
16
 
@@ -32,6 +33,7 @@ def main(ctx, connection_string, base_url, config_file):
32
33
  settings = dynaconf.Dynaconf(
33
34
  settings_files=[config_file],
34
35
  envvar_prefix="ROBOHERD",
36
+ validators=validators,
35
37
  )
36
38
  ctx.ensure_object(dict)
37
39
 
@@ -41,12 +43,12 @@ def main(ctx, connection_string, base_url, config_file):
41
43
  if connection_string:
42
44
  ctx.obj["connection_string"] = connection_string
43
45
  else:
44
- ctx.obj["connection_string"] = settings.get("connection_string")
46
+ ctx.obj["connection_string"] = settings.connection_string
45
47
 
46
48
  if base_url:
47
49
  ctx.obj["base_url"] = base_url
48
50
  else:
49
- ctx.obj["base_url"] = settings.get("base_url")
51
+ ctx.obj["base_url"] = settings.base_url
50
52
 
51
53
 
52
54
  @main.command()
@@ -5,11 +5,11 @@ from almabtrieb import Almabtrieb
5
5
 
6
6
 
7
7
  def get_raw(data: dict) -> dict:
8
- return data.get("data").get("raw")
8
+ return data.get("data", {}).get("raw")
9
9
 
10
10
 
11
11
  def get_parsed(data: dict) -> dict:
12
- result = data.get("data").get("parsed")
12
+ result = data.get("data", {}).get("parsed")
13
13
  if result is None:
14
14
  raise ValueError("No parsed data found")
15
15
  return result
@@ -23,11 +23,17 @@ ParsedData = Annotated[dict, Depends(get_parsed)]
23
23
 
24
24
 
25
25
  def get_activity(parsed: ParsedData) -> dict:
26
- return parsed.get("activity")
26
+ result = parsed.get("activity")
27
+ if not result:
28
+ raise ValueError("No activity found")
29
+ return result
27
30
 
28
31
 
29
32
  def get_embedded_object(parsed: ParsedData) -> dict:
30
- return parsed.get("embeddedObject")
33
+ result = parsed.get("embeddedObject")
34
+ if not result:
35
+ raise ValueError("No embedded object found")
36
+ return result
31
37
 
32
38
 
33
39
  Activity = Annotated[dict, Depends(get_activity)]
@@ -7,19 +7,19 @@ from .common import Profile
7
7
  try:
8
8
  from bovine.activitystreams import factories_for_actor_object
9
9
  from bovine.activitystreams.activity_factory import (
10
- ActivityFactory as BovineActivityFactory,
10
+ ActivityFactory as BovineActivityFactory, # type: ignore
11
11
  )
12
12
  from bovine.activitystreams.object_factory import (
13
- ObjectFactory as BovineObjectFactory,
13
+ ObjectFactory as BovineObjectFactory, # type: ignore
14
14
  )
15
15
 
16
- def get_activity_factory(profile: Profile) -> BovineActivityFactory:
16
+ def get_activity_factory(profile: Profile) -> BovineActivityFactory: # type: ignore
17
17
  activity_factory, _ = factories_for_actor_object(profile)
18
- return activity_factory
18
+ return activity_factory # type: ignore
19
19
 
20
- def get_object_factory(profile: Profile) -> BovineObjectFactory:
20
+ def get_object_factory(profile: Profile) -> BovineObjectFactory: # type: ignore
21
21
  _, object_factory = factories_for_actor_object(profile)
22
- return object_factory
22
+ return object_factory # type: ignore
23
23
 
24
24
  except ImportError:
25
25
 
@@ -34,8 +34,8 @@ except ImportError:
34
34
  raise ImportError("bovine not installed")
35
35
 
36
36
 
37
- ActivityFactory = Annotated[BovineActivityFactory, Depends(get_activity_factory)]
37
+ ActivityFactory = Annotated[BovineActivityFactory, Depends(get_activity_factory)] # type: ignore
38
38
  """The activity factory of type [bovine.activitystreams.activity_factory.ActivityFactory][]"""
39
39
 
40
- ObjectFactory = Annotated[BovineObjectFactory, Depends(get_object_factory)]
40
+ ObjectFactory = Annotated[BovineObjectFactory, Depends(get_object_factory)] # type: ignore
41
41
  """The object factory of type [bovine.activitystreams.object_factory.ObjectFactory][]"""
@@ -5,6 +5,8 @@ from roboherd.cow import RoboCow
5
5
 
6
6
 
7
7
  def get_profile(cow: RoboCow) -> dict:
8
+ if cow.profile is None:
9
+ raise ValueError("Cow has no profile")
8
10
  return cow.profile
9
11
 
10
12
 
@@ -187,7 +187,11 @@ class RoboCow:
187
187
  """Runs when the cow is birthed"""
188
188
 
189
189
  if self.profile is None:
190
+ if not self.actor_id:
191
+ raise ValueError("Actor ID is not set")
190
192
  result = await connection.fetch(self.actor_id, self.actor_id)
193
+ if not result.data:
194
+ raise ValueError("Could not retrieve profile")
191
195
  self.profile = result.data
192
196
 
193
197
  if self.cron_entries:
@@ -1,5 +1,15 @@
1
1
  from urllib.parse import urlparse
2
- from bovine.activitystreams.utils import as_list
2
+
3
+ try:
4
+ from bovine.activitystreams.utils import as_list
5
+ except ImportError:
6
+
7
+ def as_list(value):
8
+ if isinstance(value, list):
9
+ return value
10
+
11
+ return [value]
12
+
3
13
 
4
14
  from .types import Information
5
15
 
@@ -27,7 +37,7 @@ def key_index_from_attachment(attachments: list[dict], key: str) -> int | None:
27
37
 
28
38
 
29
39
  def determine_action_for_key_and_value(
30
- attachments: list[dict], key: str, value: str
40
+ attachments: list[dict], key: str, value: str | None
31
41
  ) -> dict | None:
32
42
  idx = key_index_from_attachment(attachments, key)
33
43
  if idx is None:
@@ -78,19 +88,19 @@ def determine_actions(information: Information, profile: dict) -> list[dict] | N
78
88
  "identifier": "acct:"
79
89
  + information.handle
80
90
  + "@"
81
- + urlparse(profile.get("id")).netloc,
91
+ + str(urlparse(profile.get("id")).netloc),
82
92
  "primary": True,
83
93
  }
84
94
  )
85
95
 
86
- actions = list(filter(lambda x: x, actions))
96
+ actions = [x for x in actions if x is not None]
87
97
 
88
98
  if len(actions) == 0:
89
99
  return None
90
100
  return actions
91
101
 
92
102
 
93
- def determine_profile_update(information: Information, profile: dict) -> dict:
103
+ def determine_profile_update(information: Information, profile: dict) -> dict | None:
94
104
  """Returns the update for the profile"""
95
105
 
96
106
  update = {"actor": profile.get("id")}
@@ -6,13 +6,13 @@ class MetaInformation(BaseModel):
6
6
  information such as the author and the source repository"""
7
7
 
8
8
  source: str | None = Field(
9
- None,
9
+ default=None,
10
10
  examples=["https://forge.example/repo"],
11
11
  description="The source repository",
12
12
  )
13
13
 
14
14
  author: str | None = Field(
15
- None,
15
+ default=None,
16
16
  examples=["acct:author@domain.example"],
17
17
  description="The author, often a Fediverse handle",
18
18
  )
@@ -22,23 +22,25 @@ class Information(BaseModel):
22
22
  """Information about the cow"""
23
23
 
24
24
  type: str = Field(
25
- "Service", examples=["Service"], description="ActivityPub type of the actor."
25
+ default="Service",
26
+ examples=["Service"],
27
+ description="ActivityPub type of the actor.",
26
28
  )
27
29
 
28
30
  handle: str | None = Field(
29
- None,
31
+ default=None,
30
32
  examples=["moocow"],
31
33
  description="Used as the handle in `acct:handle@domain.example`",
32
34
  )
33
35
 
34
36
  name: str | None = Field(
35
- None,
37
+ default=None,
36
38
  examples=["The mooing cow 🐮"],
37
39
  description="The display name of the cow",
38
40
  )
39
41
 
40
42
  description: str | None = Field(
41
- None,
43
+ default=None,
42
44
  examples=[
43
45
  "I'm a cow that moos.",
44
46
  """<p>An example bot to illustrate Roboherd</p><p>For more information on RoboHerd, see <a href="https://codeberg.org/bovine/roboherd">its repository</a>.</p>""",
@@ -47,12 +49,12 @@ class Information(BaseModel):
47
49
  )
48
50
 
49
51
  frequency: str | None = Field(
50
- None,
52
+ default=None,
51
53
  examples=["daily"],
52
54
  description="Frequency of posting. Is set automatically if cron expressions are used.",
53
55
  )
54
56
 
55
57
  meta_information: MetaInformation = Field(
56
- MetaInformation(),
58
+ default=MetaInformation(),
57
59
  description="Meta information about the cow, such as the source repository",
58
60
  )
@@ -19,5 +19,8 @@ async def call_handler(
19
19
  cow=None,
20
20
  ):
21
21
  return await inject(handler_info.func)(
22
- data=data, connection=connection, actor_id=actor_id, cow=cow
22
+ data=data, # type: ignore
23
+ connection=connection,
24
+ actor_id=actor_id,
25
+ cow=cow,
23
26
  )
@@ -45,7 +45,7 @@ async def create(
45
45
  raw: RawData, publish_object: PublishObject, object_factory: ObjectFactory
46
46
  ):
47
47
  note = (
48
- object_factory.reply(
48
+ object_factory.reply( # type: ignore
49
49
  raw.get("object"),
50
50
  content=reply_content(raw),
51
51
  )
@@ -51,7 +51,7 @@ async def post_number(
51
51
  ):
52
52
  number = random.randint(0, 1000)
53
53
 
54
- note = factory.note(content=f"Number: {number}").as_public().build()
54
+ note = factory.note(content=f"Number: {number}").as_public().build() # type: ignore
55
55
  await publisher(note)
56
56
 
57
57
  handle = "even" if number % 2 == 0 else "odd"
@@ -18,5 +18,5 @@ bot = RoboCow(
18
18
  @bot.cron("42 * * * *")
19
19
  async def crow(publisher: PublishObject, object_factory: ObjectFactory):
20
20
  await publisher(
21
- object_factory.note(content="cock-a-doodle-doo").as_public().build()
21
+ object_factory.note(content="cock-a-doodle-doo").as_public().build() # type: ignore
22
22
  )
@@ -18,4 +18,4 @@ bot = RoboCow(
18
18
 
19
19
  @bot.startup
20
20
  async def startup(publish_object: PublishObject, object_factory: ObjectFactory):
21
- await publish_object(object_factory.note(content="Booo! 🐦").as_public().build())
21
+ await publish_object(object_factory.note(content="Booo! 🐦").as_public().build()) # type: ignore
@@ -29,6 +29,9 @@ class RoboHerd:
29
29
  await self.process(connection)
30
30
 
31
31
  async def startup(self, connection: Almabtrieb):
32
+ if not connection.information:
33
+ raise Exception("Could not get information from server")
34
+
32
35
  self.cows = self.manager.existing_cows(connection.information.actors)
33
36
 
34
37
  cows_to_create = self.manager.cows_to_create(connection.information.actors)
@@ -60,9 +63,6 @@ class RoboHerd:
60
63
  scheduler = HerdScheduler(self.cron_entries(), connection)
61
64
  scheduler.create_task(tg)
62
65
 
63
- def introduce(self, cow: RoboCow):
64
- self.manager.add_to_herd(cow)
65
-
66
66
  def validate(self, connection):
67
67
  result = connection.information
68
68
 
@@ -43,4 +43,6 @@ class HerdManager:
43
43
  }
44
44
  names_to_create = self.herd_config.names - existing_names
45
45
 
46
- return {self.herd_config.for_name(name) for name in names_to_create}
46
+ cows = {self.herd_config.for_name(name) for name in names_to_create}
47
+
48
+ return {cow for cow in cows if cow}
@@ -21,7 +21,7 @@ def test_config():
21
21
  },
22
22
  },
23
23
  }
24
- )
24
+ ) # type: ignore
25
25
  return config
26
26
 
27
27
 
@@ -44,7 +44,7 @@ def test_load_config(test_config):
44
44
  assert len(herd.cows) == 2
45
45
 
46
46
  moocow = herd.for_name("moocow")
47
-
47
+ assert moocow
48
48
  assert moocow.name == "moocow"
49
49
 
50
50
 
@@ -21,7 +21,7 @@ def test_config():
21
21
  },
22
22
  },
23
23
  }
24
- )
24
+ ) # type: ignore
25
25
  return config
26
26
 
27
27
 
@@ -0,0 +1,10 @@
1
+ from dynaconf import Dynaconf
2
+
3
+ from .validators import validators
4
+
5
+
6
+ def test_validators():
7
+ settings = Dynaconf(validators=validators)
8
+
9
+ assert settings.base_url is None
10
+ assert settings.connection_string is None
@@ -4,7 +4,6 @@ import copy
4
4
  import importlib
5
5
  from importlib import import_module
6
6
  from urllib.parse import parse_qs, urlparse
7
-
8
7
  from almabtrieb import Almabtrieb
9
8
 
10
9
  from roboherd.cow import RoboCow
@@ -0,0 +1,6 @@
1
+ from dynaconf import Validator
2
+
3
+ validators = [
4
+ Validator("base_url", default=None),
5
+ Validator("connection_string", default=None),
6
+ ]
@@ -919,6 +919,15 @@ wheels = [
919
919
  { url = "https://files.pythonhosted.org/packages/99/b7/b9e70fde2c0f0c9af4cc5277782a89b66d35948ea3369ec9f598358c3ac5/multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", size = 10051 },
920
920
  ]
921
921
 
922
+ [[package]]
923
+ name = "nodeenv"
924
+ version = "1.9.1"
925
+ source = { registry = "https://pypi.org/simple" }
926
+ sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 }
927
+ wheels = [
928
+ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 },
929
+ ]
930
+
922
931
  [[package]]
923
932
  name = "packaging"
924
933
  version = "24.2"
@@ -1195,6 +1204,19 @@ wheels = [
1195
1204
  { url = "https://files.pythonhosted.org/packages/eb/f5/b9e2a42aa8f9e34d52d66de87941ecd236570c7ed2e87775ed23bbe4e224/pymdown_extensions-10.14.3-py3-none-any.whl", hash = "sha256:05e0bee73d64b9c71a4ae17c72abc2f700e8bc8403755a00580b49a4e9f189e9", size = 264467 },
1196
1205
  ]
1197
1206
 
1207
+ [[package]]
1208
+ name = "pyright"
1209
+ version = "1.1.394"
1210
+ source = { registry = "https://pypi.org/simple" }
1211
+ dependencies = [
1212
+ { name = "nodeenv" },
1213
+ { name = "typing-extensions" },
1214
+ ]
1215
+ sdist = { url = "https://files.pythonhosted.org/packages/b1/e4/79f4d8a342eed6790fdebdb500e95062f319ee3d7d75ae27304ff995ae8c/pyright-1.1.394.tar.gz", hash = "sha256:56f2a3ab88c5214a451eb71d8f2792b7700434f841ea219119ade7f42ca93608", size = 3809348 }
1216
+ wheels = [
1217
+ { url = "https://files.pythonhosted.org/packages/d6/4c/50c74e3d589517a9712a61a26143b587dba6285434a17aebf2ce6b82d2c3/pyright-1.1.394-py3-none-any.whl", hash = "sha256:5f74cce0a795a295fb768759bbeeec62561215dea657edcaab48a932b031ddbb", size = 5679540 },
1218
+ ]
1219
+
1198
1220
  [[package]]
1199
1221
  name = "pytest"
1200
1222
  version = "8.3.4"
@@ -1363,9 +1385,10 @@ wheels = [
1363
1385
 
1364
1386
  [[package]]
1365
1387
  name = "roboherd"
1366
- version = "0.1.2"
1388
+ version = "0.1.4"
1367
1389
  source = { editable = "." }
1368
1390
  dependencies = [
1391
+ { name = "aiohttp" },
1369
1392
  { name = "almabtrieb", extra = ["mqtt"] },
1370
1393
  { name = "apscheduler" },
1371
1394
  { name = "click" },
@@ -1387,6 +1410,7 @@ dev = [
1387
1410
  { name = "mkdocs-click" },
1388
1411
  { name = "mkdocs-material" },
1389
1412
  { name = "mkdocstrings-python" },
1413
+ { name = "pyright" },
1390
1414
  { name = "pytest" },
1391
1415
  { name = "pytest-asyncio" },
1392
1416
  { name = "pytest-watcher" },
@@ -1395,6 +1419,7 @@ dev = [
1395
1419
 
1396
1420
  [package.metadata]
1397
1421
  requires-dist = [
1422
+ { name = "aiohttp", specifier = ">=3.11.12" },
1398
1423
  { name = "almabtrieb", extras = ["mqtt"], specifier = ">=0.1.0a1" },
1399
1424
  { name = "apscheduler", specifier = ">=3.11.0" },
1400
1425
  { name = "bovine", marker = "extra == 'bovine'", specifier = ">=0.5.15" },
@@ -1412,6 +1437,7 @@ dev = [
1412
1437
  { name = "mkdocs-click", specifier = ">=0.8.1" },
1413
1438
  { name = "mkdocs-material", specifier = ">=9.5.50" },
1414
1439
  { name = "mkdocstrings-python", specifier = ">=1.13.0" },
1440
+ { name = "pyright", specifier = ">=1.1.394" },
1415
1441
  { name = "pytest", specifier = ">=8.3.4" },
1416
1442
  { name = "pytest-asyncio", specifier = ">=0.25.2" },
1417
1443
  { name = "pytest-watcher", specifier = ">=0.4.3" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes