argenta 0.4.10__py3-none-any.whl → 0.5.0a0__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.
- argenta/app/autocompleter/__init__.py +4 -0
- argenta/app/autocompleter/entity.py +48 -0
- argenta/app/defaults.py +1 -0
- argenta/app/exceptions.py +0 -10
- argenta/app/models.py +30 -20
- argenta/command/models.py +7 -2
- argenta/router/command_handler/entity.py +6 -6
- argenta/router/entity.py +44 -21
- {argenta-0.4.10.dist-info → argenta-0.5.0a0.dist-info}/METADATA +1 -1
- {argenta-0.4.10.dist-info → argenta-0.5.0a0.dist-info}/RECORD +12 -10
- {argenta-0.4.10.dist-info → argenta-0.5.0a0.dist-info}/LICENSE +0 -0
- {argenta-0.4.10.dist-info → argenta-0.5.0a0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
import readline
|
4
|
+
|
5
|
+
|
6
|
+
class AutoCompleter:
|
7
|
+
def __init__(self, history_filename: str = False, autocomplete_button: str = 'tab'):
|
8
|
+
self.history_filename = history_filename
|
9
|
+
self.autocomplete_button = autocomplete_button
|
10
|
+
self.matches = []
|
11
|
+
|
12
|
+
def complete(self, text, state):
|
13
|
+
matches = sorted(cmd for cmd in self.get_history_items() if cmd.startswith(text))
|
14
|
+
if len(matches) > 1:
|
15
|
+
common_prefix = matches[0]
|
16
|
+
for match in matches[1:]:
|
17
|
+
i = 0
|
18
|
+
while i < len(common_prefix) and i < len(match) and common_prefix[i] == match[i]:
|
19
|
+
i += 1
|
20
|
+
common_prefix = common_prefix[:i]
|
21
|
+
if state == 0:
|
22
|
+
readline.insert_text(common_prefix[len(text):])
|
23
|
+
readline.redisplay()
|
24
|
+
return None
|
25
|
+
elif len(matches) == 1:
|
26
|
+
return matches[0] if state == 0 else None
|
27
|
+
else:
|
28
|
+
return None
|
29
|
+
|
30
|
+
def initial_setup(self, all_commands: list[str]):
|
31
|
+
if self.history_filename:
|
32
|
+
if os.path.exists(self.history_filename):
|
33
|
+
readline.read_history_file(self.history_filename)
|
34
|
+
else:
|
35
|
+
for line in all_commands:
|
36
|
+
readline.add_history(line)
|
37
|
+
|
38
|
+
readline.set_completer(self.complete)
|
39
|
+
readline.set_completer_delims(readline.get_completer_delims().replace(' ', ''))
|
40
|
+
readline.parse_and_bind(f'{self.autocomplete_button}: complete')
|
41
|
+
|
42
|
+
def exit_setup(self):
|
43
|
+
if self.history_filename:
|
44
|
+
readline.write_history_file(self.history_filename)
|
45
|
+
|
46
|
+
@staticmethod
|
47
|
+
def get_history_items():
|
48
|
+
return [readline.get_history_item(i) for i in range(1, readline.get_current_history_length() + 1)]
|
argenta/app/defaults.py
CHANGED
@@ -5,4 +5,5 @@ from dataclasses import dataclass
|
|
5
5
|
class PredeterminedMessages:
|
6
6
|
USAGE = '[b dim]Usage[/b dim]: [i]<command> <[green]flags[/green]>[/i]'
|
7
7
|
HELP = '[b dim]Help[/b dim]: [i]<command>[/i] [b red]--help[/b red]'
|
8
|
+
AUTOCOMPLETE = '[b dim]Autocomplete[/b dim]: [i]<part>[/i] [bold]<tab>'
|
8
9
|
|
argenta/app/exceptions.py
CHANGED
@@ -3,16 +3,6 @@ class InvalidRouterInstanceException(Exception):
|
|
3
3
|
return "Invalid Router Instance"
|
4
4
|
|
5
5
|
|
6
|
-
class InvalidDescriptionMessagePatternException(Exception):
|
7
|
-
def __init__(self, pattern: str):
|
8
|
-
self.pattern = pattern
|
9
|
-
def __str__(self):
|
10
|
-
return ("Invalid Description Message Pattern\n"
|
11
|
-
"Correct pattern example: [{command}] *=*=* {description}\n"
|
12
|
-
"The pattern must contain two variables: `command` and `description` - description of the command\n"
|
13
|
-
f"Your pattern: {self.pattern}")
|
14
|
-
|
15
|
-
|
16
6
|
class NoRegisteredRoutersException(Exception):
|
17
7
|
def __str__(self):
|
18
8
|
return "No Registered Router Found"
|
argenta/app/models.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from typing import Callable
|
2
2
|
from rich.console import Console
|
3
|
+
from rich.markup import escape
|
3
4
|
from art import text2art
|
4
5
|
from contextlib import redirect_stdout
|
5
6
|
import io
|
@@ -8,13 +9,13 @@ import re
|
|
8
9
|
from argenta.command.models import Command, InputCommand
|
9
10
|
from argenta.router import Router
|
10
11
|
from argenta.router.defaults import system_router
|
12
|
+
from argenta.app.autocompleter import AutoCompleter
|
11
13
|
from argenta.app.dividing_line.models import StaticDividingLine, DynamicDividingLine
|
12
14
|
from argenta.command.exceptions import (UnprocessedInputFlagException,
|
13
15
|
RepeatedInputFlagsException,
|
14
16
|
EmptyInputCommandException,
|
15
17
|
BaseInputCommandException)
|
16
18
|
from argenta.app.exceptions import (InvalidRouterInstanceException,
|
17
|
-
InvalidDescriptionMessagePatternException,
|
18
19
|
NoRegisteredRoutersException,
|
19
20
|
NoRegisteredHandlersException)
|
20
21
|
from argenta.app.registered_routers.entity import RegisteredRouters
|
@@ -26,28 +27,29 @@ class AppInit:
|
|
26
27
|
prompt: str = '[italic dim bold]What do you want to do?\n',
|
27
28
|
initial_message: str = '\nArgenta\n',
|
28
29
|
farewell_message: str = '\nSee you\n',
|
29
|
-
exit_command:
|
30
|
-
|
31
|
-
system_points_title: str = 'System points:',
|
30
|
+
exit_command: Command = Command('Q', 'Exit command'),
|
31
|
+
system_points_title: str | None = 'System points:',
|
32
32
|
ignore_command_register: bool = True,
|
33
33
|
dividing_line: StaticDividingLine | DynamicDividingLine = StaticDividingLine(),
|
34
34
|
repeat_command_groups: bool = True,
|
35
35
|
full_override_system_messages: bool = False,
|
36
|
+
autocompleter: AutoCompleter = AutoCompleter(),
|
36
37
|
print_func: Callable[[str], None] = Console().print) -> None:
|
37
38
|
self._prompt = prompt
|
38
39
|
self._print_func = print_func
|
39
40
|
self._exit_command = exit_command
|
40
|
-
self._exit_command_description = exit_command_description
|
41
41
|
self._system_points_title = system_points_title
|
42
42
|
self._dividing_line = dividing_line
|
43
43
|
self._ignore_command_register = ignore_command_register
|
44
44
|
self._repeat_command_groups_description = repeat_command_groups
|
45
45
|
self._full_override_system_messages = full_override_system_messages
|
46
|
+
self._autocompleter = autocompleter
|
46
47
|
|
47
48
|
self._farewell_message = farewell_message
|
48
49
|
self._initial_message = initial_message
|
49
50
|
|
50
|
-
|
51
|
+
|
52
|
+
self._description_message_gen: Callable[[str, str], str] = lambda command, description: f'[bold red]{escape('['+command+']')}[/bold red] [blue dim]*=*=*[/blue dim] [bold yellow italic]{escape(description)}'
|
51
53
|
self._registered_routers: RegisteredRouters = RegisteredRouters()
|
52
54
|
self._messages_on_startup = []
|
53
55
|
|
@@ -59,14 +61,8 @@ class AppInit:
|
|
59
61
|
|
60
62
|
|
61
63
|
class AppSetters(AppInit):
|
62
|
-
def set_description_message_pattern(self, pattern: str) -> None:
|
63
|
-
|
64
|
-
second_check = re.match(r'.*{description}.*', pattern)
|
65
|
-
|
66
|
-
if bool(first_check) and bool(second_check):
|
67
|
-
self._description_message_pattern: str = pattern
|
68
|
-
else:
|
69
|
-
raise InvalidDescriptionMessagePatternException(pattern)
|
64
|
+
def set_description_message_pattern(self, pattern: Callable[[str, str], str]) -> None:
|
65
|
+
self._description_message_gen: Callable[[str, str], str] = pattern
|
70
66
|
|
71
67
|
|
72
68
|
def set_invalid_input_flags_handler(self, handler: Callable[[str], None]) -> None:
|
@@ -92,11 +88,12 @@ class AppSetters(AppInit):
|
|
92
88
|
class AppPrinters(AppInit):
|
93
89
|
def _print_command_group_description(self):
|
94
90
|
for registered_router in self._registered_routers:
|
95
|
-
|
91
|
+
if registered_router.get_title():
|
92
|
+
self._print_func(registered_router.get_title())
|
96
93
|
for command_handler in registered_router.get_command_handlers():
|
97
|
-
self._print_func(self.
|
98
|
-
|
99
|
-
|
94
|
+
self._print_func(self._description_message_gen(
|
95
|
+
command_handler.get_handled_command().get_trigger(),
|
96
|
+
command_handler.get_handled_command().get_description()))
|
100
97
|
self._print_func('')
|
101
98
|
|
102
99
|
|
@@ -111,7 +108,7 @@ class AppPrinters(AppInit):
|
|
111
108
|
|
112
109
|
class AppNonStandardHandlers(AppPrinters):
|
113
110
|
def _is_exit_command(self, command: InputCommand):
|
114
|
-
if command.get_trigger().lower() == self._exit_command.lower():
|
111
|
+
if command.get_trigger().lower() == self._exit_command.get_trigger().lower():
|
115
112
|
if self._ignore_command_register:
|
116
113
|
system_router.input_command_handler(command)
|
117
114
|
return True
|
@@ -125,10 +122,16 @@ class AppNonStandardHandlers(AppPrinters):
|
|
125
122
|
for router_entity in self._registered_routers:
|
126
123
|
for command_handler in router_entity.get_command_handlers():
|
127
124
|
handled_command_trigger = command_handler.get_handled_command().get_trigger()
|
125
|
+
handled_command_aliases = command_handler.get_handled_command().get_aliases()
|
128
126
|
if handled_command_trigger.lower() == command.get_trigger().lower() and self._ignore_command_register:
|
129
127
|
return False
|
130
128
|
elif handled_command_trigger == command.get_trigger():
|
131
129
|
return False
|
130
|
+
elif handled_command_aliases:
|
131
|
+
if command.get_trigger().lower() in [x.lower() for x in handled_command_aliases] and self._ignore_command_register:
|
132
|
+
return False
|
133
|
+
elif command.get_trigger() in handled_command_trigger:
|
134
|
+
return False
|
132
135
|
if isinstance(self._dividing_line, StaticDividingLine):
|
133
136
|
self._print_func(self._dividing_line.get_full_line())
|
134
137
|
self._unknown_command_handler(command)
|
@@ -167,7 +170,7 @@ class AppSetups(AppValidators, AppPrinters):
|
|
167
170
|
def _setup_system_router(self):
|
168
171
|
system_router.set_title(self._system_points_title)
|
169
172
|
|
170
|
-
@system_router.command(
|
173
|
+
@system_router.command(self._exit_command)
|
171
174
|
def exit_command():
|
172
175
|
self._exit_command_handler()
|
173
176
|
|
@@ -188,6 +191,12 @@ class AppSetups(AppValidators, AppPrinters):
|
|
188
191
|
self._validate_number_of_routers()
|
189
192
|
self._validate_included_routers()
|
190
193
|
|
194
|
+
all_triggers: list[str] = []
|
195
|
+
for router_entity in self._registered_routers:
|
196
|
+
all_triggers.extend(router_entity.get_triggers())
|
197
|
+
all_triggers.extend(router_entity.get_aliases())
|
198
|
+
self._autocompleter.initial_setup(all_triggers)
|
199
|
+
|
191
200
|
self._print_func(self._initial_message)
|
192
201
|
|
193
202
|
for message in self._messages_on_startup:
|
@@ -221,6 +230,7 @@ class App(AppSetters, AppNonStandardHandlers, AppSetups):
|
|
221
230
|
continue
|
222
231
|
|
223
232
|
if self._is_exit_command(input_command):
|
233
|
+
self._autocompleter.exit_setup()
|
224
234
|
return
|
225
235
|
|
226
236
|
if self._is_unknown_command(input_command):
|
argenta/command/models.py
CHANGED
@@ -19,14 +19,19 @@ class BaseCommand:
|
|
19
19
|
class Command(BaseCommand):
|
20
20
|
def __init__(self, trigger: str,
|
21
21
|
description: str = None,
|
22
|
-
flags: Flag | Flags = None
|
22
|
+
flags: Flag | Flags = None,
|
23
|
+
aliases: list[str] = None):
|
23
24
|
super().__init__(trigger)
|
24
25
|
self._registered_flags: Flags = flags if isinstance(flags, Flags) else Flags(flags) if isinstance(flags, Flag) else Flags()
|
25
|
-
self._description = f'
|
26
|
+
self._description = f'Description for "{self._trigger}" command' if not description else description
|
27
|
+
self._aliases = aliases
|
26
28
|
|
27
29
|
def get_registered_flags(self) -> Flags:
|
28
30
|
return self._registered_flags
|
29
31
|
|
32
|
+
def get_aliases(self) -> list[str] | None:
|
33
|
+
return self._aliases
|
34
|
+
|
30
35
|
def validate_input_flag(self, flag: InputFlag):
|
31
36
|
registered_flags: Flags | None = self.get_registered_flags()
|
32
37
|
if registered_flags:
|
@@ -5,17 +5,17 @@ from argenta.command.flag.models import InputFlags
|
|
5
5
|
|
6
6
|
class CommandHandler:
|
7
7
|
def __init__(self, handler: Callable[[], None] | Callable[[InputFlags], None], handled_command: Command):
|
8
|
-
self.
|
9
|
-
self.
|
8
|
+
self._handler = handler
|
9
|
+
self._handled_command = handled_command
|
10
10
|
|
11
11
|
def handling(self, input_flags: InputFlags = None):
|
12
12
|
if input_flags is not None:
|
13
|
-
self.
|
13
|
+
self._handler(input_flags)
|
14
14
|
else:
|
15
|
-
self.
|
15
|
+
self._handler()
|
16
16
|
|
17
17
|
def get_handler(self):
|
18
|
-
return self.
|
18
|
+
return self._handler
|
19
19
|
|
20
20
|
def get_handled_command(self):
|
21
|
-
return self.
|
21
|
+
return self._handled_command
|
argenta/router/entity.py
CHANGED
@@ -7,15 +7,15 @@ from argenta.router.command_handlers.entity import CommandHandlers
|
|
7
7
|
from argenta.router.command_handler.entity import CommandHandler
|
8
8
|
from argenta.command.flag.models import Flag, Flags, InputFlags
|
9
9
|
from argenta.router.exceptions import (RepeatedFlagNameException,
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
TooManyTransferredArgsException,
|
11
|
+
RequiredArgumentNotPassedException,
|
12
|
+
IncorrectNumberOfHandlerArgsException,
|
13
|
+
TriggerCannotContainSpacesException)
|
14
14
|
|
15
15
|
|
16
16
|
class Router:
|
17
17
|
def __init__(self,
|
18
|
-
title: str =
|
18
|
+
title: str = None,
|
19
19
|
name: str = 'Default'):
|
20
20
|
self._title = title
|
21
21
|
self._name = name
|
@@ -54,21 +54,29 @@ class Router:
|
|
54
54
|
for command_handler in self._command_handlers:
|
55
55
|
handle_command = command_handler.get_handled_command()
|
56
56
|
if input_command_name.lower() == handle_command.get_trigger().lower():
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
57
|
+
self._validate_input_command(input_command_flags, command_handler)
|
58
|
+
elif handle_command.get_aliases():
|
59
|
+
if input_command_name.lower() in handle_command.get_aliases():
|
60
|
+
self._validate_input_command(input_command_flags, command_handler)
|
61
|
+
|
62
|
+
|
63
|
+
def _validate_input_command(self, input_command_flags: InputFlags, command_handler: CommandHandler):
|
64
|
+
handle_command = command_handler.get_handled_command()
|
65
|
+
if handle_command.get_registered_flags().get_flags():
|
66
|
+
if input_command_flags.get_flags():
|
67
|
+
if self._validate_input_flags(handle_command, input_command_flags):
|
68
|
+
command_handler.handling(input_command_flags)
|
69
|
+
return
|
70
|
+
else:
|
71
|
+
command_handler.handling(input_command_flags)
|
72
|
+
return
|
73
|
+
else:
|
74
|
+
if input_command_flags.get_flags():
|
75
|
+
self._not_valid_flag_handler(input_command_flags[0])
|
76
|
+
return
|
77
|
+
else:
|
78
|
+
command_handler.handling()
|
79
|
+
return
|
72
80
|
|
73
81
|
|
74
82
|
def _validate_input_flags(self, handle_command: Command, input_flags: InputFlags):
|
@@ -110,6 +118,21 @@ class Router:
|
|
110
118
|
self._ignore_command_register = ignore_command_register
|
111
119
|
|
112
120
|
|
121
|
+
def get_triggers(self):
|
122
|
+
all_triggers: list[str] = []
|
123
|
+
for command_handler in self._command_handlers:
|
124
|
+
all_triggers.append(command_handler.get_handled_command().get_trigger())
|
125
|
+
return all_triggers
|
126
|
+
|
127
|
+
|
128
|
+
def get_aliases(self):
|
129
|
+
all_aliases: list[str] = []
|
130
|
+
for command_handler in self._command_handlers:
|
131
|
+
if command_handler.get_handled_command().get_aliases():
|
132
|
+
all_aliases.extend(command_handler.get_handled_command().get_aliases())
|
133
|
+
return all_aliases
|
134
|
+
|
135
|
+
|
113
136
|
def get_command_handlers(self) -> CommandHandlers:
|
114
137
|
return self._command_handlers
|
115
138
|
|
@@ -118,7 +141,7 @@ class Router:
|
|
118
141
|
return self._name
|
119
142
|
|
120
143
|
|
121
|
-
def get_title(self) -> str:
|
144
|
+
def get_title(self) -> str | None:
|
122
145
|
return self._title
|
123
146
|
|
124
147
|
|
@@ -1,10 +1,12 @@
|
|
1
1
|
argenta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
2
|
argenta/app/__init__.py,sha256=I8FTXU17ajDI-hbC6Rw0LxLmvDYipdQaos3v1pmu14E,57
|
3
|
-
argenta/app/
|
3
|
+
argenta/app/autocompleter/__init__.py,sha256=VT_p3QA78UnczV7pYR2NnwQ0Atd8mnDUnLazvUQNqJk,93
|
4
|
+
argenta/app/autocompleter/entity.py,sha256=ah85zH_RdibypTXQmgzjUU13TdhKdGpep0BgNcT_w_o,1827
|
5
|
+
argenta/app/defaults.py,sha256=D0z9yGnfF8hhWUxHCvBhxTrxg6a9rPiySAn2Fyy7TXQ,311
|
4
6
|
argenta/app/dividing_line/__init__.py,sha256=jJZDDZix8XYCAUWW4FzGJH0JmJlchYcx0FPWifjgv1I,147
|
5
7
|
argenta/app/dividing_line/models.py,sha256=ueBDmy1hfYzGAr1X2G2Mw0hjES7YQBtP7N3TLBDz9h0,700
|
6
|
-
argenta/app/exceptions.py,sha256=
|
7
|
-
argenta/app/models.py,sha256=
|
8
|
+
argenta/app/exceptions.py,sha256=oTvec3ZGX_tS1nqa0pLLAPqMlhrLS6SsXY2lYiOwIVM,465
|
9
|
+
argenta/app/models.py,sha256=yH6LGq_bA525JCq-cCl-O8v_JLbYLC6q30ZftPW5e2k,12736
|
8
10
|
argenta/app/registered_routers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
11
|
argenta/app/registered_routers/entity.py,sha256=OQZyrF4eoCoDHzRJ22zZxhNEx-bOUDu7NZIFDfO-fuY,688
|
10
12
|
argenta/command/__init__.py,sha256=Plo2Da0fhq8H1eo2mg7nA1-OBLuGjK2BYpDGRvGGMIU,67
|
@@ -12,16 +14,16 @@ argenta/command/exceptions.py,sha256=HOgddtXLDgk9Wx6c_GnzW3bMAMU5CuUnUyxjW3cVHRo
|
|
12
14
|
argenta/command/flag/__init__.py,sha256=PaZAaqU3DgyO1o5do-xKhWV6TyEuOSaQTmE4VbPY6JA,136
|
13
15
|
argenta/command/flag/defaults.py,sha256=ktKmDT0rSSBoFUghTlEQ6OletoFxCiD37hRzO73mUUc,875
|
14
16
|
argenta/command/flag/models.py,sha256=IY0FHyAFD9O1ZxSaq6NR9gSTkldoQGrKVoGrAbnmEuA,3769
|
15
|
-
argenta/command/models.py,sha256=
|
17
|
+
argenta/command/models.py,sha256=QhpxFuq-yLoxxegEQay0ZFFJT4-W1GOayDzM0gz_41Q,4594
|
16
18
|
argenta/router/__init__.py,sha256=ldrIWTXNLXUAMAGQ8ex4e8nMso_fhi01nZi2DVzHnnk,66
|
17
19
|
argenta/router/command_handler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
18
|
-
argenta/router/command_handler/entity.py,sha256=
|
20
|
+
argenta/router/command_handler/entity.py,sha256=wVBl-Z3oq3aDUYmGBeudM7TxPZZrUEdZw7BImWJL5LE,652
|
19
21
|
argenta/router/command_handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
22
|
argenta/router/command_handlers/entity.py,sha256=KgFKjAMUr_mOcn9xahTxMUKB6lIxXgqCuAsuc4TgP6E,747
|
21
23
|
argenta/router/defaults.py,sha256=huftOg1HMjrT_R2SHHOL4eJ5uZHspNEYBSg-mCq9xhU,126
|
22
|
-
argenta/router/entity.py,sha256=
|
24
|
+
argenta/router/entity.py,sha256=d9S9Af9vACAgUaiPq-CgVGrb7KcAZYfYm3oavVAceI8,5900
|
23
25
|
argenta/router/exceptions.py,sha256=tdeaR8zDvnytgRYo_wQWKHt3if2brapgauIhhMIsTsA,678
|
24
|
-
argenta-0.
|
25
|
-
argenta-0.
|
26
|
-
argenta-0.
|
27
|
-
argenta-0.
|
26
|
+
argenta-0.5.0a0.dist-info/LICENSE,sha256=zmqoGh2n5rReBv4s8wPxF_gZEZDgauJYSPMuPczgOiU,1082
|
27
|
+
argenta-0.5.0a0.dist-info/METADATA,sha256=5sPiqrm-Ab3I3exQFI1FtBNmPOwXgJqICYpVzkLLcWc,20987
|
28
|
+
argenta-0.5.0a0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
29
|
+
argenta-0.5.0a0.dist-info/RECORD,,
|
File without changes
|
File without changes
|