dshellInterpreter 1.2.1.1__tar.gz → 1.2.1.2__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.
Files changed (54) hide show
  1. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/dshell_ui.py +82 -69
  2. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellInterpreteur/dshell_interpreter.py +12 -5
  3. dshellinterpreter-1.2.1.2/Dshell/DshellInterpreteur/dshell_scope.py +159 -0
  4. {dshellinterpreter-1.2.1.1/dshellInterpreter.egg-info → dshellinterpreter-1.2.1.2}/PKG-INFO +1 -1
  5. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2/dshellInterpreter.egg-info}/PKG-INFO +1 -1
  6. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/setup.py +1 -1
  7. dshellinterpreter-1.2.1.1/Dshell/DshellInterpreteur/dshell_scope.py +0 -81
  8. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/__init__.py +0 -0
  9. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/dshell_channel.py +0 -0
  10. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/dshell_embed.py +0 -0
  11. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/dshell_file.py +0 -0
  12. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/dshell_interaction.py +0 -0
  13. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/dshell_member.py +0 -0
  14. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/dshell_message.py +0 -0
  15. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/dshell_pastbin.py +0 -0
  16. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/dshell_role.py +0 -0
  17. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/utils/__init__.py +0 -0
  18. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/utils/utils_embed.py +0 -0
  19. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/utils/utils_file.py +0 -0
  20. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/utils/utils_global.py +0 -0
  21. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/utils/utils_list.py +0 -0
  22. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/utils/utils_member.py +0 -0
  23. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/utils/utils_message.py +0 -0
  24. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/utils/utils_numbers.py +0 -0
  25. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/utils/utils_permissions.py +0 -0
  26. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/utils/utils_string.py +0 -0
  27. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/utils/utils_thread.py +0 -0
  28. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DISCORD_COMMANDS/utils/utils_type_validation.py +0 -0
  29. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellInterpreteur/__init__.py +0 -0
  30. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellInterpreteur/cached_messages.py +0 -0
  31. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellInterpreteur/dshell_arguments.py +0 -0
  32. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellInterpreteur/dshell_global_variables.py +0 -0
  33. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellInterpreteur/errors.py +0 -0
  34. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellInterpreteur/utils_interpreter.py +0 -0
  35. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellParser/__init__.py +0 -0
  36. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellParser/ast_nodes.py +0 -0
  37. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellParser/dshell_parser.py +0 -0
  38. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellParser/errors.py +0 -0
  39. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellPreProcess/__init__.py +0 -0
  40. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellPreProcess/dshell_preprocess.py +0 -0
  41. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellTokenizer/__init__.py +0 -0
  42. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellTokenizer/dshell_keywords.py +0 -0
  43. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellTokenizer/dshell_token_type.py +0 -0
  44. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/DshellTokenizer/dshell_tokenizer.py +0 -0
  45. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/__init__.py +0 -0
  46. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/full_import.py +0 -0
  47. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/Dshell/regex_test.py +0 -0
  48. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/LICENSE +0 -0
  49. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/README.md +0 -0
  50. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/dshellInterpreter.egg-info/SOURCES.txt +0 -0
  51. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/dshellInterpreter.egg-info/dependency_links.txt +0 -0
  52. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/dshellInterpreter.egg-info/requires.txt +0 -0
  53. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/dshellInterpreter.egg-info/top_level.txt +0 -0
  54. {dshellinterpreter-1.2.1.1 → dshellinterpreter-1.2.1.2}/setup.cfg +0 -0
@@ -13,19 +13,24 @@ from ..DshellParser.ast_nodes import UiSelectNode, UiButtonNode, OptionUiSelectN
13
13
 
14
14
  from ..DshellInterpreteur.utils_interpreter import regroupe_commandes
15
15
 
16
- from ..DshellInterpreteur.dshell_scope import new_scope
16
+ from ..DshellInterpreteur.dshell_scope import new_scope, get_scope, update_nbr_usage_scope, get_usage_scope
17
17
 
18
- from Dshell.full_import import Any, TYPE_CHECKING, Union
18
+ from Dshell.full_import import Any, TYPE_CHECKING, Union, Optional
19
19
 
20
20
  from .utils.utils_type_validation import (_validate_optional_code_node,
21
+ _validate_required_int,
21
22
  _validate_optional_int,
22
- _validate_optional_string)
23
+ _validate_optional_string,
24
+ _validate_required_string,
25
+ _validate_required_bool,
26
+ _validate_missing_or_type)
23
27
 
