mtcli 3.7.0.dev0__py3-none-any.whl → 3.7.0.dev1__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.
mtcli/plugin_loader.py CHANGED
@@ -1,81 +1,147 @@
1
1
  """
2
- Carregador de plugins do mtcli.
2
+ Sistema de carregamento de plugins do mtcli.
3
3
 
4
- Responsável por descobrir e registrar plugins internos e externos
5
- utilizando entry points.
4
+ Este módulo é responsável por descobrir e registrar plugins externos
5
+ instalados via entry points do Python.
6
6
 
7
- Plugins podem expor:
7
+ Plugins devem declarar entry points no grupo:
8
8
 
9
- 1. função register(cli)
10
- 2. objeto click.Command
9
+ mtcli.plugins
10
+
11
+ Um plugin pode expor:
12
+
13
+ 1. Uma função ``register(cli)``
14
+ 2. Um objeto ``click.Command``
15
+
16
+ Exemplo de entry point no pyproject.toml:
17
+
18
+ [project.entry-points."mtcli.plugins"]
19
+ renko = "mtcli_renko.plugin:register"
20
+
21
+ Ou diretamente um comando:
22
+
23
+ renko = "mtcli_renko.cli:renko"
24
+
25
+ Este loader garante:
26
+
27
+ - compatibilidade com Python 3.8+
28
+ - proteção contra plugins duplicados
29
+ - logging estruturado
30
+ - isolamento de falhas de plugins
11
31
  """
12
32
 
33
+ from __future__ import annotations
34
+
13
35
  import logging
36
+ from typing import Iterable, List
37
+
14
38
  import click
15
39
 
16
40
  try:
17
- from importlib.metadata import entry_points
41
+ from importlib.metadata import EntryPoint, entry_points
18
42
  except ImportError: # pragma: no cover
19
- from importlib_metadata import entry_points
43
+ from importlib_metadata import EntryPoint, entry_points
20
44
 
21
45
 
22
46
  logger = logging.getLogger(__name__)
23
47
 
48
+ PLUGIN_GROUP = "mtcli.plugins"
49
+
24
50
 
25
- def load_plugins(cli):
51
+ def discover_plugins() -> Iterable[EntryPoint]:
26
52
  """
27
- Descobre e carrega plugins registrados via entry points.
53
+ Descobre plugins registrados via entry points.
28
54
 
29
- Args:
30
- cli (click.Group): CLI principal.
55
+ Returns
56
+ -------
57
+ Iterable[EntryPoint]
58
+ Lista de entry points encontrados no grupo ``mtcli.plugins``.
31
59
  """
32
60
 
33
61
  try:
34
62
  eps = entry_points()
35
63
 
36
- plugins = (
37
- eps.select(group="mtcli.plugins")
38
- if hasattr(eps, "select")
39
- else eps.get("mtcli.plugins", [])
40
- )
64
+ if hasattr(eps, "select"):
65
+ return eps.select(group=PLUGIN_GROUP)
41
66
 
42
- except Exception as exc:
67
+ return eps.get(PLUGIN_GROUP, [])
68
+
69
+ except Exception as exc: # pragma: no cover
43
70
  logger.error("Erro ao descobrir plugins: %s", exc)
44
- return
71
+ return []
45
72
 
46
- loaded = set()
47
73
 
48
- for ep in plugins:
74
+ def register_plugin(cli: click.Group, ep: EntryPoint) -> None:
75
+ """
76
+ Carrega e registra um plugin individual.
49
77
 
50
- if ep.name in loaded:
51
- logger.warning("Plugin duplicado ignorado: %s", ep.name)
52
- continue
78
+ O plugin pode ser:
53
79
 
54
- try:
80
+ - função register(cli)
81
+ - objeto click.Command
55
82
 
56
- plugin = ep.load()
83
+ Parameters
84
+ ----------
85
+ cli : click.Group
86
+ CLI principal do mtcli.
87
+ ep : EntryPoint
88
+ Entry point do plugin.
89
+ """
57
90
 
58
- # função register(cli)
59
- if callable(plugin) and not isinstance(plugin, click.Command):
60
- plugin(cli)
91
+ plugin = ep.load()
61
92
 
62
- # comando click direto
63
- elif isinstance(plugin, click.Command):
64
- cli.add_command(plugin)
93
+ if callable(plugin) and not isinstance(plugin, click.Command):
94
+ plugin(cli)
95
+ logger.debug("Plugin '%s' registrado via register()", ep.name)
65
96
 
66
- else:
67
- raise TypeError(
68
- "Plugin não é comando Click nem função register(cli)"
69
- )
97
+ elif isinstance(plugin, click.Command):
98
+ cli.add_command(plugin)
99
+ logger.debug("Plugin '%s' registrado como comando", ep.name)
70
100
 
71
- loaded.add(ep.name)
101
+ else:
102
+ raise TypeError(
103
+ f"Plugin '{ep.name}' inválido: "
104
+ "não é click.Command nem função register(cli)"
105
+ )
72
106
 
