From d989471b4966dfec58fc22a3e067dcb34761a1c9 Mon Sep 17 00:00:00 2001 From: Namxobick Date: Sat, 24 Sep 2022 20:11:13 +0300 Subject: [PATCH 01/11] Array (Yurin Andrey) The main functions of the array --- array.py | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 array.py diff --git a/array.py b/array.py new file mode 100644 index 0000000..69f3335 --- /dev/null +++ b/array.py @@ -0,0 +1,61 @@ +class Array(object): + + def __init__(self, *args): + self._data = args + + def __str__(self): + return str(self._data) + + def __add__(self, other): + if not isinstance(other, Array): + print(f"Warning, expected type Array (type {other} is {type(other)})") + new_array = Array(*self._data, other) + else: + new_array = Array(*self._data, *other._data) + return new_array + + def __len__(self): + return len(self._data) + + def __iter__(self): + return iter(self._data) + + def __getitem__(self, item: int): + if not isinstance(item, int): + print(f"Error, expected type Array (type {item} is {type(item)})") + elif item < -len(self._data) or item >= len(self._data): + print("Error,index out of range") + else: + return self._data[item] + return + + def __setitem__(self, key: int, value): + if not isinstance(key, int): + print(f"Error, expected type Array (type {key} is {type(key)})") + return + + if key < 0: + key = len(self) + key + + if key < 0 or key >= len(self): + print("Error, index out of range") + return + + self._data = Array(*self._data[:key], value, *self[key + 1:]) + + def __eq__(self, other): + if not isinstance(other, Array): + return False + if self._data == other._data: + return True + else: + return False + + def append(self, new_element): + self._data = Array(*self._data, new_element) + + def index(self, searched_element): + try: + return self._data.index(searched_element) + except ValueError: + return -1 From 237ee9d67b404d0f7c6b64763bfada1bad6513fb Mon Sep 17 00:00:00 2001 From: Namxobick Date: Sat, 24 Sep 2022 20:15:23 +0300 Subject: [PATCH 02/11] Changed the folder system --- array.py => "homeworks/Yurin Andrey/\342\204\2261/Array.py" | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename array.py => "homeworks/Yurin Andrey/\342\204\2261/Array.py" (100%) diff --git a/array.py "b/homeworks/Yurin Andrey/\342\204\2261/Array.py" similarity index 100% rename from array.py rename to "homeworks/Yurin Andrey/\342\204\2261/Array.py" From aefd4fd52edb93f403cccd462ff717a2a5ab85aa Mon Sep 17 00:00:00 2001 From: Namxobick Date: Wed, 28 Sep 2022 00:00:45 +0300 Subject: [PATCH 03/11] Fixed error description text --- "homeworks/Yurin Andrey/\342\204\2261/Array.py" | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git "a/homeworks/Yurin Andrey/\342\204\2261/Array.py" "b/homeworks/Yurin Andrey/\342\204\2261/Array.py" index 69f3335..36061f7 100644 --- "a/homeworks/Yurin Andrey/\342\204\2261/Array.py" +++ "b/homeworks/Yurin Andrey/\342\204\2261/Array.py" @@ -22,7 +22,7 @@ def __iter__(self): def __getitem__(self, item: int): if not isinstance(item, int): - print(f"Error, expected type Array (type {item} is {type(item)})") + print(f"Error, expected type int (type {item} is {type(item)})") elif item < -len(self._data) or item >= len(self._data): print("Error,index out of range") else: @@ -31,7 +31,7 @@ def __getitem__(self, item: int): def __setitem__(self, key: int, value): if not isinstance(key, int): - print(f"Error, expected type Array (type {key} is {type(key)})") + print(f"Error, expected type int (type {key} is {type(key)})") return if key < 0: @@ -58,4 +58,4 @@ def index(self, searched_element): try: return self._data.index(searched_element) except ValueError: - return -1 + return -1 \ No newline at end of file From 0ea1017016211c4e2699cb4e7014551b140279e3 Mon Sep 17 00:00:00 2001 From: Namxobick Date: Tue, 4 Oct 2022 17:00:17 +0300 Subject: [PATCH 04/11] =?UTF-8?q?Add=20HW3=20and=20practice=20=E2=84=961?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Yurin Andrey/\342\204\2262/Array.py" | 0 .../\342\204\2263/\320\241ontract.py" | 29 ++++++ "practice/\342\204\2261/game_15_puzzle.py" | 89 +++++++++++++++++++ 3 files changed, 118 insertions(+) rename "homeworks/Yurin Andrey/\342\204\2261/Array.py" => "homeworks/Yurin Andrey/\342\204\2262/Array.py" (100%) create mode 100644 "homeworks/Yurin Andrey/\342\204\2263/\320\241ontract.py" create mode 100644 "practice/\342\204\2261/game_15_puzzle.py" diff --git "a/homeworks/Yurin Andrey/\342\204\2261/Array.py" "b/homeworks/Yurin Andrey/\342\204\2262/Array.py" similarity index 100% rename from "homeworks/Yurin Andrey/\342\204\2261/Array.py" rename to "homeworks/Yurin Andrey/\342\204\2262/Array.py" diff --git "a/homeworks/Yurin Andrey/\342\204\2263/\320\241ontract.py" "b/homeworks/Yurin Andrey/\342\204\2263/\320\241ontract.py" new file mode 100644 index 0000000..ba8137e --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2263/\320\241ontract.py" @@ -0,0 +1,29 @@ +class ContractError(Exception): + """We use this error when someone breaks our contract.""" + + +#: Special value, that indicates that validation for this type is not required. +Any = object() + + +def contract(arg_types=None, return_type=None, raises=None): + def decorator(function): + def wrapper(*args, **kwargs): + if arg_types is not None: + for index, arg_type in enumerate(arg_types): + if arg_type is not Any and arg_type != type(args[index]): + raise ContractError('Invalid argument type') from TypeError + + try: + result = function(*args, **kwargs) + except (raises if raises is not None and Any not in raises else Exception) as ex: + raise ex + except Exception: + raise ContractError from Exception + + if return_type is not None and not isinstance(result, return_type): + raise ContractError('Invalid return type') from TypeError + + return result + return wrapper + return decorator diff --git "a/practice/\342\204\2261/game_15_puzzle.py" "b/practice/\342\204\2261/game_15_puzzle.py" new file mode 100644 index 0000000..320d9dd --- /dev/null +++ "b/practice/\342\204\2261/game_15_puzzle.py" @@ -0,0 +1,89 @@ +import random + +EMPTY_MARK = 'x' + +MOVES = { + 'w': -4, + 's': 4, + 'a': -1, + 'd': 1, +} + + +def shuffle_field(gameMap): + mixingDepth = random.randint(50, 100) + for i in range(mixingDepth): + key = random.choice(["w", "s", "d", "a"]) + perform_move(gameMap, key) + return gameMap + pass + + +def print_field(gameMap): + for i in range(len(gameMap)): + if i % 4 == 0 and i != 0: + print() + print(gameMap[i], end=' ') + pass + + +def is_game_finished(gameMap): + if gameMap[:len(gameMap) - 1] == [(i + 1) for i in range(15)]: + return True + else: + return False + pass + + +def perform_move(gameMap, key): + indexEmptyTile = gameMap.index('x') + + if key == 'w': + if indexEmptyTile in [12, 13, 14, 15]: + return None + else: + gameMap[indexEmptyTile], gameMap[indexEmptyTile + 4] = gameMap[indexEmptyTile + 4], gameMap[indexEmptyTile] + + elif key == 's': + if indexEmptyTile in [0, 1, 2, 3]: + return None + else: + gameMap[indexEmptyTile], gameMap[indexEmptyTile - 4] = gameMap[indexEmptyTile - 4], gameMap[indexEmptyTile] + + elif key == 'a': + if indexEmptyTile in [3, 7, 11, 15]: + return None + else: + gameMap[indexEmptyTile], gameMap[indexEmptyTile + 1] = gameMap[indexEmptyTile + 1], gameMap[indexEmptyTile] + else: + if indexEmptyTile in [0, 4, 8, 12]: + return None + else: + gameMap[indexEmptyTile], gameMap[indexEmptyTile - 1] = gameMap[indexEmptyTile - 1], gameMap[indexEmptyTile] + pass + + +def handle_user_input(): + print() + key = input() + return key + + pass + + +def main(): + GameMap = shuffle_field([(i + 1) for i in range(15)] + [EMPTY_MARK]) + + while not(is_game_finished(GameMap)): + print_field(GameMap) + perform_move(GameMap, handle_user_input()) + print("WIN") + + pass + + +if __name__ == '__main__': + + main() + + From 98a63f3a1c2172d448c3527626bd4cb49ce6aeb1 Mon Sep 17 00:00:00 2001 From: Namxobick Date: Wed, 5 Oct 2022 19:36:11 +0300 Subject: [PATCH 05/11] Create exceptions.py --- "practice/\342\204\2262/exceptions.py" | 161 +++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 "practice/\342\204\2262/exceptions.py" diff --git "a/practice/\342\204\2262/exceptions.py" "b/practice/\342\204\2262/exceptions.py" new file mode 100644 index 0000000..2e4d3bb --- /dev/null +++ "b/practice/\342\204\2262/exceptions.py" @@ -0,0 +1,161 @@ +import math + + +def call_system_exit(): + try: + exit() + except SystemExit: + print('SystemExit') + + +def call_generator_exit(): + def generate(): + values = [0, 1] + for value in values: + try: + yield value + + except GeneratorExit: + print('GeneratorExit') + raise + + my_generator = generate() + next(my_generator) + my_generator.close() + + +def call_runtime_error(): + def generate(): + values = [0, 1] + for value in values: + try: + yield value + + except RuntimeError: + print('RuntimeError') + + my_generator = generate() + next(my_generator) + my_generator.close() + + +def call_stop_iteration(): + def generate(): + for value in [0]: + yield value + return + + my_generator = generate() + for i in range(2): + try: + next(my_generator) + except StopIteration: + print("StopIteration") + + +def call_overflow_error(): + results = [] + for i in range(1000): + try: + results.append(math.exp(i)) + except OverflowError: + print("OverflowError") + return + + +def call_zero_division_error(): + try: + x = 1 / 0 + except ZeroDivisionError: + print("ZeroDivisionError") + + +def call_assertion_error(): + try: + assert 0 == 1 + except AssertionError: + print("AssertionError") + + +def call_attribute_error(): + my_str = 'my' + try: + my_str.noattr + except AttributeError: + print("AttributeError") + + +def call_value_error(): + try: + chr(-100) + except ValueError: + print("ValueError") + + +def call_index_error(): + a = [] + try: + a[1] + except IndexError: + print("IndexError") + + +def call_key_error(): + my_dict = {0: 0} + try: + my_dict[1] + except KeyError: + print("KeyError") + + +def call_file_not_found_error(): + try: + f = open('') + except FileNotFoundError: + print('FileNotFoundError') + + +def call_memory_error(): + try: + x = list(range(10000000000)) + except MemoryError: + print('MemoryError') + + +def call_type_error(): + try: + x = 'a' + 5 + except TypeError: + print('TypeError') + + +def call_recursion_error(): + try: + call_recursion_error() + except RecursionError: + print('RecursionError') + + +def call_unicode_decoder_error(): + try: + b'\xd0'.decode('utf-8') + except UnicodeDecodeError: + print('UnicodeDecodeError') + + +call_system_exit() +call_generator_exit() +call_runtime_error() +call_stop_iteration() +call_overflow_error() +call_zero_division_error() +call_assertion_error() +call_attribute_error() +call_value_error() +call_index_error() +call_key_error() +call_file_not_found_error() +call_memory_error() +call_type_error() +call_recursion_error() +call_unicode_decoder_error() From ee4624ff2916b6fe233555050276fd75f4611846 Mon Sep 17 00:00:00 2001 From: Namxobick Date: Wed, 12 Oct 2022 00:08:58 +0300 Subject: [PATCH 06/11] Add HW4 --- .../\342\204\2264/.idea/.gitignore" | 3 + .../Yurin Andrey/\342\204\2264/.idea/hw5.iml" | 12 ++ .../inspectionProfiles/profiles_settings.xml" | 6 + .../\342\204\2264/.idea/misc.xml" | 4 + .../\342\204\2264/.idea/modules.xml" | 8 ++ .../Yurin Andrey/\342\204\2264/README.md" | 27 ++++ "homeworks/Yurin Andrey/\342\204\2264/hw5.md" | 53 +++++++ .../\342\204\2264/tests/conftest.py" | 5 + .../\342\204\2264/tests/test_done_command.py" | 30 ++++ .../tests/test_representations.py" | 18 +++ .../\342\204\2264/tests/test_to_read_item.py" | 43 ++++++ .../tests/test_undone_command.py" | 30 ++++ .../\342\204\2264/todo/__init__.py" | 0 .../\342\204\2264/todo/__main__.py" | 28 ++++ .../\342\204\2264/todo/commands.py" | 132 ++++++++++++++++++ .../\342\204\2264/todo/custom_exceptions.py" | 2 + .../\342\204\2264/todo/models.py" | 77 ++++++++++ .../\342\204\2264/todo/reflection.py" | 31 ++++ .../\342\204\2264/todo/runtime.py" | 52 +++++++ .../\342\204\2264/todo/storage.py" | 21 +++ 20 files changed, 582 insertions(+) create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/.idea/.gitignore" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/.idea/hw5.iml" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/.idea/inspectionProfiles/profiles_settings.xml" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/.idea/misc.xml" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/.idea/modules.xml" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/README.md" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/hw5.md" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/tests/conftest.py" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/tests/test_done_command.py" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/tests/test_representations.py" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/tests/test_to_read_item.py" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/tests/test_undone_command.py" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/todo/__init__.py" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/todo/__main__.py" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/todo/commands.py" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/todo/custom_exceptions.py" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/todo/models.py" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/todo/reflection.py" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/todo/runtime.py" create mode 100644 "homeworks/Yurin Andrey/\342\204\2264/todo/storage.py" diff --git "a/homeworks/Yurin Andrey/\342\204\2264/.idea/.gitignore" "b/homeworks/Yurin Andrey/\342\204\2264/.idea/.gitignore" new file mode 100644 index 0000000..26d3352 --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/.idea/.gitignore" @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git "a/homeworks/Yurin Andrey/\342\204\2264/.idea/hw5.iml" "b/homeworks/Yurin Andrey/\342\204\2264/.idea/hw5.iml" new file mode 100644 index 0000000..8b8c395 --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/.idea/hw5.iml" @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git "a/homeworks/Yurin Andrey/\342\204\2264/.idea/inspectionProfiles/profiles_settings.xml" "b/homeworks/Yurin Andrey/\342\204\2264/.idea/inspectionProfiles/profiles_settings.xml" new file mode 100644 index 0000000..105ce2d --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/.idea/inspectionProfiles/profiles_settings.xml" @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git "a/homeworks/Yurin Andrey/\342\204\2264/.idea/misc.xml" "b/homeworks/Yurin Andrey/\342\204\2264/.idea/misc.xml" new file mode 100644 index 0000000..d1e22ec --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/.idea/misc.xml" @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git "a/homeworks/Yurin Andrey/\342\204\2264/.idea/modules.xml" "b/homeworks/Yurin Andrey/\342\204\2264/.idea/modules.xml" new file mode 100644 index 0000000..8f8a12d --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/.idea/modules.xml" @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git "a/homeworks/Yurin Andrey/\342\204\2264/README.md" "b/homeworks/Yurin Andrey/\342\204\2264/README.md" new file mode 100644 index 0000000..3f2028c --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/README.md" @@ -0,0 +1,27 @@ +# TODO CLI App + + +## Legend + +This application is used for our internal needs. +It was developed a long time ago by an unknown developer. + +It works fine. But there are several things we would like to improve. + + +## Running + +To run this app follow these steps: + +1. `cd hw5` +2. `python3 -m todo` ([docs](https://docs.python.org/3/using/cmdline.html#cmdoption-m)) +3. Enjoy! + + +## Testing + +If you don't know what you are doing, skip this step. + +0. Make sure that you are in this folder, near `README.md` +1. `pip install pytest` +2. `pytest` diff --git "a/homeworks/Yurin Andrey/\342\204\2264/hw5.md" "b/homeworks/Yurin Andrey/\342\204\2264/hw5.md" new file mode 100644 index 0000000..c587eff --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/hw5.md" @@ -0,0 +1,53 @@ +## Теория + +### Полезности + +- `datetime`: https://docs.python.org/3/library/datetime.html +- `collections`: https://docs.python.org/3/library/collections.html +- `itertools`: https://docs.python.org/3/library/itertools.html +- `functools`: https://docs.python.org/3/library/functools.html +- `os`: https://docs.python.org/3/library/os.html +- `sys`: https://docs.python.org/3/library/sys.html +- `re`: https://docs.python.org/3/library/re.html +- `argparse`: https://docs.python.org/3/library/argparse.html + +### Imports + +- modules: https://docs.python.org/3/tutorial/modules.html +- Решаем проблему цикличных импортов: https://stackoverflow.com/questions/5748946/pythonic-way-to-resolve-circular-import-statements +- Откуда можно импортировать: https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH и https://stackoverflow.com/questions/19917492/how-to-use-pythonpath и http://www.johnny-lin.com/cdat_tips/tips_pylang/path.html +- Relative imports vs absolute: https://stackoverflow.com/questions/28400690/python3-correct-way-to-import-relative-or-absolute и http://pulkitgoyal.com/absolute-relative-imports + + +## Практика + +Данное задание будет построено в формате ролевой игры, чтобы показать реальность подобных задач, ситуаций и подходов. + +### Легенда + +В компании, куда вы пришли работать, есть для вас первая задача. +Все сотрудники пользуются внутренней системой ведения учета задач. +Но вот проблема, в ней есть некоторые моменты, которые нужно улучшить. + +Никто точно не помнит, кто написал программу и когда. +К сожаению, спросить, как она работает, некого. +Более того, сами сотрудники не могут посмотреть, что там в коде, потому что они не программисты. +Но они слышали, что там есть сложные части, а есть какие-то простые. +Вроде бы код написан нормально, но документации к нему не сохранилось. +Только инструкция по запуску. + +### Технические требования + +Менеджер подготовил для вас техническое задание, какие фичи он хотел бы увидеть: + +- Необходимо добавить всем задачам визуальный статус готовности. Он должен выглядеть так: `+ ToDo: ...`, где `+` или `-` - статус готовности (`+` - выполнено, `-` - невыполнено), `ToDo` - название типа задачи и `...` - атрибуты задачи +- Необходимо реализовать команду `done`, которая бы отмечала задачи выполнеными (все задачи по-умолчанию невыполнены) +- Необходимо реализовать команду `undone`, которая бы отмечала задачи невыполнеными +- Необходимо добавить новый тип задачи: `ToReadItem`, у которой было бы два поля: `heading` и `url`. Следовательно: что прочитать и где + +### Проверка результатов (для продвинуты, необязательно) + +Старый разработчик был большим фанатом [TTD](https://en.wikipedia.org/wiki/Test-driven_development), и он успел перед своим уходом написать тесты, как должы работать все те новые требования. +Но закончить проект не успел. + +В качестве проверки будем использовать их. diff --git "a/homeworks/Yurin Andrey/\342\204\2264/tests/conftest.py" "b/homeworks/Yurin Andrey/\342\204\2264/tests/conftest.py" new file mode 100644 index 0000000..e846edf --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/tests/conftest.py" @@ -0,0 +1,5 @@ +import os +import sys + +BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) +sys.path.append(BASE_DIR) diff --git "a/homeworks/Yurin Andrey/\342\204\2264/tests/test_done_command.py" "b/homeworks/Yurin Andrey/\342\204\2264/tests/test_done_command.py" new file mode 100644 index 0000000..83a6927 --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/tests/test_done_command.py" @@ -0,0 +1,30 @@ +def test_class_exists(): + from todo.commands import DoneCommand + + assert isinstance(DoneCommand, type) + + +def test_command_label_in_list(): + from todo.commands import DoneCommand + from todo.runtime import get_routes + + assert DoneCommand().label in get_routes() + + +def test_command_execution(monkeypatch): + from todo.commands import DoneCommand + from todo.runtime import perform_command + from todo.models import Storage, ToDoItem + + monkeypatch.setitem(__builtins__, 'input', lambda _: 0) + + item = ToDoItem('test') + item.done = False + + s = Storage() + s.items.clear() + s.items.append(item) + + perform_command(DoneCommand().label) + + assert item.done is True diff --git "a/homeworks/Yurin Andrey/\342\204\2264/tests/test_representations.py" "b/homeworks/Yurin Andrey/\342\204\2264/tests/test_representations.py" new file mode 100644 index 0000000..07043c8 --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/tests/test_representations.py" @@ -0,0 +1,18 @@ +def test_to_buy_item(): + from todo.models import ToBuyItem + + item = ToBuyItem('header', 'price') + assert str(item).startswith('- ToBuy: ') + + item.done = True + assert str(item).startswith('+ ToBuy: ') + + +def test_to_do_item(): + from todo.models import ToDoItem + + item = ToDoItem('subject') + assert str(item).startswith('- ToDo: ') + + item.done = True + assert str(item).startswith('+ ToDo: ') diff --git "a/homeworks/Yurin Andrey/\342\204\2264/tests/test_to_read_item.py" "b/homeworks/Yurin Andrey/\342\204\2264/tests/test_to_read_item.py" new file mode 100644 index 0000000..b1ea1e3 --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/tests/test_to_read_item.py" @@ -0,0 +1,43 @@ +def test_class_exists(): + from todo.models import ToReadItem + + assert isinstance(ToReadItem, type) + + +def test_there_are_attributes(): + from todo.models import ToReadItem + + heading = 'test' + url = 'http://ya.ru' + item = ToReadItem(heading, url) + + assert item.heading == heading + assert item.url == url + assert item.done is False + + +def test_fabric_method(monkeypatch): + from todo.models import ToReadItem + + value = 'some input' + + monkeypatch.setitem(__builtins__, 'input', lambda _: value) + item = ToReadItem.construct() + + assert item.heading == value + assert item.url == value + assert item.done is False + + +def test_representation(): + from todo.models import ToReadItem + + heading = 'test' + url = 'http://ya.ru' + item = ToReadItem(heading, url) + + assert str(item) == '- ToRead: {} {}'.format(heading, url) + + item.done = True + + assert str(item) == '+ ToRead: {} {}'.format(heading, url) diff --git "a/homeworks/Yurin Andrey/\342\204\2264/tests/test_undone_command.py" "b/homeworks/Yurin Andrey/\342\204\2264/tests/test_undone_command.py" new file mode 100644 index 0000000..b25bedf --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/tests/test_undone_command.py" @@ -0,0 +1,30 @@ +def test_class_exists(): + from todo.commands import UndoneCommand + + assert isinstance(UndoneCommand, type) + + +def test_command_label_in_list(): + from todo.commands import UndoneCommand + from todo.runtime import get_routes + + assert UndoneCommand().label in get_routes() + + +def test_command_execution(monkeypatch): + from todo.commands import UndoneCommand + from todo.runtime import perform_command + from todo.models import Storage, ToDoItem + + monkeypatch.setitem(__builtins__, 'input', lambda _: 0) + + item = ToDoItem('test') + item.done = True + + s = Storage() + s.items.clear() + s.items.append(item) + + perform_command(UndoneCommand().label) + + assert item.done is False diff --git "a/homeworks/Yurin Andrey/\342\204\2264/todo/__init__.py" "b/homeworks/Yurin Andrey/\342\204\2264/todo/__init__.py" new file mode 100644 index 0000000..e69de29 diff --git "a/homeworks/Yurin Andrey/\342\204\2264/todo/__main__.py" "b/homeworks/Yurin Andrey/\342\204\2264/todo/__main__.py" new file mode 100644 index 0000000..55f9f2c --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/todo/__main__.py" @@ -0,0 +1,28 @@ +""" +Main file. Contains program execution logic. +""" + +from todo.custom_exceptions import UserExitException +from todo.runtime import parse_user_input, perform_command + + +def main(): + """ + Main method, works infinitely until user runs `exit` command. + Or hits `Ctrl+C` in the console. + """ + while True: + try: + perform_command(parse_user_input()) + except UserExitException: + break + except Exception as ex: + print('You have done something wrong!', ex) + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + print() + print('Shutting down, bye!') diff --git "a/homeworks/Yurin Andrey/\342\204\2264/todo/commands.py" "b/homeworks/Yurin Andrey/\342\204\2264/todo/commands.py" new file mode 100644 index 0000000..dea1063 --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/todo/commands.py" @@ -0,0 +1,132 @@ + +from todo.custom_exceptions import UserExitException +from todo.models import BaseItem +from todo.reflection import find_classes + + +class BaseCommand(object): + label: str + + def perform(self, store): + raise NotImplementedError() + + +class ListCommand(BaseCommand): + label = 'list' + + def perform(self, store): + if len(store.items) == 0: + print('There are no items in the storage.') + return + + for index, obj in enumerate(store.items): + print('{0}: {1}'.format(index, str(obj))) + + +class NewCommand(BaseCommand): + label = 'new' + + def perform(self, store): + classes = self._load_item_classes() + + print('Select item type:') + for index, name in enumerate(classes.keys()): + print('{0}: {1}'.format(index, name)) + + selection = None + selected_key = None + + while True: + try: + selected_key = self._select_item(classes) + except ValueError: + print('Bad input, try again.') + except IndexError: + print('Wrong index, try again.') + else: + break + + selected_class = classes[selected_key] + print('Selected: {0}'.format(selected_class.__name__)) + print() + + new_object = selected_class.construct() + + store.items.append(new_object) + + print('Added {0}'.format(str(new_object))) + print() + return new_object + + def _load_item_classes(self) -> dict: + # Dynamic load: + return dict(find_classes(BaseItem)) + + def _select_item(self, classes): + selection = int(input('Input number: ')) + if selection < 0: + raise IndexError('Index needs to be >0') + return list(classes.keys())[selection] + + +class BaseMarkCommand(object): + + def perform(self, store): + counts = 0 + + for obj in store.items: + if str(obj)[0] == self.char: + counts += 1 + + if counts == 0: + print('There are no {0}items in the storage.'.format(self.char)) + return + + for index, obj in enumerate(store.items): + if str(obj)[0] == self.char: + print('{0}: {1}'.format(index, str(obj))) + + selected_item = self.select_item(store) + + print('{0} has been {1}'.format(selected_item, self.operation)) + print() + + operation = getattr(selected_item, str(self.operation), None) + assert operation is not None + operation() + + def select_item(self, store): + while True: + try: + selection = int(input('Input number of item: ')) + if selection < 0 or str(store.items[selection])[0] != self.char: + raise IndexError('MarkCommand -> select_item') + except ValueError: + print('Bad input, try again.') + except IndexError: + print('Wrong index, try again.') + else: + break + + if store.items is None: + print('There are no items in the storage.') + return store.items[selection] + + +class DoneCommand(BaseMarkCommand, BaseCommand): + label = 'done' + operation = 'mark_done' + char = '-' + + +class UndoneCommand(BaseMarkCommand, BaseCommand): + label = 'undone' + operation = 'mark_undone' + char = '+' + + +class ExitCommand(BaseCommand): + label = 'exit' + + def perform(self, _store): + raise UserExitException('See you next time!') diff --git "a/homeworks/Yurin Andrey/\342\204\2264/todo/custom_exceptions.py" "b/homeworks/Yurin Andrey/\342\204\2264/todo/custom_exceptions.py" new file mode 100644 index 0000000..5e8963c --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/todo/custom_exceptions.py" @@ -0,0 +1,2 @@ +class UserExitException(Exception): + """We use this exception when user wants to exit.""" diff --git "a/homeworks/Yurin Andrey/\342\204\2264/todo/models.py" "b/homeworks/Yurin Andrey/\342\204\2264/todo/models.py" new file mode 100644 index 0000000..aaaa03c --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/todo/models.py" @@ -0,0 +1,77 @@ +class BaseItem(object): + def __init__(self, heading): + self.heading = heading + self.done = False # TODO: make sure we can use it... + + def __repr__(self): + return self.__class__.__name__ + + def __str__(self): + return '{0} {1}'.format( + '+' if self.done else '-', + self.__repr__(), + ) + + def set_done(self, status: bool): + self.done = status + + def mark_done(self): + self.done = True + + def mark_undone(self): + self.done = False + + @classmethod + def construct(cls): + raise NotImplementedError() + + +class ToDoItem(BaseItem): + def __str__(self): + return '{0}: {1}'.format( + super().__str__(), + self.heading, + ) + + @classmethod + def construct(cls): + heading = input('Input heading: ') + return cls(heading) + + +class ToBuyItem(BaseItem): + def __init__(self, heading, price): + super().__init__(heading) + self.price = price + + def __str__(self): + return '{0}: {1} for {2}'.format( + super().__str__(), + self.heading, + self.price, + ) + + @classmethod + def construct(cls): + heading = input('Input heading: ') + price = input('Input price: ') + return cls(heading, price) + + +class ToReadItem(BaseItem): + def __init__(self, heading, url): + super().__init__(heading) + self.url = url + + def __str__(self): + return '{0}: {1} {2}'.format( + super().__str__(), + self.heading, + self.url, + ) + + @classmethod + def construct(cls): + heading = input('Input heading: ') + url = input('Input url: ') + return cls(heading, url) diff --git "a/homeworks/Yurin Andrey/\342\204\2264/todo/reflection.py" "b/homeworks/Yurin Andrey/\342\204\2264/todo/reflection.py" new file mode 100644 index 0000000..e16c6f6 --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/todo/reflection.py" @@ -0,0 +1,31 @@ +import inspect +import sys + + +def find_classes(base_class) -> tuple: + """ + Finds all subclasses of a class inside module. + + :param base_class: Base class to search children. + :return: tuple of subclasses + """ + return inspect.getmembers(sys.modules[base_class.__module__], _reflect_filter(base_class),) + + +def _reflect_filter(base_class): + """ + Reflection is used to load modules dynamically. + + This method is complex. It does some dark magic. + How is it even possible to understand it? + + :param base_class: Target base class + :return: function to filter only subclasses of `base_class` + """ + def class_filter(klass): + return inspect.isclass( + klass, + ) and klass.__module__ == base_class.__module__ and issubclass( + klass, base_class, + ) and klass is not base_class + return class_filter diff --git "a/homeworks/Yurin Andrey/\342\204\2264/todo/runtime.py" "b/homeworks/Yurin Andrey/\342\204\2264/todo/runtime.py" new file mode 100644 index 0000000..c955fa3 --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/todo/runtime.py" @@ -0,0 +1,52 @@ +from todo.commands import BaseCommand +from todo.custom_exceptions import UserExitException +from todo.reflection import find_classes +from todo.storage import Storage + + +def get_routes() -> dict: + """ + This function contains the dictionary of possible commands. + :return: `dict` of possible commands, with the format: `name -> class` + """ + routes = find_classes(BaseCommand) + return { + route().label: route + for _, route in routes + } + + +def perform_command(command: str) -> None: + """ + Performs the command by name. + Stores the result in `Storage()`. + :param command: command name, selected by user. + """ + command = command.lower() + routes = get_routes() + + try: + command_class = routes[command] + except KeyError: + print('Bad command, try again.') + return + + command_inst = command_class() + storage = Storage() + + try: + command_inst.perform(storage) + except UserExitException as ex: + # Handling `exit` command. + print(ex) + raise + + +def parse_user_input(): + """ + Gets the user input. + :return: `str` with the user input. + """ + commands = get_routes().keys() + message = 'Input your command: ({0}): '.format('|'.join(commands)) + return input(message) diff --git "a/homeworks/Yurin Andrey/\342\204\2264/todo/storage.py" "b/homeworks/Yurin Andrey/\342\204\2264/todo/storage.py" new file mode 100644 index 0000000..ef58690 --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2264/todo/storage.py" @@ -0,0 +1,21 @@ +class Storage(object): # storage = Storge() + """ + Singleton storage. + + Read more about singleton design pattern: + https://stackoverflow.com/questions/6760685/creating-a-singleton-in-python + https://en.wikipedia.org/wiki/Singleton_pattern + + It is used to emulate in-memory storage. + It should be replaced with a database in a real application. + """ + + obj = None + items = None + + @classmethod + def __new__(cls, *args): + if cls.obj is None: + cls.obj = object.__new__(cls) + cls.items = [] + return cls.obj From e80a98bfb6e4354bdda705abde87ef69e29faea6 Mon Sep 17 00:00:00 2001 From: Namxobick Date: Tue, 18 Oct 2022 18:24:34 +0300 Subject: [PATCH 07/11] add HW5 --- .../Yurin Andrey/\342\204\2262/Array.py" | 61 --- .../\342\204\2263/\320\241ontract.py" | 29 -- .../\342\204\2264/.idea/.gitignore" | 3 - .../Yurin Andrey/\342\204\2264/.idea/hw5.iml" | 12 - .../inspectionProfiles/profiles_settings.xml" | 6 - .../\342\204\2264/.idea/misc.xml" | 4 - .../\342\204\2264/.idea/modules.xml" | 8 - .../Yurin Andrey/\342\204\2264/README.md" | 27 -- "homeworks/Yurin Andrey/\342\204\2264/hw5.md" | 53 --- .../\342\204\2264/tests/conftest.py" | 5 - .../\342\204\2264/tests/test_done_command.py" | 30 -- .../tests/test_representations.py" | 18 - .../\342\204\2264/tests/test_to_read_item.py" | 43 -- .../tests/test_undone_command.py" | 30 -- .../\342\204\2264/todo/__main__.py" | 28 -- .../\342\204\2264/todo/commands.py" | 132 ------ .../\342\204\2264/todo/custom_exceptions.py" | 2 - .../\342\204\2264/todo/models.py" | 77 ---- .../\342\204\2264/todo/reflection.py" | 31 -- .../\342\204\2264/todo/runtime.py" | 52 --- .../\342\204\2264/todo/storage.py" | 21 - .../my-awesome-script/README.md" | 0 .../my_awesome_script/__init__.py" | 1 + .../my_awesome_script/__main__.py" | 17 + .../my_awesome_script/cmdInput.py" | 17 + .../my_awesome_script/command.py" | 33 ++ .../my-awesome-script/poetry.lock" | 406 ++++++++++++++++++ .../my-awesome-script/pyproject.toml" | 28 ++ .../my-awesome-script/tests/__init__.py" | 0 "practice/\342\204\2261/game_15_puzzle.py" | 89 ---- "practice/\342\204\2262/exceptions.py" | 161 ------- 31 files changed, 502 insertions(+), 922 deletions(-) delete mode 100644 "homeworks/Yurin Andrey/\342\204\2262/Array.py" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2263/\320\241ontract.py" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/.idea/.gitignore" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/.idea/hw5.iml" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/.idea/inspectionProfiles/profiles_settings.xml" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/.idea/misc.xml" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/.idea/modules.xml" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/README.md" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/hw5.md" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/tests/conftest.py" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/tests/test_done_command.py" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/tests/test_representations.py" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/tests/test_to_read_item.py" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/tests/test_undone_command.py" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/todo/__main__.py" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/todo/commands.py" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/todo/custom_exceptions.py" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/todo/models.py" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/todo/reflection.py" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/todo/runtime.py" delete mode 100644 "homeworks/Yurin Andrey/\342\204\2264/todo/storage.py" rename "homeworks/Yurin Andrey/\342\204\2264/todo/__init__.py" => "homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/README.md" (100%) create mode 100644 "homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/__init__.py" create mode 100644 "homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/__main__.py" create mode 100644 "homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/cmdInput.py" create mode 100644 "homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/command.py" create mode 100644 "homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/poetry.lock" create mode 100644 "homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/pyproject.toml" create mode 100644 "homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/tests/__init__.py" delete mode 100644 "practice/\342\204\2261/game_15_puzzle.py" delete mode 100644 "practice/\342\204\2262/exceptions.py" diff --git "a/homeworks/Yurin Andrey/\342\204\2262/Array.py" "b/homeworks/Yurin Andrey/\342\204\2262/Array.py" deleted file mode 100644 index 36061f7..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2262/Array.py" +++ /dev/null @@ -1,61 +0,0 @@ -class Array(object): - - def __init__(self, *args): - self._data = args - - def __str__(self): - return str(self._data) - - def __add__(self, other): - if not isinstance(other, Array): - print(f"Warning, expected type Array (type {other} is {type(other)})") - new_array = Array(*self._data, other) - else: - new_array = Array(*self._data, *other._data) - return new_array - - def __len__(self): - return len(self._data) - - def __iter__(self): - return iter(self._data) - - def __getitem__(self, item: int): - if not isinstance(item, int): - print(f"Error, expected type int (type {item} is {type(item)})") - elif item < -len(self._data) or item >= len(self._data): - print("Error,index out of range") - else: - return self._data[item] - return - - def __setitem__(self, key: int, value): - if not isinstance(key, int): - print(f"Error, expected type int (type {key} is {type(key)})") - return - - if key < 0: - key = len(self) + key - - if key < 0 or key >= len(self): - print("Error, index out of range") - return - - self._data = Array(*self._data[:key], value, *self[key + 1:]) - - def __eq__(self, other): - if not isinstance(other, Array): - return False - if self._data == other._data: - return True - else: - return False - - def append(self, new_element): - self._data = Array(*self._data, new_element) - - def index(self, searched_element): - try: - return self._data.index(searched_element) - except ValueError: - return -1 \ No newline at end of file diff --git "a/homeworks/Yurin Andrey/\342\204\2263/\320\241ontract.py" "b/homeworks/Yurin Andrey/\342\204\2263/\320\241ontract.py" deleted file mode 100644 index ba8137e..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2263/\320\241ontract.py" +++ /dev/null @@ -1,29 +0,0 @@ -class ContractError(Exception): - """We use this error when someone breaks our contract.""" - - -#: Special value, that indicates that validation for this type is not required. -Any = object() - - -def contract(arg_types=None, return_type=None, raises=None): - def decorator(function): - def wrapper(*args, **kwargs): - if arg_types is not None: - for index, arg_type in enumerate(arg_types): - if arg_type is not Any and arg_type != type(args[index]): - raise ContractError('Invalid argument type') from TypeError - - try: - result = function(*args, **kwargs) - except (raises if raises is not None and Any not in raises else Exception) as ex: - raise ex - except Exception: - raise ContractError from Exception - - if return_type is not None and not isinstance(result, return_type): - raise ContractError('Invalid return type') from TypeError - - return result - return wrapper - return decorator diff --git "a/homeworks/Yurin Andrey/\342\204\2264/.idea/.gitignore" "b/homeworks/Yurin Andrey/\342\204\2264/.idea/.gitignore" deleted file mode 100644 index 26d3352..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/.idea/.gitignore" +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git "a/homeworks/Yurin Andrey/\342\204\2264/.idea/hw5.iml" "b/homeworks/Yurin Andrey/\342\204\2264/.idea/hw5.iml" deleted file mode 100644 index 8b8c395..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/.idea/hw5.iml" +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git "a/homeworks/Yurin Andrey/\342\204\2264/.idea/inspectionProfiles/profiles_settings.xml" "b/homeworks/Yurin Andrey/\342\204\2264/.idea/inspectionProfiles/profiles_settings.xml" deleted file mode 100644 index 105ce2d..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/.idea/inspectionProfiles/profiles_settings.xml" +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git "a/homeworks/Yurin Andrey/\342\204\2264/.idea/misc.xml" "b/homeworks/Yurin Andrey/\342\204\2264/.idea/misc.xml" deleted file mode 100644 index d1e22ec..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/.idea/misc.xml" +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git "a/homeworks/Yurin Andrey/\342\204\2264/.idea/modules.xml" "b/homeworks/Yurin Andrey/\342\204\2264/.idea/modules.xml" deleted file mode 100644 index 8f8a12d..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/.idea/modules.xml" +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git "a/homeworks/Yurin Andrey/\342\204\2264/README.md" "b/homeworks/Yurin Andrey/\342\204\2264/README.md" deleted file mode 100644 index 3f2028c..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/README.md" +++ /dev/null @@ -1,27 +0,0 @@ -# TODO CLI App - - -## Legend - -This application is used for our internal needs. -It was developed a long time ago by an unknown developer. - -It works fine. But there are several things we would like to improve. - - -## Running - -To run this app follow these steps: - -1. `cd hw5` -2. `python3 -m todo` ([docs](https://docs.python.org/3/using/cmdline.html#cmdoption-m)) -3. Enjoy! - - -## Testing - -If you don't know what you are doing, skip this step. - -0. Make sure that you are in this folder, near `README.md` -1. `pip install pytest` -2. `pytest` diff --git "a/homeworks/Yurin Andrey/\342\204\2264/hw5.md" "b/homeworks/Yurin Andrey/\342\204\2264/hw5.md" deleted file mode 100644 index c587eff..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/hw5.md" +++ /dev/null @@ -1,53 +0,0 @@ -## Теория - -### Полезности - -- `datetime`: https://docs.python.org/3/library/datetime.html -- `collections`: https://docs.python.org/3/library/collections.html -- `itertools`: https://docs.python.org/3/library/itertools.html -- `functools`: https://docs.python.org/3/library/functools.html -- `os`: https://docs.python.org/3/library/os.html -- `sys`: https://docs.python.org/3/library/sys.html -- `re`: https://docs.python.org/3/library/re.html -- `argparse`: https://docs.python.org/3/library/argparse.html - -### Imports - -- modules: https://docs.python.org/3/tutorial/modules.html -- Решаем проблему цикличных импортов: https://stackoverflow.com/questions/5748946/pythonic-way-to-resolve-circular-import-statements -- Откуда можно импортировать: https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH и https://stackoverflow.com/questions/19917492/how-to-use-pythonpath и http://www.johnny-lin.com/cdat_tips/tips_pylang/path.html -- Relative imports vs absolute: https://stackoverflow.com/questions/28400690/python3-correct-way-to-import-relative-or-absolute и http://pulkitgoyal.com/absolute-relative-imports - - -## Практика - -Данное задание будет построено в формате ролевой игры, чтобы показать реальность подобных задач, ситуаций и подходов. - -### Легенда - -В компании, куда вы пришли работать, есть для вас первая задача. -Все сотрудники пользуются внутренней системой ведения учета задач. -Но вот проблема, в ней есть некоторые моменты, которые нужно улучшить. - -Никто точно не помнит, кто написал программу и когда. -К сожаению, спросить, как она работает, некого. -Более того, сами сотрудники не могут посмотреть, что там в коде, потому что они не программисты. -Но они слышали, что там есть сложные части, а есть какие-то простые. -Вроде бы код написан нормально, но документации к нему не сохранилось. -Только инструкция по запуску. - -### Технические требования - -Менеджер подготовил для вас техническое задание, какие фичи он хотел бы увидеть: - -- Необходимо добавить всем задачам визуальный статус готовности. Он должен выглядеть так: `+ ToDo: ...`, где `+` или `-` - статус готовности (`+` - выполнено, `-` - невыполнено), `ToDo` - название типа задачи и `...` - атрибуты задачи -- Необходимо реализовать команду `done`, которая бы отмечала задачи выполнеными (все задачи по-умолчанию невыполнены) -- Необходимо реализовать команду `undone`, которая бы отмечала задачи невыполнеными -- Необходимо добавить новый тип задачи: `ToReadItem`, у которой было бы два поля: `heading` и `url`. Следовательно: что прочитать и где - -### Проверка результатов (для продвинуты, необязательно) - -Старый разработчик был большим фанатом [TTD](https://en.wikipedia.org/wiki/Test-driven_development), и он успел перед своим уходом написать тесты, как должы работать все те новые требования. -Но закончить проект не успел. - -В качестве проверки будем использовать их. diff --git "a/homeworks/Yurin Andrey/\342\204\2264/tests/conftest.py" "b/homeworks/Yurin Andrey/\342\204\2264/tests/conftest.py" deleted file mode 100644 index e846edf..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/tests/conftest.py" +++ /dev/null @@ -1,5 +0,0 @@ -import os -import sys - -BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) -sys.path.append(BASE_DIR) diff --git "a/homeworks/Yurin Andrey/\342\204\2264/tests/test_done_command.py" "b/homeworks/Yurin Andrey/\342\204\2264/tests/test_done_command.py" deleted file mode 100644 index 83a6927..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/tests/test_done_command.py" +++ /dev/null @@ -1,30 +0,0 @@ -def test_class_exists(): - from todo.commands import DoneCommand - - assert isinstance(DoneCommand, type) - - -def test_command_label_in_list(): - from todo.commands import DoneCommand - from todo.runtime import get_routes - - assert DoneCommand().label in get_routes() - - -def test_command_execution(monkeypatch): - from todo.commands import DoneCommand - from todo.runtime import perform_command - from todo.models import Storage, ToDoItem - - monkeypatch.setitem(__builtins__, 'input', lambda _: 0) - - item = ToDoItem('test') - item.done = False - - s = Storage() - s.items.clear() - s.items.append(item) - - perform_command(DoneCommand().label) - - assert item.done is True diff --git "a/homeworks/Yurin Andrey/\342\204\2264/tests/test_representations.py" "b/homeworks/Yurin Andrey/\342\204\2264/tests/test_representations.py" deleted file mode 100644 index 07043c8..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/tests/test_representations.py" +++ /dev/null @@ -1,18 +0,0 @@ -def test_to_buy_item(): - from todo.models import ToBuyItem - - item = ToBuyItem('header', 'price') - assert str(item).startswith('- ToBuy: ') - - item.done = True - assert str(item).startswith('+ ToBuy: ') - - -def test_to_do_item(): - from todo.models import ToDoItem - - item = ToDoItem('subject') - assert str(item).startswith('- ToDo: ') - - item.done = True - assert str(item).startswith('+ ToDo: ') diff --git "a/homeworks/Yurin Andrey/\342\204\2264/tests/test_to_read_item.py" "b/homeworks/Yurin Andrey/\342\204\2264/tests/test_to_read_item.py" deleted file mode 100644 index b1ea1e3..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/tests/test_to_read_item.py" +++ /dev/null @@ -1,43 +0,0 @@ -def test_class_exists(): - from todo.models import ToReadItem - - assert isinstance(ToReadItem, type) - - -def test_there_are_attributes(): - from todo.models import ToReadItem - - heading = 'test' - url = 'http://ya.ru' - item = ToReadItem(heading, url) - - assert item.heading == heading - assert item.url == url - assert item.done is False - - -def test_fabric_method(monkeypatch): - from todo.models import ToReadItem - - value = 'some input' - - monkeypatch.setitem(__builtins__, 'input', lambda _: value) - item = ToReadItem.construct() - - assert item.heading == value - assert item.url == value - assert item.done is False - - -def test_representation(): - from todo.models import ToReadItem - - heading = 'test' - url = 'http://ya.ru' - item = ToReadItem(heading, url) - - assert str(item) == '- ToRead: {} {}'.format(heading, url) - - item.done = True - - assert str(item) == '+ ToRead: {} {}'.format(heading, url) diff --git "a/homeworks/Yurin Andrey/\342\204\2264/tests/test_undone_command.py" "b/homeworks/Yurin Andrey/\342\204\2264/tests/test_undone_command.py" deleted file mode 100644 index b25bedf..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/tests/test_undone_command.py" +++ /dev/null @@ -1,30 +0,0 @@ -def test_class_exists(): - from todo.commands import UndoneCommand - - assert isinstance(UndoneCommand, type) - - -def test_command_label_in_list(): - from todo.commands import UndoneCommand - from todo.runtime import get_routes - - assert UndoneCommand().label in get_routes() - - -def test_command_execution(monkeypatch): - from todo.commands import UndoneCommand - from todo.runtime import perform_command - from todo.models import Storage, ToDoItem - - monkeypatch.setitem(__builtins__, 'input', lambda _: 0) - - item = ToDoItem('test') - item.done = True - - s = Storage() - s.items.clear() - s.items.append(item) - - perform_command(UndoneCommand().label) - - assert item.done is False diff --git "a/homeworks/Yurin Andrey/\342\204\2264/todo/__main__.py" "b/homeworks/Yurin Andrey/\342\204\2264/todo/__main__.py" deleted file mode 100644 index 55f9f2c..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/todo/__main__.py" +++ /dev/null @@ -1,28 +0,0 @@ -""" -Main file. Contains program execution logic. -""" - -from todo.custom_exceptions import UserExitException -from todo.runtime import parse_user_input, perform_command - - -def main(): - """ - Main method, works infinitely until user runs `exit` command. - Or hits `Ctrl+C` in the console. - """ - while True: - try: - perform_command(parse_user_input()) - except UserExitException: - break - except Exception as ex: - print('You have done something wrong!', ex) - - -if __name__ == '__main__': - try: - main() - except KeyboardInterrupt: - print() - print('Shutting down, bye!') diff --git "a/homeworks/Yurin Andrey/\342\204\2264/todo/commands.py" "b/homeworks/Yurin Andrey/\342\204\2264/todo/commands.py" deleted file mode 100644 index dea1063..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/todo/commands.py" +++ /dev/null @@ -1,132 +0,0 @@ - -from todo.custom_exceptions import UserExitException -from todo.models import BaseItem -from todo.reflection import find_classes - - -class BaseCommand(object): - label: str - - def perform(self, store): - raise NotImplementedError() - - -class ListCommand(BaseCommand): - label = 'list' - - def perform(self, store): - if len(store.items) == 0: - print('There are no items in the storage.') - return - - for index, obj in enumerate(store.items): - print('{0}: {1}'.format(index, str(obj))) - - -class NewCommand(BaseCommand): - label = 'new' - - def perform(self, store): - classes = self._load_item_classes() - - print('Select item type:') - for index, name in enumerate(classes.keys()): - print('{0}: {1}'.format(index, name)) - - selection = None - selected_key = None - - while True: - try: - selected_key = self._select_item(classes) - except ValueError: - print('Bad input, try again.') - except IndexError: - print('Wrong index, try again.') - else: - break - - selected_class = classes[selected_key] - print('Selected: {0}'.format(selected_class.__name__)) - print() - - new_object = selected_class.construct() - - store.items.append(new_object) - - print('Added {0}'.format(str(new_object))) - print() - return new_object - - def _load_item_classes(self) -> dict: - # Dynamic load: - return dict(find_classes(BaseItem)) - - def _select_item(self, classes): - selection = int(input('Input number: ')) - if selection < 0: - raise IndexError('Index needs to be >0') - return list(classes.keys())[selection] - - -class BaseMarkCommand(object): - - def perform(self, store): - counts = 0 - - for obj in store.items: - if str(obj)[0] == self.char: - counts += 1 - - if counts == 0: - print('There are no {0}items in the storage.'.format(self.char)) - return - - for index, obj in enumerate(store.items): - if str(obj)[0] == self.char: - print('{0}: {1}'.format(index, str(obj))) - - selected_item = self.select_item(store) - - print('{0} has been {1}'.format(selected_item, self.operation)) - print() - - operation = getattr(selected_item, str(self.operation), None) - assert operation is not None - operation() - - def select_item(self, store): - while True: - try: - selection = int(input('Input number of item: ')) - if selection < 0 or str(store.items[selection])[0] != self.char: - raise IndexError('MarkCommand -> select_item') - except ValueError: - print('Bad input, try again.') - except IndexError: - print('Wrong index, try again.') - else: - break - - if store.items is None: - print('There are no items in the storage.') - return store.items[selection] - - -class DoneCommand(BaseMarkCommand, BaseCommand): - label = 'done' - operation = 'mark_done' - char = '-' - - -class UndoneCommand(BaseMarkCommand, BaseCommand): - label = 'undone' - operation = 'mark_undone' - char = '+' - - -class ExitCommand(BaseCommand): - label = 'exit' - - def perform(self, _store): - raise UserExitException('See you next time!') diff --git "a/homeworks/Yurin Andrey/\342\204\2264/todo/custom_exceptions.py" "b/homeworks/Yurin Andrey/\342\204\2264/todo/custom_exceptions.py" deleted file mode 100644 index 5e8963c..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/todo/custom_exceptions.py" +++ /dev/null @@ -1,2 +0,0 @@ -class UserExitException(Exception): - """We use this exception when user wants to exit.""" diff --git "a/homeworks/Yurin Andrey/\342\204\2264/todo/models.py" "b/homeworks/Yurin Andrey/\342\204\2264/todo/models.py" deleted file mode 100644 index aaaa03c..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/todo/models.py" +++ /dev/null @@ -1,77 +0,0 @@ -class BaseItem(object): - def __init__(self, heading): - self.heading = heading - self.done = False # TODO: make sure we can use it... - - def __repr__(self): - return self.__class__.__name__ - - def __str__(self): - return '{0} {1}'.format( - '+' if self.done else '-', - self.__repr__(), - ) - - def set_done(self, status: bool): - self.done = status - - def mark_done(self): - self.done = True - - def mark_undone(self): - self.done = False - - @classmethod - def construct(cls): - raise NotImplementedError() - - -class ToDoItem(BaseItem): - def __str__(self): - return '{0}: {1}'.format( - super().__str__(), - self.heading, - ) - - @classmethod - def construct(cls): - heading = input('Input heading: ') - return cls(heading) - - -class ToBuyItem(BaseItem): - def __init__(self, heading, price): - super().__init__(heading) - self.price = price - - def __str__(self): - return '{0}: {1} for {2}'.format( - super().__str__(), - self.heading, - self.price, - ) - - @classmethod - def construct(cls): - heading = input('Input heading: ') - price = input('Input price: ') - return cls(heading, price) - - -class ToReadItem(BaseItem): - def __init__(self, heading, url): - super().__init__(heading) - self.url = url - - def __str__(self): - return '{0}: {1} {2}'.format( - super().__str__(), - self.heading, - self.url, - ) - - @classmethod - def construct(cls): - heading = input('Input heading: ') - url = input('Input url: ') - return cls(heading, url) diff --git "a/homeworks/Yurin Andrey/\342\204\2264/todo/reflection.py" "b/homeworks/Yurin Andrey/\342\204\2264/todo/reflection.py" deleted file mode 100644 index e16c6f6..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/todo/reflection.py" +++ /dev/null @@ -1,31 +0,0 @@ -import inspect -import sys - - -def find_classes(base_class) -> tuple: - """ - Finds all subclasses of a class inside module. - - :param base_class: Base class to search children. - :return: tuple of subclasses - """ - return inspect.getmembers(sys.modules[base_class.__module__], _reflect_filter(base_class),) - - -def _reflect_filter(base_class): - """ - Reflection is used to load modules dynamically. - - This method is complex. It does some dark magic. - How is it even possible to understand it? - - :param base_class: Target base class - :return: function to filter only subclasses of `base_class` - """ - def class_filter(klass): - return inspect.isclass( - klass, - ) and klass.__module__ == base_class.__module__ and issubclass( - klass, base_class, - ) and klass is not base_class - return class_filter diff --git "a/homeworks/Yurin Andrey/\342\204\2264/todo/runtime.py" "b/homeworks/Yurin Andrey/\342\204\2264/todo/runtime.py" deleted file mode 100644 index c955fa3..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/todo/runtime.py" +++ /dev/null @@ -1,52 +0,0 @@ -from todo.commands import BaseCommand -from todo.custom_exceptions import UserExitException -from todo.reflection import find_classes -from todo.storage import Storage - - -def get_routes() -> dict: - """ - This function contains the dictionary of possible commands. - :return: `dict` of possible commands, with the format: `name -> class` - """ - routes = find_classes(BaseCommand) - return { - route().label: route - for _, route in routes - } - - -def perform_command(command: str) -> None: - """ - Performs the command by name. - Stores the result in `Storage()`. - :param command: command name, selected by user. - """ - command = command.lower() - routes = get_routes() - - try: - command_class = routes[command] - except KeyError: - print('Bad command, try again.') - return - - command_inst = command_class() - storage = Storage() - - try: - command_inst.perform(storage) - except UserExitException as ex: - # Handling `exit` command. - print(ex) - raise - - -def parse_user_input(): - """ - Gets the user input. - :return: `str` with the user input. - """ - commands = get_routes().keys() - message = 'Input your command: ({0}): '.format('|'.join(commands)) - return input(message) diff --git "a/homeworks/Yurin Andrey/\342\204\2264/todo/storage.py" "b/homeworks/Yurin Andrey/\342\204\2264/todo/storage.py" deleted file mode 100644 index ef58690..0000000 --- "a/homeworks/Yurin Andrey/\342\204\2264/todo/storage.py" +++ /dev/null @@ -1,21 +0,0 @@ -class Storage(object): # storage = Storge() - """ - Singleton storage. - - Read more about singleton design pattern: - https://stackoverflow.com/questions/6760685/creating-a-singleton-in-python - https://en.wikipedia.org/wiki/Singleton_pattern - - It is used to emulate in-memory storage. - It should be replaced with a database in a real application. - """ - - obj = None - items = None - - @classmethod - def __new__(cls, *args): - if cls.obj is None: - cls.obj = object.__new__(cls) - cls.items = [] - return cls.obj diff --git "a/homeworks/Yurin Andrey/\342\204\2264/todo/__init__.py" "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/README.md" similarity index 100% rename from "homeworks/Yurin Andrey/\342\204\2264/todo/__init__.py" rename to "homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/README.md" diff --git "a/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/__init__.py" "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/__init__.py" new file mode 100644 index 0000000..8b13789 --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/__init__.py" @@ -0,0 +1 @@ + diff --git "a/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/__main__.py" "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/__main__.py" new file mode 100644 index 0000000..e12a4d3 --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/__main__.py" @@ -0,0 +1,17 @@ +from .cmdInput import cmd_input + + +def main(): + try: + cmd_input() + except Exception as ex: + print('You have done something wrong!', ex) + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + print() + print('Shutting down, bye!') + diff --git "a/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/cmdInput.py" "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/cmdInput.py" new file mode 100644 index 0000000..f159705 --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/cmdInput.py" @@ -0,0 +1,17 @@ +import argparse +from .command import command + + +def cmd_input(): + parser = argparse.ArgumentParser() + parser.add_argument("operation", help="highlight 'any text' or cowsay 'any text' or time 'Region/City'" + "(example: Europe/Moscow)", + choices=["highlight", "cowsay", "time"]) + parser.add_argument("text") + args = parser.parse_args() + + try: + command(args.operation, args.text) + except Exception as ex: + command("help") + raise ex diff --git "a/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/command.py" "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/command.py" new file mode 100644 index 0000000..65e37ad --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/command.py" @@ -0,0 +1,33 @@ +from pygments import highlight +from pygments.formatters import TerminalFormatter +from pygments.lexers import PythonLexer +from cowpy import cow +from datetime import datetime +import pytz + + +def command(operation: str, text: str = None) -> None: + functions[operation](text) if operation != "help" else functions[operation]() + + +def highlight_(code: str) -> None: + print(highlight(code, PythonLexer(), TerminalFormatter())) + + +def cowsay_(text: str) -> None: + print(cow.milk_random_cow(text)) + + +def time_(timezone: str) -> None: + local_date = datetime.now(pytz.timezone(timezone)) + print("{0} {1}".format(local_date.date(), local_date.time())) + + +def help_() -> None: + print("EXAMPLE COMMAND:") + print(" highlight 'any text'") + print(" cowsay 'any text'") + print(" time 'Region/City' (example: Europe/Moscow)") + + +functions = {'highlight': highlight_, "cowsay": cowsay_, "time": time_, "help": help_} diff --git "a/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/poetry.lock" "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/poetry.lock" new file mode 100644 index 0000000..7edca04 --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/poetry.lock" @@ -0,0 +1,406 @@ +[[package]] +name = "argparse" +version = "1.4.0" +description = "Python command-line parsing library" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "certifi" +version = "2022.9.24" +description = "Python package for providing Mozilla's CA Bundle." +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "charset-normalizer" +version = "2.1.1" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "dev" +optional = false +python-versions = ">=3.6.0" + +[package.extras] +unicode-backport = ["unicodedata2"] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.5" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "cowpy" +version = "1.1.5" +description = "" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "cowsay" +version = "5.0" +description = "The famous cowsay for GNU/Linux is now available for python" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "datetime" +version = "4.7" +description = "This package provides a DateTime data type, as known from Zope. Unless you need to communicate with Zope APIs, you're probably better off using Python's built-in datetime module." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pytz = "*" +"zope.interface" = "*" + +[[package]] +name = "dparse" +version = "0.6.2" +description = "A parser for Python dependency files" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +packaging = "*" +toml = "*" + +[package.extras] +conda = ["pyyaml"] +pipenv = ["pipenv"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pygments" +version = "2.13.0" +description = "Pygments is a syntax highlighting package written in Python." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "dev" +optional = false +python-versions = ">=3.6.8" + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pytz" +version = "2022.5" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "requests" +version = "2.28.1" +description = "Python HTTP for Humans." +category = "dev" +optional = false +python-versions = ">=3.7, <4" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<3" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "ruamel-yaml" +version = "0.17.21" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +category = "dev" +optional = false +python-versions = ">=3" + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} + +[package.extras] +docs = ["ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.6" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "safety" +version = "2.3.1" +description = "Checks installed dependencies for known vulnerabilities and licenses." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +Click = ">=8.0.2" +dparse = ">=0.6.2" +packaging = ">=21.0" +requests = "*" +"ruamel.yaml" = ">=0.17.21" +setuptools = ">=19.3" + +[package.extras] +github = ["jinja2 (>=3.1.0)", "pygithub (>=1.43.3)"] +gitlab = ["python-gitlab (>=1.3.0)"] + +[[package]] +name = "setuptools" +version = "65.5.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "urllib3" +version = "1.26.12" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "zope-interface" +version = "5.5.0" +description = "Interfaces for Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +setuptools = "*" + +[package.extras] +docs = ["Sphinx", "repoze.sphinx.autointerface"] +test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] +testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.10" +content-hash = "d036dc1a7cac361bea337634151b60e5959201bebcd5f35561850a3df446cc67" + +[metadata.files] +argparse = [ + {file = "argparse-1.4.0-py2.py3-none-any.whl", hash = "sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314"}, + {file = "argparse-1.4.0.tar.gz", hash = "sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4"}, +] +certifi = [ + {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"}, + {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, + {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, +] +click = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] +colorama = [ + {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, + {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, +] +cowpy = [ + {file = "cowpy-1.1.5-py3-none-any.whl", hash = "sha256:de5ae7646dd30b4936013666c6bd019af9cf411cc3b377c8538cfd8414262921"}, + {file = "cowpy-1.1.5.tar.gz", hash = "sha256:089172db1d88c30a2e1b741b18945ee84170bd943a3ca71948e4ae3a3255e554"}, +] +cowsay = [ + {file = "cowsay-5.0.tar.gz", hash = "sha256:c00e02444f5bc7332826686bd44d963caabbaba9a804a63153822edce62bbbf3"}, +] +datetime = [ + {file = "DateTime-4.7-py2.py3-none-any.whl", hash = "sha256:b8d2d605cfb5fed0da86f9ad64d0973c6f84b21939d49265e135811b33ee8113"}, + {file = "DateTime-4.7.tar.gz", hash = "sha256:7ff7c4a857f08b73db17a85fc54f102d065ad16e7db0133e699c5f1b37e41478"}, +] +dparse = [ + {file = "dparse-0.6.2-py3-none-any.whl", hash = "sha256:8097076f1dd26c377f30d4745e6ec18fef42f3bf493933b842ac5bafad8c345f"}, + {file = "dparse-0.6.2.tar.gz", hash = "sha256:d45255bda21f998bc7ddf2afd5e62505ba6134756ba2d42a84c56b0826614dfe"}, +] +idna = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +pygments = [ + {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, + {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, +] +pyparsing = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] +pytz = [ + {file = "pytz-2022.5-py2.py3-none-any.whl", hash = "sha256:335ab46900b1465e714b4fda4963d87363264eb662aab5e65da039c25f1f5b22"}, + {file = "pytz-2022.5.tar.gz", hash = "sha256:c4d88f472f54d615e9cd582a5004d1e5f624854a6a27a6211591c251f22a6914"}, +] +requests = [ + {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, + {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, +] +ruamel-yaml = [ + {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, + {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, +] +ruamel-yaml-clib = [ + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:066f886bc90cc2ce44df8b5f7acfc6a7e2b2e672713f027136464492b0c34d7c"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win32.whl", hash = "sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win32.whl", hash = "sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win_amd64.whl", hash = "sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d3c620a54748a3d4cf0bcfe623e388407c8e85a4b06b8188e126302bcab93ea8"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win32.whl", hash = "sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win_amd64.whl", hash = "sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:210c8fcfeff90514b7133010bf14e3bad652c8efde6b20e00c43854bf94fa5a6"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win32.whl", hash = "sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win_amd64.whl", hash = "sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:61bc5e5ca632d95925907c569daa559ea194a4d16084ba86084be98ab1cec1c6"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win32.whl", hash = "sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win_amd64.whl", hash = "sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:1b4139a6ffbca8ef60fdaf9b33dec05143ba746a6f0ae0f9d11d38239211d335"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win32.whl", hash = "sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win_amd64.whl", hash = "sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7"}, + {file = "ruamel.yaml.clib-0.2.6.tar.gz", hash = "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd"}, +] +safety = [ + {file = "safety-2.3.1-py3-none-any.whl", hash = "sha256:8f098d12b607db2756886280e85c28ece8db1bba4f45fc5f981f4663217bd619"}, + {file = "safety-2.3.1.tar.gz", hash = "sha256:6e6fcb7d4e8321098cf289f59b65051cafd3467f089c6e57c9f894ae32c23b71"}, +] +setuptools = [ + {file = "setuptools-65.5.0-py3-none-any.whl", hash = "sha256:f62ea9da9ed6289bfe868cd6845968a2c854d1427f8548d52cae02a42b4f0356"}, + {file = "setuptools-65.5.0.tar.gz", hash = "sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] +urllib3 = [ + {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, + {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, +] +zope-interface = [ + {file = "zope.interface-5.5.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:2cb3003941f5f4fa577479ac6d5db2b940acb600096dd9ea9bf07007f5cab46f"}, + {file = "zope.interface-5.5.0-cp27-cp27m-win32.whl", hash = "sha256:8c791f4c203ccdbcda588ea4c8a6e4353e10435ea48ddd3d8734a26fe9714cba"}, + {file = "zope.interface-5.5.0-cp27-cp27m-win_amd64.whl", hash = "sha256:3eedf3d04179774d750e8bb4463e6da350956a50ed44d7b86098e452d7ec385e"}, + {file = "zope.interface-5.5.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:58a66c2020a347973168a4a9d64317bac52f9fdfd3e6b80b252be30da881a64e"}, + {file = "zope.interface-5.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da7912ae76e1df6a1fb841b619110b1be4c86dfb36699d7fd2f177105cdea885"}, + {file = "zope.interface-5.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:423c074e404f13e6fa07f4454f47fdbb38d358be22945bc812b94289d9142374"}, + {file = "zope.interface-5.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7bdcec93f152e0e1942102537eed7b166d6661ae57835b20a52a2a3d6a3e1bf3"}, + {file = "zope.interface-5.5.0-cp310-cp310-win32.whl", hash = "sha256:03f5ae315db0d0de668125d983e2a819a554f3fdb2d53b7e934e3eb3c3c7375d"}, + {file = "zope.interface-5.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:8b9f153208d74ccfa25449a0c6cb756ab792ce0dc99d9d771d935f039b38740c"}, + {file = "zope.interface-5.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeac590cce44e68ee8ad0b8ecf4d7bf15801f102d564ca1b0eb1f12f584ee656"}, + {file = "zope.interface-5.5.0-cp35-cp35m-win32.whl", hash = "sha256:7d9ec1e6694af39b687045712a8ad14ddcb568670d5eb1b66b48b98b9312afba"}, + {file = "zope.interface-5.5.0-cp35-cp35m-win_amd64.whl", hash = "sha256:d18fb0f6c8169d26044128a2e7d3c39377a8a151c564e87b875d379dbafd3930"}, + {file = "zope.interface-5.5.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:0eb2b3e84f48dd9cfc8621c80fba905d7e228615c67f76c7df7c716065669bb6"}, + {file = "zope.interface-5.5.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df6593e150d13cfcce69b0aec5df7bc248cb91e4258a7374c129bb6d56b4e5ca"}, + {file = "zope.interface-5.5.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9dc4493aa3d87591e3d2bf1453e25b98038c839ca8e499df3d7106631b66fe83"}, + {file = "zope.interface-5.5.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5c6023ae7defd052cf76986ce77922177b0c2f3913bea31b5b28fbdf6cb7099e"}, + {file = "zope.interface-5.5.0-cp36-cp36m-win32.whl", hash = "sha256:a69c28d85bb7cf557751a5214cb3f657b2b035c8c96d71080c1253b75b79b69b"}, + {file = "zope.interface-5.5.0-cp36-cp36m-win_amd64.whl", hash = "sha256:85dd6dd9aaae7a176948d8bb62e20e2968588fd787c29c5d0d964ab475168d3d"}, + {file = "zope.interface-5.5.0-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:970661ece2029915b8f7f70892e88404340fbdefd64728380cad41c8dce14ff4"}, + {file = "zope.interface-5.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e3495bb0cdcea212154e558082c256f11b18031f05193ae2fb85d048848db14"}, + {file = "zope.interface-5.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3f68404edb1a4fb6aa8a94675521ca26c83ebbdbb90e894f749ae0dc4ca98418"}, + {file = "zope.interface-5.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:740f3c1b44380658777669bcc42f650f5348e53797f2cee0d93dc9b0f9d7cc69"}, + {file = "zope.interface-5.5.0-cp37-cp37m-win32.whl", hash = "sha256:006f8dd81fae28027fc28ada214855166712bf4f0bfbc5a8788f9b70982b9437"}, + {file = "zope.interface-5.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:43490ad65d4c64e45a30e51a2beb7a6b63e1ff395302ad22392224eb618476d6"}, + {file = "zope.interface-5.5.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:f70726b60009433111fe9928f5d89cbb18962411d33c45fb19eb81b9bbd26fcd"}, + {file = "zope.interface-5.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa614d049667bed1c737435c609c0956c5dc0dbafdc1145ee7935e4658582cb"}, + {file = "zope.interface-5.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:58a975f89e4584d0223ab813c5ba4787064c68feef4b30d600f5e01de90ae9ce"}, + {file = "zope.interface-5.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:37ec9ade9902f412cc7e7a32d71f79dec3035bad9bd0170226252eed88763c48"}, + {file = "zope.interface-5.5.0-cp38-cp38-win32.whl", hash = "sha256:be11fce0e6af6c0e8d93c10ef17b25aa7c4acb7ec644bff2596c0d639c49e20f"}, + {file = "zope.interface-5.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:cbbf83914b9a883ab324f728de869f4e406e0cbcd92df7e0a88decf6f9ab7d5a"}, + {file = "zope.interface-5.5.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:26c1456520fdcafecc5765bec4783eeafd2e893eabc636908f50ee31fe5c738c"}, + {file = "zope.interface-5.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47ff078734a1030c48103422a99e71a7662d20258c00306546441adf689416f7"}, + {file = "zope.interface-5.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:687cab7f9ae18d2c146f315d0ca81e5ffe89a139b88277afa70d52f632515854"}, + {file = "zope.interface-5.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d80f6236b57a95eb19d5e47eb68d0296119e1eff6deaa2971ab8abe3af918420"}, + {file = "zope.interface-5.5.0-cp39-cp39-win32.whl", hash = "sha256:9cdc4e898d3b1547d018829fd4a9f403e52e51bba24be0fbfa37f3174e1ef797"}, + {file = "zope.interface-5.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:6566b3d2657e7609cd8751bcb1eab1202b1692a7af223035a5887d64bb3a2f3b"}, + {file = "zope.interface-5.5.0.tar.gz", hash = "sha256:700ebf9662cf8df70e2f0cb4988e078c53f65ee3eefd5c9d80cf988c4175c8e3"}, +] diff --git "a/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/pyproject.toml" "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/pyproject.toml" new file mode 100644 index 0000000..b090d3c --- /dev/null +++ "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/pyproject.toml" @@ -0,0 +1,28 @@ +[tool.poetry] +name = "my-awesome-script" +version = "0.1.0" +description = "" +authors = ["Your Namxobick "] +readme = "README.md" +packages = [{include = "my_awesome_script"}] + +[tool.poetry.dependencies] +python = "^3.10" +pygments = "^2.13.0" +argparse = "^1.4.0" +cowpy = "^1.1.5" +cowsay = "^5.0" +pytz = "^2022.4" +datetime = "^4.7" + + +[tool.poetry.group.dev.dependencies] +safety = "^2.3.1" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry.scripts] +my_awesome_script = "my_awesome_script.__main__:main" + diff --git "a/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/tests/__init__.py" "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/tests/__init__.py" new file mode 100644 index 0000000..e69de29 diff --git "a/practice/\342\204\2261/game_15_puzzle.py" "b/practice/\342\204\2261/game_15_puzzle.py" deleted file mode 100644 index 320d9dd..0000000 --- "a/practice/\342\204\2261/game_15_puzzle.py" +++ /dev/null @@ -1,89 +0,0 @@ -import random - -EMPTY_MARK = 'x' - -MOVES = { - 'w': -4, - 's': 4, - 'a': -1, - 'd': 1, -} - - -def shuffle_field(gameMap): - mixingDepth = random.randint(50, 100) - for i in range(mixingDepth): - key = random.choice(["w", "s", "d", "a"]) - perform_move(gameMap, key) - return gameMap - pass - - -def print_field(gameMap): - for i in range(len(gameMap)): - if i % 4 == 0 and i != 0: - print() - print(gameMap[i], end=' ') - pass - - -def is_game_finished(gameMap): - if gameMap[:len(gameMap) - 1] == [(i + 1) for i in range(15)]: - return True - else: - return False - pass - - -def perform_move(gameMap, key): - indexEmptyTile = gameMap.index('x') - - if key == 'w': - if indexEmptyTile in [12, 13, 14, 15]: - return None - else: - gameMap[indexEmptyTile], gameMap[indexEmptyTile + 4] = gameMap[indexEmptyTile + 4], gameMap[indexEmptyTile] - - elif key == 's': - if indexEmptyTile in [0, 1, 2, 3]: - return None - else: - gameMap[indexEmptyTile], gameMap[indexEmptyTile - 4] = gameMap[indexEmptyTile - 4], gameMap[indexEmptyTile] - - elif key == 'a': - if indexEmptyTile in [3, 7, 11, 15]: - return None - else: - gameMap[indexEmptyTile], gameMap[indexEmptyTile + 1] = gameMap[indexEmptyTile + 1], gameMap[indexEmptyTile] - else: - if indexEmptyTile in [0, 4, 8, 12]: - return None - else: - gameMap[indexEmptyTile], gameMap[indexEmptyTile - 1] = gameMap[indexEmptyTile - 1], gameMap[indexEmptyTile] - pass - - -def handle_user_input(): - print() - key = input() - return key - - pass - - -def main(): - GameMap = shuffle_field([(i + 1) for i in range(15)] + [EMPTY_MARK]) - - while not(is_game_finished(GameMap)): - print_field(GameMap) - perform_move(GameMap, handle_user_input()) - print("WIN") - - pass - - -if __name__ == '__main__': - - main() - - diff --git "a/practice/\342\204\2262/exceptions.py" "b/practice/\342\204\2262/exceptions.py" deleted file mode 100644 index 2e4d3bb..0000000 --- "a/practice/\342\204\2262/exceptions.py" +++ /dev/null @@ -1,161 +0,0 @@ -import math - - -def call_system_exit(): - try: - exit() - except SystemExit: - print('SystemExit') - - -def call_generator_exit(): - def generate(): - values = [0, 1] - for value in values: - try: - yield value - - except GeneratorExit: - print('GeneratorExit') - raise - - my_generator = generate() - next(my_generator) - my_generator.close() - - -def call_runtime_error(): - def generate(): - values = [0, 1] - for value in values: - try: - yield value - - except RuntimeError: - print('RuntimeError') - - my_generator = generate() - next(my_generator) - my_generator.close() - - -def call_stop_iteration(): - def generate(): - for value in [0]: - yield value - return - - my_generator = generate() - for i in range(2): - try: - next(my_generator) - except StopIteration: - print("StopIteration") - - -def call_overflow_error(): - results = [] - for i in range(1000): - try: - results.append(math.exp(i)) - except OverflowError: - print("OverflowError") - return - - -def call_zero_division_error(): - try: - x = 1 / 0 - except ZeroDivisionError: - print("ZeroDivisionError") - - -def call_assertion_error(): - try: - assert 0 == 1 - except AssertionError: - print("AssertionError") - - -def call_attribute_error(): - my_str = 'my' - try: - my_str.noattr - except AttributeError: - print("AttributeError") - - -def call_value_error(): - try: - chr(-100) - except ValueError: - print("ValueError") - - -def call_index_error(): - a = [] - try: - a[1] - except IndexError: - print("IndexError") - - -def call_key_error(): - my_dict = {0: 0} - try: - my_dict[1] - except KeyError: - print("KeyError") - - -def call_file_not_found_error(): - try: - f = open('') - except FileNotFoundError: - print('FileNotFoundError') - - -def call_memory_error(): - try: - x = list(range(10000000000)) - except MemoryError: - print('MemoryError') - - -def call_type_error(): - try: - x = 'a' + 5 - except TypeError: - print('TypeError') - - -def call_recursion_error(): - try: - call_recursion_error() - except RecursionError: - print('RecursionError') - - -def call_unicode_decoder_error(): - try: - b'\xd0'.decode('utf-8') - except UnicodeDecodeError: - print('UnicodeDecodeError') - - -call_system_exit() -call_generator_exit() -call_runtime_error() -call_stop_iteration() -call_overflow_error() -call_zero_division_error() -call_assertion_error() -call_attribute_error() -call_value_error() -call_index_error() -call_key_error() -call_file_not_found_error() -call_memory_error() -call_type_error() -call_recursion_error() -call_unicode_decoder_error() From 7fab8cf22fb27a726e1439d5ce6626da3bebabc8 Mon Sep 17 00:00:00 2001 From: Namxobick <100288192+Namxobick@users.noreply.github.com> Date: Wed, 19 Oct 2022 19:47:09 +0300 Subject: [PATCH 08/11] Update command.py --- .../my-awesome-script/my_awesome_script/command.py" | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git "a/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/command.py" "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/command.py" index 65e37ad..7d0ec6c 100644 --- "a/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/command.py" +++ "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/command.py" @@ -7,7 +7,7 @@ def command(operation: str, text: str = None) -> None: - functions[operation](text) if operation != "help" else functions[operation]() + functions[operation]() if operation == 'help' else functions[operation](text) def highlight_(code: str) -> None: @@ -30,4 +30,4 @@ def help_() -> None: print(" time 'Region/City' (example: Europe/Moscow)") -functions = {'highlight': highlight_, "cowsay": cowsay_, "time": time_, "help": help_} +functions = {'highlight': highlight_, 'cowsay': cowsay_, 'time': time_, 'help': help_} From bf39444ef98bb619e38344fdac05f1916cafeb90 Mon Sep 17 00:00:00 2001 From: Namxobick <100288192+Namxobick@users.noreply.github.com> Date: Wed, 19 Oct 2022 19:50:37 +0300 Subject: [PATCH 09/11] Update cmdInput.py --- .../my_awesome_script/cmdInput.py" | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git "a/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/cmdInput.py" "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/cmdInput.py" index f159705..aebf2c5 100644 --- "a/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/cmdInput.py" +++ "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/cmdInput.py" @@ -1,17 +1,17 @@ import argparse -from .command import command + +from .command import command, functions def cmd_input(): + """Input method.""" parser = argparse.ArgumentParser() - parser.add_argument("operation", help="highlight 'any text' or cowsay 'any text' or time 'Region/City'" - "(example: Europe/Moscow)", - choices=["highlight", "cowsay", "time"]) - parser.add_argument("text") + parser.add_argument('operation', help='input command', choices=functions) + parser.add_argument('parameter', help="'Any text' or 'Region/City'") args = parser.parse_args() try: - command(args.operation, args.text) + command(args.operation, args.parameter) except Exception as ex: - command("help") + command('help') raise ex From 3c3e026c155dfe91d318f3795443e82944cf7154 Mon Sep 17 00:00:00 2001 From: Namxobick <100288192+Namxobick@users.noreply.github.com> Date: Wed, 19 Oct 2022 19:51:59 +0300 Subject: [PATCH 10/11] Update command.py --- .../my-awesome-script/my_awesome_script/command.py" | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git "a/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/command.py" "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/command.py" index 7d0ec6c..a8df3a2 100644 --- "a/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/command.py" +++ "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/command.py" @@ -1,9 +1,11 @@ + +from datetime import datetime + +import pytz +from cowpy import cow from pygments import highlight from pygments.formatters import TerminalFormatter from pygments.lexers import PythonLexer -from cowpy import cow -from datetime import datetime -import pytz def command(operation: str, text: str = None) -> None: @@ -24,7 +26,7 @@ def time_(timezone: str) -> None: def help_() -> None: - print("EXAMPLE COMMAND:") + print('EXAMPLE COMMAND:') print(" highlight 'any text'") print(" cowsay 'any text'") print(" time 'Region/City' (example: Europe/Moscow)") From 9c97217556a4c78400296113bf7073b3c9ae840f Mon Sep 17 00:00:00 2001 From: Namxobick <100288192+Namxobick@users.noreply.github.com> Date: Wed, 19 Oct 2022 19:53:42 +0300 Subject: [PATCH 11/11] Update cmdInput.py --- .../my-awesome-script/my_awesome_script/cmdInput.py" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/cmdInput.py" "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/cmdInput.py" index aebf2c5..04bfc96 100644 --- "a/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/cmdInput.py" +++ "b/homeworks/Yurin Andrey/\342\204\2265/my-awesome-script/my_awesome_script/cmdInput.py" @@ -6,7 +6,7 @@ def cmd_input(): """Input method.""" parser = argparse.ArgumentParser() - parser.add_argument('operation', help='input command', choices=functions) + parser.add_argument('operation', help='Input command', choices=functions) parser.add_argument('parameter', help="'Any text' or 'Region/City'") args = parser.parse_args()