24
28
  from .utils.utils_global import utils_refactor_emoji
25
29
 
26
30
  if TYPE_CHECKING:
27
31
  from ..DshellInterpreteur.dshell_interpreter import DshellInterpreteur
28
32
 
33
+ scope_id = "scope_id"
29
34
 
30
35
  ButtonStyleValues: set = {i.name for i in ButtonStyle}
31
36
  SelectSyleValues: dict = {'string': ComponentType.string_select,
@@ -42,29 +47,26 @@ async def build_ui_button_parameters(ui_button_node: UiButtonNode, interpreter:
42
47
  :param interpreter:
43
48
  :return:
44
49
  """
50
+ _CMD = "button"
51
+
45
52
  regrouped_parameters = await regroupe_commandes(ui_button_node.body, interpreter, normalise=True)
46
53
  args_button: dict[str, list[Any]] = regrouped_parameters.get_dict_parameters()
47
54
 
48
55
  code = args_button.pop('code', None)
49
- style = args_button.pop('style', 'primary').lower()
56
+ style = args_button.pop('style', StrNode('primary')).lower()
50
57
  custom_id = args_button.pop('custom_id', StrNode('ui_button_'+str(random())))
51
58
  row = args_button.pop('row', IntNode(0))
52
59
  emoji = utils_refactor_emoji(args_button.pop('emoji', None))
53
60
 
54
- _validate_optional_code_node(code, "Button code", "build_ui_button")
55
-
56
- if not isinstance(custom_id, StrNode):
57
- raise TypeError(f"Button custom_id must be a string, not {type(custom_id)} !")
61
+ _validate_optional_code_node(code, "code", _CMD)
62
+ _validate_required_string(style, "style", _CMD)
63
+ _validate_required_string(custom_id, "custom_id", _CMD)
64
+ _validate_required_int(row, "row", _CMD)
65
+ _validate_optional_string(emoji, "emoji", _CMD)
58
66
 
59
67
  if style not in ButtonStyleValues:
60
68
  raise ValueError(f"Button style must be one of {', '.join(ButtonStyleValues)}, not '{style}' !")
61
69
 
62
- if not isinstance(row, IntNode):
63
- raise TypeError(f"Buttton row must be a int, not {type(row)} !")
64
-
65
- if not isinstance(emoji, StrNode):
66
- raise TypeError(f"Button emoji must be a str, not {type(emoji)} !")
67
-
68
70
  args_button['custom_id'] = custom_id
69
71
  args_button['row'] = row
70
72
  args_button['style'] = ButtonStyle[style]
@@ -80,40 +82,32 @@ async def build_ui_select_parameters(ui_select_node: UiSelectNode, interpreter:
80
82
  :param interpreter:
81
83
  :return:
82
84
  """
85
+ _CMD = "select"
86
+
83
87
  regrouped_parameters = await regroupe_commandes(ui_select_node.body, interpreter, normalise=True)
84
88
  args_select: dict[str, list[Any]] = regrouped_parameters.get_dict_parameters()
85
89
 
86
90
  code = args_select.pop('code', None)
87
91
  custom_id = args_select.pop('custom_id', StrNode('ui_select_'+str(random())))
88
- select_type = args_select.pop('type', 'string').lower()
89
-
90
- disabled = args_select.get('disabled', False)
91
- max_values = args_select.get('max', 1)
92
- min_values = args_select.get('min', 1)
93
- placeholder = args_select.get('placeholder', "")
94
- row = args_select.pop('row', 0)
95
-
96
- _validate_optional_code_node(code, "Select code", "build_ui_select")
97
-
98
- if not isinstance(custom_id, StrNode):
99
- raise TypeError(f"Select custom_id must be a string, not {type(custom_id)} !")
100
-
101
- if select_type is None or not isinstance(select_type, StrNode) or select_type not in SelectSyleValues:
102
- raise TypeError(f"Select type must be a string, not {type(select_type)} !")
103
-
104
- if not isinstance(disabled, BoolNode):
105
- raise TypeError(f"Select disabled must be a bool, not {type(disabled)} !")
106
-
107
- if not isinstance(max_values, IntNode):
108
- raise TypeError(f"Select max_values must be an int, not {type(max_values)} !")
109
-
110
- if not isinstance(min_values, IntNode):
111
- raise TypeError(f"Select min_values must be an int, not {type(min_values)} !")
112
-
113
- if not isinstance(placeholder, StrNode):
114
- raise TypeError(f"Select placeholder must be a string, not {type(placeholder)} !")
115
-
116
- _validate_optional_int(row, "Select row", "build_ui_select")
92
+ select_type = args_select.pop('type', StrNode('string')).lower()
93
+
94
+ disabled = args_select.get('disabled', BoolNode(0))
95
+ max_values = args_select.get('max', IntNode(1))
96
+ min_values = args_select.get('min', IntNode(1))
97
+ placeholder = args_select.get('placeholder', StrNode(""))
98
+ row = args_select.pop('row', IntNode(0))
99
+
100
+ _validate_optional_code_node(code, "Select code", _CMD)
101
+ _validate_required_string(custom_id, "custom_id", _CMD)
102
+ _validate_required_string(select_type, "type", _CMD)
103
+ _validate_required_bool(disabled, "disabled", _CMD)
104
+ _validate_required_int(max_values, "max", _CMD)
105
+ _validate_required_int(max_values, "min", _CMD)
106
+ _validate_required_string(placeholder, "placeholder", _CMD)
107
+ _validate_optional_int(row, "row", _CMD)
108
+
109
+ if select_type not in SelectSyleValues:
110
+ raise TypeError(f"Select style must be one of {', '.join(SelectSyleValues.keys())}, not '{select_type}' !")
117
111
 
118
112
  args_select["disabled"] = disabled
119
113
  args_select["max_values"] = max_values
@@ -134,6 +128,8 @@ async def build_ui_select_options(option_nodes: list[OptionUiSelectNode], interp
134
128
  :param interpreter:
135
129
  :return:
136
130
  """
131
+ _CMD = "option"
132
+
137
133
  option_results: list[dict[str, Any]] = []
138
134
 
139
135
  for option_node in option_nodes:
@@ -146,28 +142,21 @@ async def build_ui_select_options(option_nodes: list[OptionUiSelectNode], interp
146
142
  emoji = utils_refactor_emoji(args_option.pop('emoji', None))
147
143
  default = args_option.pop('default', False)
148
144
 
149
- if label is None or not isinstance(label, str):
150
- raise TypeError(f"Option label must be a string, not {type(label)} !")
145
+ _validate_required_string(label, "label", _CMD)
146
+ _validate_missing_or_type(value, "value", StrNode, _CMD)
147
+ _validate_optional_string(description, "description", _CMD)
148
+ _validate_optional_string(emoji, "emoji", _CMD)
149
+ _validate_required_bool(default, "default", _CMD)
151
150
 
152
151
  if len(label) > 100:
153
152
  raise ValueError("Option label must be less than 100 characters !")
154
153
 
155
- if value and not isinstance(value, str):
156
- raise TypeError(f"Option value must be a string, not {type(value)} !")
157
-
158
154
  if value and len(value) > 100:
159
155
  raise ValueError("Option value must be less than 100 characters !")
160
156
 
161
- _validate_optional_string(description, "Option description", "build_ui_option")
162
-
163
157
  if description is not None and len(description) > 100:
164
158
  raise ValueError("Option description must be less than 100 characters !")
165
159
 
166
- _validate_optional_string(emoji, "Option emoji", "build_ui_option")
167
-
168
- if not isinstance(default, bool):
169
- raise TypeError(f"Option default must be a bool, not {type(default)} !")
170
-
171
160
  option_dict = {
172
161
  'label': label,
173
162
  'value': value,
@@ -194,7 +183,7 @@ async def build_ui(ui_node: Union[UiButtonNode, UiSelectNode], interpreter: "Dsh
194
183
  async for _, args_button, code in build_ui_button_parameters(ui_node, interpreter):
195
184
  b = ui.Button(**args_button)
196
185
  view.add_items(b)
197
- view.set_callable(b.custom_id, _callable=ui_button_callback, data={'code': code, 'interpreter': interpreter})
186
+ view.set_callable(b.custom_id, _callable=ui_button_callback, data={'code': code, scope_id: interpreter.scope_id})
198
187
 
199
188
  elif isinstance(ui_node, UiSelectNode):
200
189
  s = SelectMenu()
@@ -209,23 +198,23 @@ async def build_ui(ui_node: Union[UiButtonNode, UiSelectNode], interpreter: "Dsh
209
198
  for option in options:
210
199
  menu.add_option(**option)
211
200
 
212
- s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, 'interpreter': interpreter})
201
+ s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, scope_id: interpreter.scope_id})
213
202
 
214
203
  elif select_type == ComponentType.role_select:
215
204
  s.add_role_select_menu(**args_select)
216
- s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, 'interpreter': interpreter})
205
+ s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, scope_id: interpreter.scope_id})
217
206
 
218
207
  elif select_type == ComponentType.user_select:
219
208
  s.add_user_select_menu(**args_select)
220
- s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, 'interpreter': interpreter})
209
+ s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, scope_id: interpreter.scope_id})
221
210
 
222
211
  elif select_type == ComponentType.mentionable_select:
223
212
  s.add_mentionable_select_menu(**args_select)
224
- s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, 'interpreter': interpreter})
213
+ s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, scope_id: interpreter.scope_id})
225
214
 
226
215
  elif select_type == ComponentType.channel_select:
227
216
  s.add_channel_select_menu(**args_select)
228
- s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, 'interpreter': interpreter})
217
+ s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, scope_id: interpreter.scope_id})
229
218
 
230
219
  view.add_items(s)
231
220
 
@@ -258,7 +247,7 @@ async def rebuild_ui(ui_node: Union[UiButtonNode, UiSelectNode], view: EasyModif
258
247
  ui.url = args_button.get('url', ui.url)
259
248
  ui.row = args_button.get('row', ui.row)
260
249
  new_code = code if code is not None else view.get_callable_data(args_button['custom_id'])['code']
261
- view.set_callable(args_button['custom_id'], _callable=ui_button_callback, data={'code': new_code, 'interpreter': interpreter})
250
+ view.set_callable(args_button['custom_id'], _callable=ui_button_callback, data={'code': new_code, scope_id: interpreter.scope_id})
262
251
 
263
252
  elif isinstance(ui_node, UiSelectNode):
264
253
 
@@ -279,7 +268,7 @@ async def rebuild_ui(ui_node: Union[UiButtonNode, UiSelectNode], view: EasyModif
279
268
  ui.add_option(**option)
280
269
 
281
270
  new_code = code if code is not None else view.get_callable_data(args_select['custom_id'])['code']
282
- view.set_callable(args_select['custom_id'], _callable=ui_select_callback, data={'code': new_code, 'interpreter': interpreter})
271
+ view.set_callable(args_select['custom_id'], _callable=ui_select_callback, data={'code': new_code, scope_id: interpreter.scope_id})
283
272
 
284
273
  return view
285
274
 
@@ -294,7 +283,8 @@ async def ui_button_callback(button: ui.Button, interaction: Interaction, data:
294
283
  :return:
295
284
  """
296
285
  code = data.get('code', None)
297
- interpreter: "DshellInterpreteur" = data.get('interpreter', None)
286
+ scope: Optional[str] = data.get(scope_id, None)
287
+
298
288
  if code is not None:
299
289
  message = interaction
300
290
  local_env = {
@@ -382,13 +372,23 @@ async def ui_button_callback(button: ui.Button, interaction: Interaction, data:
382
372
  }
383
373
  )
384
374
 
385
- #local_env.update(data)
386
375
  from ..DshellInterpreteur.dshell_interpreter import DshellInterpreteur
387
376
 
388
- new_interpreter = DshellInterpreteur(code, ctx=interaction, debug=False, vars_env=interpreter.env)
377
+ new_interpreter = DshellInterpreteur(
378
+ code,
379
+ ctx=interaction,
380
+ debug=False,
381
+ vars_env=get_scope(scope))
382
+
383
+ if scope is not None:
384
+ update_nbr_usage_scope(scope, 1)
385
+
389
386
  with new_scope(new_interpreter, local_env):
390
387
  await new_interpreter.execute()
391
388
 
389
+ if scope is not None:
390
+ update_nbr_usage_scope(scope, -1)
391
+
392
392
  else:
393
393
  await interaction.response.defer(invisible=True)
394
394
 
@@ -403,7 +403,7 @@ async def ui_select_callback(select: ui.Select, interaction: Interaction, data:
403
403
  :return:
404
404
  """
405
405
  code = data.get('code', None)
406
- interpreter: "DshellInterpreteur" = data.get('interpreter', None)
406
+ scope: Optional[str] = data.get(scope_id, None)
407
407
 
408
408
  message = interaction
409
409
  if code is not None:
@@ -518,7 +518,20 @@ async def ui_select_callback(select: ui.Select, interaction: Interaction, data:
518
518
 
519
519
  local_env.update(data)
520
520
  from ..DshellInterpreteur.dshell_interpreter import DshellInterpreteur
521
- with new_scope(interpreter, local_env):
522
- await DshellInterpreteur(code, ctx=interaction, debug=False, vars_env=interpreter.env).execute()
521
+ new_interpreter = DshellInterpreteur(
522
+ code,
523
+ ctx=interaction,
524
+ debug=False,
525
+ vars_env=get_scope(scope))
526
+
527
+ if scope is not None:
528
+ update_nbr_usage_scope(scope, 1)
529
+
530
+ with new_scope(new_interpreter, local_env):
531
+ await new_interpreter.execute()
532
+
533
+ if scope is not None:
534
+ update_nbr_usage_scope(scope, -1)
535
+
523
536
  else:
524
537
  await interaction.response.defer(invisible=True)
@@ -1,9 +1,8 @@
1
1
  from ..DshellTokenizer.dshell_token_type import Token
2
2
  from ..DshellTokenizer.dshell_token_type import DshellTokenType as DTT
3
- from ..DshellInterpreteur.errors import DshellInterpreterStopExecution, DshellInterpreterError
4
- from Dshell.full_import import TypeVar, Union, Optional, Any, Callable, sleep, findall
3
+ from ..full_import import TypeVar, Union, Optional, Any, Callable, sleep, findall
5
4
  from ..DshellParser.ast_nodes import *
6
- from Dshell.full_import import AutoShardedBot, Interaction, Message, PrivateChannel
5
+ from ..full_import import AutoShardedBot, Interaction, Message, PrivateChannel, random
7
6
  from ..DshellParser.dshell_parser import parse, print_ast
8
7
  from ..DshellTokenizer.dshell_tokenizer import DshellTokenizer
9
8
  from .cached_messages import dshell_cached_messages
@@ -12,7 +11,7 @@ from .utils_interpreter import get_params, eval_expression, eval_expression_inli
12
11
  from ..DISCORD_COMMANDS.dshell_embed import build_embed, rebuild_embed
13
12
  from ..DISCORD_COMMANDS.dshell_ui import build_ui
14
13
  from ..DISCORD_COMMANDS.utils.utils_permissions import build_permission
15
- from .dshell_scope import Scope, new_scope
14
+ from .dshell_scope import Scope, new_scope, get_scope, create_scope, update_nbr_usage_scope, get_usage_scope
16
15
  from .dshell_global_variables import MAX_SLEEP_TIME_SECONDS, MIN_SLEEP_TIME_SECONDS
17
16
 
18
17
 
@@ -48,7 +47,14 @@ class DshellInterpreteur:
48
47
  self.ast: list[ASTNode] = code.body
49
48
 
50
49
  message = ctx.message if isinstance(ctx, Interaction) else ctx
51
- self.env: Scope = Scope()
50
+
51
+ # scope creation
52
+ self.scope_id: str = create_scope()
53
+ self.env: Optional[Scope] = get_scope(self.scope_id)
54
+
55
+ if self.env is None:
56
+ raise Exception(f"Scope {self.scope_id} not found in interpreter creation !")
57
+
52
58
  self.env.update({
53
59
  '__ret__': None, # environment variables, '__ret__' is used to store the return value of commands
54
60
  '__loop__': None, # used to store the current loop variable in loop nodes if the loop identifier is not specified
@@ -329,6 +335,7 @@ class DshellInterpreteur:
329
335
  """
330
336
  self.env.clear()
331
337
  self.ast.clear()
338
+ update_nbr_usage_scope(self.scope_id, -1)
332
339
 
333
340
 
334
341
  async def call_function(function: Callable, args: ArgsCommandNode, interpreter: DshellInterpreteur):
@@ -0,0 +1,159 @@
1
+ from contextlib import contextmanager
2
+ from typing import Any, Optional, Dict, Set, Union
3
+ from random import random
4
+
5
+ # manage all scope with one unique ID by scope to separate the interpreter and scopes
6
+ context_scope: dict[str, tuple[int, "Scope"]] = {}
7
+
8
+ def generate_scope_id() -> str:
9
+ """
10
+ generate a new scope code unused
11
+ :return:
12
+ """
13
+ while (_id := (str(random()) + str(random()))) in context_scope: pass
14
+ return _id
15
+
16
+
17
+ def get_scope(_id: str) -> Union["Scope", None]:
18
+ """
19
+ Get the current scope linked with an ID
20
+ :param _id:
21
+ :return: the scope link with the id, or None if the scope id is not found
22
+ """
23
+ x = context_scope.get(_id, None)
24
+ return x[1] if x is not None else x
25
+
26
+ def get_usage_scope(_id: str) -> Union[int, None]:
27
+ """
28
+ Get the usage number for a scope
29
+ :param _id: An integer, or None if the scope id is not found
30
+ :return:
31
+ """
32
+ x = context_scope.get(_id, None)
33
+ return x[0] if x is not None else x
34
+
35
+ class Scope:
36
+ """
37
+ Represents a variable scope with optional parent scope for nested scoping.
38
+ """
39
+ def __init__(self, parent: Optional[str] = None):
40
+ self.parent: Optional[str] = parent
41
+ self.vars: Dict[str, Any] = {}
42
+
43
+ def get(self, name: str) -> Any:
44
+ """
45
+ Get a variable value from this scope or parent scopes.
46
+ :param name: Variable name
47
+ :return: Variable value
48
+ :raises KeyError: If variable not found in any scope
49
+ """
50
+ if name in self.vars:
51
+ return self.vars[name]
52
+ if self.parent:
53
+ if self.parent in context_scope:
54
+ return context_scope[self.parent][1].get(name)
55
+ raise Exception(f"Scope {self.parent} not found [get] !")
56
+ raise KeyError(name)
57
+
58
+ def set(self, name: str, value: Any) -> None:
59
+ """
60
+ Set a variable in this scope.
61
+ :param name: Variable name
62
+ :param value: Variable value
63
+ """
64
+ self.vars[name] = value
65
+
66
+ def update(self, mapping: Dict[str, Any]) -> None:
67
+ """
68
+ Update multiple variables in this scope.
69
+ :param mapping: Dictionary of variable names and values
70
+ """
71
+ self.vars.update(mapping)
72
+
73
+ def contains(self, name: str) -> bool:
74
+ """
75
+ Check if a variable exists in this scope or parent scopes.
76
+ :param name: Variable name
77
+ :return: True if variable exists, False otherwise
78
+ """
79
+ if name in self.vars:
80
+ return True
81
+ if self.parent:
82
+ if self.parent in context_scope:
83
+ return context_scope[self.parent][1].contains(name)
84
+ Exception(f"Scope {self.parent} not found [contains] !")
85
+ return False
86
+
87
+ def keys(self) -> Set[str]:
88
+ """
89
+ Get all variable names from this scope and parent scopes.
90
+ :return: Set of variable names
91
+ """
92
+ keys = set(self.vars.keys())
93
+ if self.parent:
94
+ if self.parent in context_scope:
95
+ keys.update(context_scope[self.parent][1].keys())
96
+ else:
97
+ Exception(f"Scope {self.parent} not found [keys] !")
98
+ return keys
99
+
100
+ def clear(self) -> None:
101
+ """Clear all variables in this scope (does not affect parent)."""
102
+ self.vars.clear()
103
+
104
+
105
+ def create_scope() -> str:
106
+ """
107
+ Create a new scope and return the scope code
108
+ :return:
109
+ """
110
+ _id = generate_scope_id()
111
+ scope = Scope()
112
+ context_scope[_id] = (1, scope)
113
+ return _id
114
+
115
+ def update_nbr_usage_scope(scope_id: str, nbr_usage: int):
116
+ """
117
+ Update the usage number for a scope
118
+ :param nbr_usage:
119
+ :return:
120
+ """
121
+ if scope_id not in context_scope:
122
+ raise Exception(f"Scope {scope_id} not found [update nbr usage scope] !")
123
+
124
+ new_usage = get_usage_scope(scope_id)+nbr_usage
125
+
126
+ if new_usage <= 0:
127
+ del context_scope[scope_id]
128
+ return
129
+
130
+ new = (new_usage, context_scope[scope_id][1])
131
+
132
+ del context_scope[scope_id]
133
+ context_scope[scope_id] = new
134
+
135
+
136
+ @contextmanager
137
+ def new_scope(interpreter, initial_vars: Optional[Dict[str, Any]] = None):
138
+ """
139
+ Context manager for creating a new scope temporarily.
140
+ :param interpreter: The interpreter instance
141
+ :param initial_vars: Optional initial variables for the new scope
142
+ """
143
+ parent = interpreter.scope_id
144
+
145
+ new_scope_id: str = generate_scope_id()
146
+ context_scope[new_scope_id] = (1, Scope(parent))
147
+
148
+ interpreter.scope_id = new_scope_id
149
+ interpreter.env = get_scope(new_scope_id)
150
+
151
+ if initial_vars:
152
+ interpreter.env.update(initial_vars)
153
+ try:
154
+ yield
155
+ finally:
156
+ interpreter.clear()
157
+ interpreter.env = get_scope(parent)
158
+ interpreter.scope_id = parent
159
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dshellInterpreter
3
- Version: 1.2.1.1
3
+ Version: 1.2.1.2
4
4
  Summary: A Discord bot interpreter for creating custom commands and automations.
5
5
  Home-page: https://github.com/BOXERRMD/Dshell_Interpreter
6
6
  Author: Chronos
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dshellInterpreter
3
- Version: 1.2.1.1
3
+ Version: 1.2.1.2
4
4
  Summary: A Discord bot interpreter for creating custom commands and automations.
5
5
  Home-page: https://github.com/BOXERRMD/Dshell_Interpreter
6
6
  Author: Chronos
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setup(
7
7
  name="dshellInterpreter",
8
- version="1.2.1.1",
8
+ version="1.2.1.2",
9
9
  author="Chronos",
10
10
  author_email="vagabonwalybi@gmail.com",
11
11
  description="A Discord bot interpreter for creating custom commands and automations.",
@@ -1,81 +0,0 @@
1
- from contextlib import contextmanager
2
- from typing import Any, Optional, Dict, Set
3
-
4
- class Scope:
5
- """
6
- Represents a variable scope with optional parent scope for nested scoping.
7
- """
8
- def __init__(self, parent: Optional['Scope'] = None):
9
- self.parent: Optional['Scope'] = parent
10
- self.vars: Dict[str, Any] = {}
11
-
12
- def get(self, name: str) -> Any:
13
- """
14
- Get a variable value from this scope or parent scopes.
15
- :param name: Variable name
16
- :return: Variable value
17
- :raises KeyError: If variable not found in any scope
18
- """
19
- if name in self.vars:
20
- return self.vars[name]
21
- if self.parent:
22
- return self.parent.get(name)
23
- raise KeyError(name)
24
-
25
- def set(self, name: str, value: Any) -> None:
26
- """
27
- Set a variable in this scope.
28
- :param name: Variable name
29
- :param value: Variable value
30
- """
31
- self.vars[name] = value
32
-
33
- def update(self, mapping: Dict[str, Any]) -> None:
34
- """
35
- Update multiple variables in this scope.
36
- :param mapping: Dictionary of variable names and values
37
- """
38
- self.vars.update(mapping)
39
-
40
- def contains(self, name: str) -> bool:
41
- """
42
- Check if a variable exists in this scope or parent scopes.
43
- :param name: Variable name
44
- :return: True if variable exists, False otherwise
45
- """
46
- if name in self.vars:
47
- return True
48
- if self.parent:
49
- return self.parent.contains(name)
50
- return False
51
-
52
- def keys(self) -> Set[str]:
53
- """
54
- Get all variable names from this scope and parent scopes.
55
- :return: Set of variable names
56
- """
57
- keys = set(self.vars.keys())
58
- if self.parent:
59
- keys.update(self.parent.keys())
60
- return keys
61
-
62
- def clear(self) -> None:
63
- """Clear all variables in this scope (does not affect parent)."""
64
- self.vars.clear()
65
-
66
- @contextmanager
67
- def new_scope(interpreter, initial_vars: Optional[Dict[str, Any]] = None):
68
- """
69
- Context manager for creating a new scope temporarily.
70
- :param interpreter: The interpreter instance
71
- :param initial_vars: Optional initial variables for the new scope
72
- """
73
- parent = interpreter.env
74
- interpreter.env = Scope(parent)
75
- if initial_vars:
76
- interpreter.env.update(initial_vars)
77
- try:
78
- yield
79
- finally:
80
- interpreter.env.clear()
81
- interpreter.env = parent