73
- logger.debug("Plugin carregado: %s", ep.name)
74
107
 
75
- except Exception as exc:
108
+ def load_plugins(cli: click.Group) -> List[str]:
109
+ """
110
+ Descobre e carrega todos os plugins instalados.
76
111
 
112
+ Parameters
113
+ ----------
114
+ cli : click.Group
115
+ CLI principal do mtcli.
116
+
117
+ Returns
118
+ -------
119
+ list[str]
120
+ Lista com os nomes dos plugins carregados com sucesso.
121
+ """
122
+
123
+ loaded: List[str] = []
124
+ seen = set()
125
+
126
+ for ep in discover_plugins():
127
+
128
+ if ep.name in seen:
129
+ logger.warning("Plugin duplicado ignorado: %s", ep.name)
130
+ continue
131
+
132
+ try:
133
+ register_plugin(cli, ep)
134
+
135
+ seen.add(ep.name)
136
+ loaded.append(ep.name)
137
+
138
+ except Exception as exc:
77
139
  logger.error(
78
140
  "Falha ao carregar plugin '%s': %s",
79
141
  ep.name,
80
142
  exc,
81
143
  )
144
+
145
+ logger.info("Plugins carregados: %d", len(loaded))
146
+
147
+ return loaded
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mtcli
3
- Version: 3.7.0.dev0
3
+ Version: 3.7.0.dev1
4
4
  Summary: Aplicativo CLI para exibir gráficos do MetaTrader 5 screen reader friendly
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -28,7 +28,7 @@ mtcli/models/signals_model.py,sha256=XhyCErgyuHF0o85vnjIVJlgsCF7XFsWQltSbLZ3Aals
28
28
  mtcli/models/unconsecutive_bar_model.py,sha256=AiClcoyYnk3K6D1YkC0E2PRUhbGwsjdSnh1vIHZsIe8,2079
29
29
  mtcli/mt5_context.py,sha256=oLevCe4nYeZ6VtGoaO-j7U4xzACE09wGmX38P0oRg5A,2147
30
30
  mtcli/plugin.py,sha256=Wabbr22BVi32D-XNM7XS45bRdgKob7EKO6TuWtq9_ZM,414
31
- mtcli/plugin_loader.py,sha256=d1hemrZeGRZcUnXzF6_lv2JFmpE6pg51AJhpwj69UhY,1850
31
+ mtcli/plugin_loader.py,sha256=N4gNjNh6dp05iwpIvlJAybsQPdc7WPmLDpzvhn8eNQQ,3378
32
32
  mtcli/plugin_manager.py,sha256=JTK6rmhjLa9MdyUiGgufAAnaaBhUI1Bm4g4t6p18WdU,555
33
33
  mtcli/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  mtcli/plugins/exemplo.py-dist,sha256=q8H_sBpI-IZej9RvkCl2o_br9q2z_WXXn_qA1MRRbBo,538
@@ -65,8 +65,8 @@ mtcli/views/ranges_view.py,sha256=5gA2bAJuco-Xj964nsjs87tZ-079O7xKvaeif1UJ-PQ,12
65
65
  mtcli/views/rates_view.py,sha256=Jkdbg-Q_OVJuID-Q9HFUVICCF1jE82d-qfZWNm1JJ_4,1214
66
66
  mtcli/views/vars_view.py,sha256=Vpcej41qnZSvvSyvXYyW8W7CEFdqTxBd8NczV84XPJY,1635
67
67
  mtcli/views/volumes_view.py,sha256=d_6YxM0vA1revwoPwnwUjV19KcqnddVgdpBHTt5Vqms,1575
68
- mtcli-3.7.0.dev0.dist-info/entry_points.txt,sha256=sFL07BcOh-MKaij-M26bNlRbmlw9Xx0CTYTPtHegfI8,99
69
- mtcli-3.7.0.dev0.dist-info/licenses/LICENSE,sha256=Z-2ANeRgM9IjXnHeg9mA2gillM6eTQj8sIExAGNe2-8,1092
70
- mtcli-3.7.0.dev0.dist-info/METADATA,sha256=6Q_T3LztXHUaDL3dtrIpelmZHZSyPDPXz08rakwY9uI,3650
71
- mtcli-3.7.0.dev0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
72
- mtcli-3.7.0.dev0.dist-info/RECORD,,
68
+ mtcli-3.7.0.dev1.dist-info/entry_points.txt,sha256=sFL07BcOh-MKaij-M26bNlRbmlw9Xx0CTYTPtHegfI8,99
69
+ mtcli-3.7.0.dev1.dist-info/licenses/LICENSE,sha256=Z-2ANeRgM9IjXnHeg9mA2gillM6eTQj8sIExAGNe2-8,1092
70
+ mtcli-3.7.0.dev1.dist-info/METADATA,sha256=nRc1fXuF9bl9I5Gyi2tjnWIlS1erd3Twi_m_jp8H_MY,3650
71
+ mtcli-3.7.0.dev1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
72
+ mtcli-3.7.0.dev1.dist-info/RECORD,,