Skip to content

Commit e8b34de

Browse files
committed
[Tentacles] fix install order
1 parent 280afed commit e8b34de

2 files changed

Lines changed: 113 additions & 10 deletions

File tree

packages/tentacles_manager/octobot_tentacles_manager/util/tentacle_explorer.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,19 @@ def _load_all_metadata(tentacles):
6363

6464
def _parse_all_tentacles(root: str):
6565
factory = models.TentacleFactory(root)
66-
return [
67-
factory.create_tentacle_from_type(tentacle_entry.name, tentacle_type)
68-
for tentacle_type in _get_tentacle_types(root)
69-
for tentacle_entry in os.scandir(path.join(root, tentacle_type.to_path()))
70-
if not (tentacle_entry.name == constants.PYTHON_INIT_FILE or
71-
tentacle_entry.name in constants.FOLDERS_BLACK_LIST)
72-
] + [
73-
factory.create_tentacle_from_type(tentacle_name, tentacle_type, [tentacle_name])
74-
for tentacle_name, (tentacle_type, _) in _extra_tentacle_data_by_name.items()
75-
]
66+
return sorted(
67+
[
68+
factory.create_tentacle_from_type(tentacle_entry.name, tentacle_type)
69+
for tentacle_type in _get_tentacle_types(root)
70+
for tentacle_entry in os.scandir(path.join(root, tentacle_type.to_path()))
71+
if not (tentacle_entry.name == constants.PYTHON_INIT_FILE or
72+
tentacle_entry.name in constants.FOLDERS_BLACK_LIST)
73+
] + [
74+
factory.create_tentacle_from_type(tentacle_name, tentacle_type, [tentacle_name])
75+
for tentacle_name, (tentacle_type, _) in _extra_tentacle_data_by_name.items()
76+
],
77+
key=lambda x: x.name # ensure parsed order stays consistent
78+
)
7679

7780

7881
def _get_tentacle_types(ref_tentacles_root):
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Drakkar-Software OctoBot-Tentacles-Manager
2+
# Copyright (c) Drakkar-Software, All rights reserved.
3+
#
4+
# This library is free software; you can redistribute it and/or
5+
# modify it under the terms of the GNU Lesser General Public
6+
# License as published by the Free Software Foundation; either
7+
# version 3.0 of the License, or (at your option) any later version.
8+
#
9+
# This library is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
# Lesser General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU Lesser General Public
15+
# License along with this library.
16+
import os
17+
18+
import octobot_tentacles_manager.constants as constants
19+
import octobot_tentacles_manager.util.tentacle_explorer as tentacle_explorer
20+
21+
22+
class _DummyTentacleClass:
23+
pass
24+
25+
26+
def _write_tentacle_metadata_file(directory: str) -> None:
27+
metadata_path = os.path.join(directory, constants.TENTACLE_METADATA)
28+
with open(metadata_path, "w", encoding="utf-8") as metadata_file:
29+
metadata_file.write("{}")
30+
31+
32+
def _ensure_tentacle_type_with_metadata_nested_under(root: str, type_folder_name: str) -> str:
33+
"""
34+
Make ``type_folder_name`` a discovered tentacle type: one direct child folder
35+
must contain ``metadata.json`` (see _has_tentacle_in_direct_sub_directories).
36+
"""
37+
type_dir = os.path.join(root, type_folder_name)
38+
marker = os.path.join(type_dir, "_type_marker_tentacle")
39+
os.makedirs(marker, exist_ok=True)
40+
_write_tentacle_metadata_file(marker)
41+
return type_dir
42+
43+
44+
def _add_tentacle_folders(tentacle_type_dir: str, *tentacle_folder_names: str) -> None:
45+
for entry_name in tentacle_folder_names:
46+
os.makedirs(os.path.join(tentacle_type_dir, entry_name), exist_ok=True)
47+
48+
49+
class TestParseAllTentacles:
50+
def test_tentacle_names_are_sorted_regardless_of_filesystem_entry_order(
51+
self, tmp_path, monkeypatch
52+
):
53+
# Avoid interference from any tentacles registered elsewhere in the test session.
54+
monkeypatch.setattr(
55+
tentacle_explorer, "_extra_tentacle_data_by_name", {}
56+
)
57+
services_dir = _ensure_tentacle_type_with_metadata_nested_under(
58+
str(tmp_path), "Services"
59+
)
60+
# Intentional non-alphabetic creation order; result must be sorted by name.
61+
_add_tentacle_folders(services_dir, "zebra", "mike", "charlie")
62+
names = [tentacle.name for tentacle in tentacle_explorer._parse_all_tentacles(str(tmp_path))]
63+
expected_names = sorted(names)
64+
assert names == expected_names
65+
assert names == [
66+
"_type_marker_tentacle",
67+
"charlie",
68+
"mike",
69+
"zebra",
70+
]
71+
72+
def test_merges_extra_registered_tentacles_and_sorts_by_name(
73+
self, tmp_path, monkeypatch
74+
):
75+
extra = {}
76+
monkeypatch.setattr(
77+
tentacle_explorer, "_extra_tentacle_data_by_name", extra
78+
)
79+
services_dir = _ensure_tentacle_type_with_metadata_nested_under(
80+
str(tmp_path), "Services"
81+
)
82+
_add_tentacle_folders(services_dir, "mountain", "ant")
83+
# Register names that interleave with filesystem names when unsorted.
84+
tentacle_explorer.register_extra_tentacle_data("zebra", "Other", _DummyTentacleClass)
85+
tentacle_explorer.register_extra_tentacle_data("alpha", "Other", _DummyTentacleClass)
86+
try:
87+
names = [
88+
t.name
89+
for t in tentacle_explorer._parse_all_tentacles(str(tmp_path))
90+
]
91+
assert names == sorted(names)
92+
assert names == [
93+
"_type_marker_tentacle",
94+
"alpha",
95+
"ant",
96+
"mountain",
97+
"zebra",
98+
]
99+
finally:
100+
extra.clear()

0 commit comments

Comments
 (0)