diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..3b8b06b --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,22 @@ +# This is a basic workflow to help you get started with Actions + +name: CI + +on: push + +jobs: + code_quality: + runs-on: ubuntu-latest + name: Checks with black, isort and possibly run tests + container: python:3.9 + + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Run script + run: | + pip install flake8 isort pytest + ls -la + flake8 . + isort --check . + py.test tests diff --git a/homework1/.gitignore b/homework1/.gitignore deleted file mode 100644 index 1ceb2fd..0000000 --- a/homework1/.gitignore +++ /dev/null @@ -1,132 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# ide -.idea/ diff --git a/homework1/requirements.txt b/homework1/requirements.txt deleted file mode 100644 index 769dce2..0000000 --- a/homework1/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -pytest -pytest-sugar \ No newline at end of file diff --git a/homework1/sample_project/calculator/calc.py b/homework1/sample_project/calculator/calc.py deleted file mode 100644 index f214d8f..0000000 --- a/homework1/sample_project/calculator/calc.py +++ /dev/null @@ -1,2 +0,0 @@ -def check_power_of_2(a: int) -> bool: - return bool(a & (a - 1) == 0 and (a != 0)) diff --git a/homework1/sample_project/requirements.txt b/homework1/sample_project/requirements.txt deleted file mode 100644 index 1bf5204..0000000 --- a/homework1/sample_project/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -pytest -pytest-sugar diff --git a/homework1/sample_project/tests/__init__.py b/homework1/sample_project/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/homework1/sample_project/tests/test_calculator.py b/homework1/sample_project/tests/test_calculator.py deleted file mode 100644 index 1cfe600..0000000 --- a/homework1/sample_project/tests/test_calculator.py +++ /dev/null @@ -1,15 +0,0 @@ -import pytest - -from sample_project.calculator.calc import check_power_of_2 - -def test_positive_case(): - """Testing that actual powers of 2 give True""" - assert check_power_of_2(65536) - assert check_power_of_2(8) - assert check_power_of_2(256) - assert check_power_of_2(2048) -def test_negative_case(): - """Testing that non-powers of 2 give False""" - assert not check_power_of_2(12) - assert not check_power_of_2(0) - assert not check_power_of_2(-16) diff --git a/homework1/tasks/__init__.py b/homework1/tasks/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/homework1/tasks/task02.py b/homework1/tasks/task02.py deleted file mode 100644 index 3f71dc6..0000000 --- a/homework1/tasks/task02.py +++ /dev/null @@ -1,11 +0,0 @@ -from typing import Sequence - - -def check_fibonacci(data: Sequence[int]) -> bool: - for element in range(2, len(data)): - if data[element] == data[element - 1] + data[element - 2]: - continue - else: - return False - return True - diff --git a/homework1/tasks/task03.py b/homework1/tasks/task03.py deleted file mode 100644 index fd0e17f..0000000 --- a/homework1/tasks/task03.py +++ /dev/null @@ -1,17 +0,0 @@ -from typing import Tuple - - -def find_maximum_and_minimum(file_name: str) -> Tuple[int, int]: - max = 1 - min = 1 - with open(file_name) as fi: - for line in fi: - if int(line) > max: - max = int(line) - elif int(line) < min: - min = int(line) - else: - continue - a = min, max - print(a) - return a diff --git a/homework1/tasks/task04.py b/homework1/tasks/task04.py deleted file mode 100644 index 4369551..0000000 --- a/homework1/tasks/task04.py +++ /dev/null @@ -1,10 +0,0 @@ -from typing import List - -def check_sum_of_four(a: List[int], b: List[int], c: List[int], d: List[int]) -> int: - counter = 0 - for i in range (0, len(a)): - if a[i] + b[i] + c[i] + d[i] == 0: - counter += 1 - else: - continue - return counter \ No newline at end of file diff --git a/homework1/tasks/task05.py b/homework1/tasks/task05.py deleted file mode 100644 index a02440a..0000000 --- a/homework1/tasks/task05.py +++ /dev/null @@ -1,11 +0,0 @@ -from typing import List - - -def find_maximal_subarray_sum(nums: List[int], k: int) -> int: - summ = 0 - current_sum = 0 - for num in nums: - current_sum = sum(nums[num:k+num]) - if current_sum > summ: - summ = current_sum - return summ \ No newline at end of file diff --git a/homework1/tests/__init__.py b/homework1/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/homework1/tests/some_file.txt b/homework1/tests/some_file.txt deleted file mode 100644 index 1341a1e..0000000 --- a/homework1/tests/some_file.txt +++ /dev/null @@ -1,10 +0,0 @@ -0 -1 -2 -3 -4 -5 -6 -16 -18 -22222222 \ No newline at end of file diff --git a/homework1/tests/tests.py b/homework1/tests/tests.py deleted file mode 100644 index d799d48..0000000 --- a/homework1/tests/tests.py +++ /dev/null @@ -1,49 +0,0 @@ -import pytest - -from tasks.task02 import check_fibonacci -from tasks.task03 import find_maximum_and_minimum -from tasks.task04 import check_sum_of_four -from tasks.task05 import find_maximal_subarray_sum - - -def test_task_02(): - check1 = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55] - check2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] - check3 = [25, 26, 1, 2, 3, 5, 8, 13, 21, 34, 55] - - assert check_fibonacci(check1) - assert check_fibonacci(check2) is False - assert check_fibonacci(check3) is False - - -def test_task_03(): - assert find_maximum_and_minimum("some_file.txt") - - -def test_task_04(): - a = [2, 4, 6, 8] - b = [0, -4, 6, 8] - c = [-2, 0, -12, 8] - d = [0, 0, 0, -24] - - a1 = [0, 5, 5, 5, 5, 5, 5, 5, 5, 5] - b1 = [0, 6, -5, 5, 7, 2, 3, 6, 2, 3] - c1 = [0, 2, 3, 4, 5, 6, 7, 1, 6, 1] - d1 = [0, 3, -3, 4, 6, 7, 8, 9, 1, 3] - - # check - assert (check_sum_of_four(a, b, c, d), - check_sum_of_four(a1, b1, c1, d1), check_sum_of_four(b, b1, c1, d), check_sum_of_four(a, b1, c1, d)) - - -def test_task_05(): - nums = [1, 3, -1, -3, 5, 3, 6, 7] - k = 3 - - nums_1 = [0, 2, -2, 4, 6, 7, 8, 9, 9, 12, 12, -4, -5, -22, -30] - k_1 = 2 - - nums_2 = [0, 1, 2, 3] - k_2 = 10 - assert (find_maximal_subarray_sum(nums, k), find_maximal_subarray_sum(nums_1, k_1), - find_maximal_subarray_sum(nums_2, k_2)) diff --git a/homework1/__init__.py b/homework3/__init__.py similarity index 100% rename from homework1/__init__.py rename to homework3/__init__.py diff --git a/homework3/task01.py b/homework3/task01.py new file mode 100644 index 0000000..8275b59 --- /dev/null +++ b/homework3/task01.py @@ -0,0 +1,49 @@ +""" +In previous homework task 4, you wrote a cache function that remembers other +function output value. Modify it to be a parametrized decorator, +so that the following code: + +@cache(times=3) +def some_function(): + pass +Would give out cached value up to times number only. Example: + +@cache(times=2) +def f(): + return input('? ') # careful with input() in python2, + use raw_input() instead +f() +? 1 +'1' +f() # will remember previous value +'1' +f() # but use it up to two times only +'1' +f() +? 2 +'2' +""" + +import functools + + +def cache(times): + def wrapped_cache(func): + cache_dict = {} + counter = times + + @functools.wraps(func) + def wrapped_func(*args, **kwargs): + nonlocal counter + if counter > 0 and args in cache_dict: + counter -= 1 + return cache_dict[args] + else: + values_to_cache = func(*args, **kwargs) + cache_dict[args] = values_to_cache + counter = times - 1 + return values_to_cache + + return wrapped_func + + return wrapped_cache diff --git a/homework3/task02.py b/homework3/task02.py new file mode 100644 index 0000000..91b4465 --- /dev/null +++ b/homework3/task02.py @@ -0,0 +1,49 @@ +""" +Here's a not very efficient calculation function that calculates +something important: + +import time +import struct +import random +import hashlib + +def slow_calculate(value): + """"""Some weird voodoo magic calculations"""""" + time.sleep(random.randint(1,3)) + data = hashlib.md5(str(value).encode()).digest() + return sum(struct.unpack('<' + 'B' * len(data), data)) + +Calculate total sum of slow_calculate() of all numbers +starting from 0 to 500. +Calculation time should not take more than a minute. +Use functional capabilities of multiprocessing module. +You are not allowed to modify slow_calculate function. +""" + +import hashlib +import random +import struct +import time +from multiprocessing import Pool, freeze_support + + +def slow_calculate(value): + """Some weird voodoo magic calculations""" + time.sleep(random.randint(1, 3)) + data = hashlib.md5(str(value).encode()).digest() + return sum(struct.unpack('<' + 'B' * len(data), data)) + + +def timing(): + start_time = time.time() + return lambda x: print("[{:.2f}s] {}".format(time.time() - start_time, x)) + + +def pool_calculation(last_value, processors): + loop_range = range(0, last_value) + with Pool(processors) as pool: + return sum(pool.map(slow_calculate, loop_range)) + + +if __name__ == '__main__': + freeze_support() diff --git a/homework3/task03.py b/homework3/task03.py new file mode 100644 index 0000000..dc56d00 --- /dev/null +++ b/homework3/task03.py @@ -0,0 +1,58 @@ +# I decided to write a code that generates data filtering +# object from a list of keyword parameters: + +class Filter: + """ + Helper filter class. Accepts a list of single-argument + functions that return True if object in list conforms to some criteria + """ + + def __init__(self, functions): + self.functions = functions + + def apply(self, data): + return [ + item for item in data + if all(i(item) for i in self.functions) + ] + + +# example of usage: +# positive_even = Filter(lamba a: a % 2 == 0, lambda a: a > 0, +# lambda a: isinstance(int, a))) +# positive_even.apply(range(100)) should return only even numbers from 0 to 99 + + +def make_filter(**keywords): + """ + Generate filter object for specified keywords + """ + filter_funcs = [] + for key, value in keywords.items(): + def keyword_filter_func(sub_value): + return sub_value.get(key) == value + + filter_funcs.append(keyword_filter_func) + return Filter(filter_funcs) + + +sample_data = [ + { + "name": "Bill", + "last_name": "Gilbert", + "occupation": "was here", + "type": "person", + }, + { + "is_dead": True, + "kind": "parrot", + "type": "bird", + "name": "polly" + } +] + +# make_filter(name='polly', type='bird').apply(sample_data) +# should return only second entry from the list + +# There are multiple bugs in this code. +# Find them all and write tests for faulty cases. diff --git a/homework3/task04.py b/homework3/task04.py new file mode 100644 index 0000000..739f239 --- /dev/null +++ b/homework3/task04.py @@ -0,0 +1,23 @@ +""" +Armstrong number is a number that is the sum of its +own digits each raised to the power of the number of digits. +https://en.wikipedia.org/wiki/Narcissistic_number +Examples: +- 9 is an Armstrong number, 9 = 9^1 = 9 +- 10 is not: 10 != 1^2 + 0^2 = 1 +- 153 is : 153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153 +Write a function that detects if a number is +Armstrong number in functionaly style: + - use map or other utilities from functools library, + - use anonymous functions (or use function as argument) + - do not use loops, preferably using list comprehensions +### Example function signature and call +""" + + +def is_armstrong(number: int) -> bool: + end_sum = 0 + power = len(str(number)) + for i in str(number): + end_sum += pow(int(i), int(power)) + return number == end_sum diff --git a/homework1/sample_project/__init__.py b/tests/__init__.py similarity index 100% rename from homework1/sample_project/__init__.py rename to tests/__init__.py diff --git a/homework1/sample_project/calculator/__init__.py b/tests/homework3/__init__.py similarity index 100% rename from homework1/sample_project/calculator/__init__.py rename to tests/homework3/__init__.py diff --git a/tests/homework3/test_task01.py b/tests/homework3/test_task01.py new file mode 100644 index 0000000..d25f8b3 --- /dev/null +++ b/tests/homework3/test_task01.py @@ -0,0 +1,18 @@ +from homework3.task01 import cache + +counter = 0 + + +def test_cache_decorator(): + @cache(times=2) + def f(a, b): + global counter + counter += 1 + return a + b + + assert f(1, 2) == 3 + assert counter == 1 + assert f(1, 2) == 3 + assert counter == 1 + assert f(1, 2) == 3 + assert counter == 2 diff --git a/tests/homework3/test_task02.py b/tests/homework3/test_task02.py new file mode 100644 index 0000000..676233f --- /dev/null +++ b/tests/homework3/test_task02.py @@ -0,0 +1,23 @@ +import time + +from homework3.task02 import pool_calculation, slow_calculate + + +def slow_calculation_loop(last_value): + loop_range = range(0, last_value) + return sum(slow_calculate(i) for i in loop_range) + + +def test_result_of_slow_calculation(): + assert pool_calculation(500, 30) == 1024259 + + +def test_time_of_slow_calculation(): + start_time = time.time() + pool_calculation(500, 30) + end_time = time.time() - start_time + assert end_time < 60 + + +def test_equal_results_from_both_func(): + assert pool_calculation(5, 30) == slow_calculation_loop(5) diff --git a/tests/homework3/test_task03.py b/tests/homework3/test_task03.py new file mode 100644 index 0000000..fad4fcf --- /dev/null +++ b/tests/homework3/test_task03.py @@ -0,0 +1,7 @@ +from homework3.task03 import make_filter, sample_data + + +def test_main_result(): + result = [{'is_dead': True, 'kind': 'parrot', + 'type': 'bird', 'name': 'polly'}] + assert make_filter(name='polly', type='bird').apply(sample_data) == result diff --git a/tests/homework3/test_task04.py b/tests/homework3/test_task04.py new file mode 100644 index 0000000..8017592 --- /dev/null +++ b/tests/homework3/test_task04.py @@ -0,0 +1,17 @@ +from homework3.task04 import is_armstrong + + +def test_positive_armstrong_list(): + armstrong_numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 153, 370, 371, 407, + 1634, 8208, 9474, 54748, 92727, 93084, 548834, + 1741725, 4210818, 9800817, 146511208, 472335975, + 534494836, 912985153, 4679307774] + + for i in armstrong_numbers: + assert is_armstrong(i) + + +def test_negative_armstrong(): + just_numbers = [10, 11, 12, 124, 555, 666] + for i in just_numbers: + assert not is_armstrong(i)