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
41 changes: 34 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
# @main
# @python_main

[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/python-main)](https://pypi.org/project/python-main/)
[![PyPI - Version](https://img.shields.io/pypi/v/python-main)](https://pypi.org/project/python-main/)


`@main` decorator which runs the tagged function if the current module is being executed as a script.
`@python_main` is a decorator which:
- Automatically calls the function(s) tagged with it, if the current module is being **executed as a script**.
- Does nothing if the current module is being **imported**.

No more `if __name__ == "__main__":` all over the place.
It is, essentially, equivalent to the `if __name__ == "__main__":` construct, but as a decorator.

That's it!
That's all it does.

### Installation

Expand All @@ -20,14 +22,39 @@ poetry add python-main # ...
### Usage

```python
from python_main import main
from python_main import python_main

A = 10
B = 20


@main
@python_main
def do_print():
"""This will run if this module is executed."""
print(A + B)
```

You can also tag multiple functions with `@python_main` and they will all run if the module is executed, in the order they are defined.

```python
from python_main import python_main

A = 10
B = 20
C = 0

@python_main
def add_a_to_c():
global C
C += A

# ... other functions/ definitions ...

@python_main
def add_b_to_c():
global C
C += B

# At this point:
# - C will be 30 if this module is executed as a script.
# - C will be untouched if this module is imported.
```
15 changes: 10 additions & 5 deletions python_main/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@
__MAIN_RETURN_TYPE = TypeVar("__MAIN_RETURN_TYPE")


def main(f: Callable[[], __MAIN_RETURN_TYPE]) -> Callable[[], __MAIN_RETURN_TYPE]:
if getattr(f, __CALLABLE_MODULE_PROP) == __RAN_AS_SCRIPT_MODULE:
f()
return f
def python_main(
main_function_to_be_called: Callable[[], __MAIN_RETURN_TYPE]
) -> Callable[[], __MAIN_RETURN_TYPE]:
if (
getattr(main_function_to_be_called, __CALLABLE_MODULE_PROP)
== __RAN_AS_SCRIPT_MODULE
):
main_function_to_be_called()
return main_function_to_be_called


# Only export the main function
__all__ = ["main"]
__all__ = ["python_main"]
6 changes: 3 additions & 3 deletions tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pytest

from python_main import main
from python_main import python_main

EXIT_CODE_RECEIVED = -1

Expand Down Expand Up @@ -44,7 +44,7 @@ def test_assert_function_actually_gets_called(mock_exit):
__my_main_func.__module__ = "__main__"

# Decorate it
main(__my_main_func)
python_main(__my_main_func)

# Ensure that our main function was able to call mock_exit with the expected value.
global EXIT_CODE_RECEIVED
Expand All @@ -60,7 +60,7 @@ def test_assert_function_does_not_get_called(mock_exit):
"""

# Call the function, which is coming from a pytest execution and being imported as a module
function_returned = main(__my_main_func)
function_returned = python_main(__my_main_func)

# Exit code will not have been set.
global EXIT_CODE_RECEIVED
Expand Down