Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[flake8]
format = html
htmldir = flake8-report
count = True
max-complexity = 10
statistics = True
max-line-length = 127
21 changes: 17 additions & 4 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,27 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
pip install flake8 flake8-html pytest pytest-html
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --format=html --htmldir=flake8-report
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --format=html --htmldir=flake8-report
- name: Upload HTML Flake8 report
uses: actions/upload-artifact@v4
with:
name: flake8-html-report
path: flake8-report/
retention-days: 1
- name: Test with pytest
run: |
pytest -vv
pytest -v --html=pytest-report.html --self-contained-html
- name: Upload HTML Pytest report
uses: actions/upload-artifact@v4
with:
name: pytest-html-report
path: pytest-report.html
retention-days: 1

6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,8 @@ cython_debug/
.idea

# user generated files
*output.log
*output.log

# Flake8
flake8-report

4 changes: 3 additions & 1 deletion src/asynchronous/runner/concurrent.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
from asynchronous.runner.task.myasynctask import custom_async_task
from asynchronous.runner.operation.myoperations import first_operation, second_operation


# this coroutine runs both runner concurrently with asyncio.gather()
async def running_concurrently():
await asyncio.gather(custom_async_task(first_operation, 3),
custom_async_task(second_operation, 2))


# starts the event loop, running and waiting for both runner to complete
def main():
asyncio.run(running_concurrently())
asyncio.run(running_concurrently())
2 changes: 2 additions & 0 deletions src/asynchronous/runner/operation/myoperations.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
Definition of two simple operations.
"""


def first_operation():
print("First operation")


def second_operation():
print("Second operation")
5 changes: 4 additions & 1 deletion src/asynchronous/runner/sequential.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
from asynchronous.runner.task.myasynctask import custom_async_task
from asynchronous.runner.operation.myoperations import first_operation, second_operation

# runs two runner sequentially, waiting for one to finish before starting the next

# runs two runner sequentially, waiting for
# one to finish before starting the next
async def running_sequentially():
await custom_async_task(first_operation, 3)
await custom_async_task(second_operation, 2)


# executes both runner, totaling around 5 seconds of runtime
def main():
asyncio.run(running_sequentially())
3 changes: 2 additions & 1 deletion src/asynchronous/runner/task/myasynctask.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import asyncio


async def custom_async_task(func, sleep_time):
"""
Custom asynchronous task (coroutine)
Expand All @@ -9,4 +10,4 @@ async def custom_async_task(func, sleep_time):
print(f"Execution of {func.__name__} started")
func()
await asyncio.sleep(sleep_time)
print(f"Execution of {func.__name__} completed")
print(f"Execution of {func.__name__} completed")
1 change: 1 addition & 0 deletions src/asynchronous/tests/usage_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def test_sequential_run(mock_print):

mock_print.assert_has_calls(order_of_calls, any_order=False)


@patch('builtins.print', create=True)
def test_concurrent_run(mock_print):
concurrent.main()
Expand Down
3 changes: 2 additions & 1 deletion src/asynchronous/usage1.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from asynchronous.runner import sequential

sequential.main()

sequential.main()
3 changes: 2 additions & 1 deletion src/asynchronous/usage2.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from asynchronous.runner import concurrent

concurrent.main()

concurrent.main()
17 changes: 12 additions & 5 deletions src/decorator/logging/mylog.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@
from enum import Enum
from datetime import datetime


class Level(Enum):
NONE = 0
INFO = 1
DEBUG = 2


LOG_FILE_NAME = "output.log"


# If you’ve called @name without arguments, then the
# decorated function will be passed in as _func. If you’ve
# called it with arguments, then _func will be None, and
Expand All @@ -23,18 +26,23 @@ class Level(Enum):
# that you can’t call the remaining arguments as positional arguments.
def logme(_func=None, *, level: Level = Level.NONE):
def decorated_function(func):
@wraps(func) # this ensures docstring, function name, arguments list, etc. are all copied
# to the wrapped function - instead of being replaced with wrapper's info
# "wraps" ensures docstring, function name, arguments list,
# etc. are all copied to the wrapped function - instead of
# being replaced with wrapper's info
@wraps(func)
def wrapper(*args, **kwargs):
if show_info():
with open(LOG_FILE_NAME, "a+") as log_file:
log_file.writelines("[{}] Executing function: {}\n".format(get_current_timestamp(), func.__name__))
log_file.writelines("[{}] Executing function: {}\n".format(
get_current_timestamp(), func.__name__))

result = func(*args, **kwargs)

if show_debug():
with open(LOG_FILE_NAME, "a+") as log_file:
log_file.writelines("[{}] Result of function {} is: {}\n".format(get_current_timestamp(), func.__name__, result))
log_file.writelines("[{}] Result of function {} is: {}\n"
.format(get_current_timestamp(),
func.__name__, result))

return result

Expand Down Expand Up @@ -63,4 +71,3 @@ def show_debug():
# Apply the decorator to the function immediately.
else:
return decorated_function(_func)

10 changes: 7 additions & 3 deletions src/decorator/tests/mylog_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def test_logme_for_none(mock_print, mock_open):
# but target function was called
mock_print.assert_called_once()


@patch("builtins.open", create=True)
@patch("builtins.print", create=True)
def test_logme_for_info(mock_print, mock_open):
Expand All @@ -42,14 +43,16 @@ def test_logme_for_info(mock_print, mock_open):
"""
mock_print.__name__ = "print"

# logme returns a wrapper function for the passed function, and I am calling it directly
# logme returns a wrapper function for the passed
# function, and I am calling it directly
logme(mock_print, level=Level.INFO)()

# one write call to the log output
mock_open.assert_called_with(LOG_FILE_NAME, "a+")
# target function was called
mock_print.assert_called_once()


@patch("builtins.open", create=True)
@patch("builtins.print", create=True)
def test_logme_for_debug(mock_print, mock_open):
Expand All @@ -60,10 +63,11 @@ def test_logme_for_debug(mock_print, mock_open):
"""
mock_print.__name__ = "print"

# logme returns a wrapper function for the passed function, and I am calling it directly
# logme returns a wrapper function for the passed
# function, and I am calling it directly
logme(print, level=Level.DEBUG)()

# in this case, two write calls to the log output
assert mock_open.call_count == 2
# target function was called
mock_print.assert_called_once()
mock_print.assert_called_once()
5 changes: 5 additions & 0 deletions src/decorator/using_mylog.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,27 @@
"""
from logging.mylog import logme, Level


@logme
def add(a, b):
return a + b


@logme(level=Level.INFO)
def subtract(a, b):
return a - b


@logme(level=Level.DEBUG)
def multiply(a, b):
return a * b


@logme
def divide(a, b):
return a / b


print("Adding two numbers: " + str(add(1, 2)))
print("Subtracting two numbers: " + str(subtract(8, 3)))
print("Multiplying two numbers: " + str(multiply(5, 6)))
Expand Down