- Introduces
@overload,@overrideand@<Function>.adddecorators, allowing one to overload and override functions. Functions are then called according to their argument types:
@overload
def func(var: str):
return var
#via @<Function>.add
@func.add
def _(var: int) -> str:
return str(var * 5)
#via @overload
@overload
def func() -> str:
return "Functions don't need to have arguments."
#via @override
@override(funcs=[func])
def new(str_1: str, int_1: int):
return str_1 * int_1
assert func("a") == "a" == new("a")
assert func(1) == "5" == new(1)
assert func() == "Functions don't need to have arguments." == new()
assert new("house", 2) == "househouse"- Raises human readable errors, if no callable was determined with the given arguments:
@overload
def some_func(str_1: str, int_1: int):
return str_1 + str(int_1)
@overload
def some_func(str_1: str):
return str_1
>>> some_func(str_1=2)
PyOverloadError:
Error when calling:
(__main__.some_func):
def some_func(str_1: int):
...:
'str_1' needs to be of type (<class 'str'>,) (is type <class 'int'>)or
>>> some_func(10)
__main__.NoFunctionFoundError: No matching function found.
Following definitions of 'some_func' were found:
(__main__.some_func):
def some_func(str_1: str, int_1: int):
...
(__main__.some_func):
def some_func(str_1: str):
...
The following call was made:
(__main__.some_func):
def some_func(int_1: int):
...- Any type of variables is allowed: Build-in ones like
str, int, Listbut also own ones, like classes etc. @overloadusesget_type_hintsto identify the right function call via type-checking. Hence, it may also be used as a type-checker for functions.- Forgot, which overloads of a specific function have been implemented? No worries, you can print them with their typing information using
print(func_versions_info(<my_func>)), e.g.
>>> print(func_versions_info(some_func))
Following overloads of 'some_func' exist:
(__main__.some_func):
def some_func(str_1: str, int_1: int):
...
(__main__.some_func):
def some_func(str_1: str):
...Requires Python 3.7+.
pip install -U overloadlibor install with Poetry
poetry add overloadlibThen you can run
overloadlib --helpor with Poetry:
poetry run overloadlib --helpInstalling Poetry
To download and install Poetry run (with curl):
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py | python -or on windows (without curl):
(Invoke-WebRequest -Uri https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py -UseBasicParsing).Content | python -If you wan to uninstall the package, simply run
pip uninstall overloadlibor with Poetry:
poetry remove overloadlibImport
Imports need to come form overloadlib. Importing via
from overloadlib import *imports overload, override and <Function>.add decorators, as well as func_versions_info.
Overloading of functions can be done via the @overload, @override or @<Function>.add decorator.
Overloading of functions:
@overload
def func(var: str):
return var
@overload
def func(var: int):
return str(var * 5)
func("a") == "a" # True
"a: " + func(1) # "a: 5"Overloading of methods (or mixtures of both) are also possible using the same decorator:
@dataclass
class Hello:
text: str = "Hello"
class Some:
def __init__(self) -> None:
pass
@overload
def func(self, str_1: str, int_1: int) -> str:
return str_1 + str(int_1)
@overload
def func(self, str_1: str) -> str:
return str_1
@overload
def func(self, obj: Hello) -> str:
return obj.text
@overload
def func(str_1: str) -> str:
return "yummy " + str_1Note that own classes, can be given as types to the function. Furthermore, methods and functions ma have the same name. Possible calls could now look like this:
# Giving only *args
some.func("Number: ", 1) # "Number: 1"
# Giving **kwargs
some.func(str_1="Number: ", int_1=1) # "Number: 1"
# An object as argument
some.func(Hello()) # "Hello"
# Calling the function not the method
func("yummy") # "yummy cheese"You may also 'overload' functions using the @override decorator. This one overrides an list of callables or Function (function wrapper class of overloadlib.py.) via a given new 'parent' function.
def func_str(var: str) -> str:
return "I am a string"
def func_int(var: int) -> str:
return "I am an integer"
@overload
def func_both(var_1: int, var_2: str) -> str:
return var_2 * var_1
@override(funcs=[func_str, func_int, func_both]) # callables and `Function` are given
def new_func(fl: float) -> str:
return "Float parameter"Possible calls could now look like this:
new_func(1.0) == "Float parameter" # True
new_func("a") == func_str("a") == "I am a string" # True
new_func(1) == func_int(1) == "I am an integer" # True
new_func(1, "a") == func_both(1, "a") == "a" # TrueOverriding Function's (callables that are decorated with @overload) overrides every version of that Function:
@dataclass
class Some:
text: str = "Hello"
@overload
def func(str_1: str) -> str:
return str_1
@func.add
def _(obj: Some) -> str:
return obj.text
@overload
def func() -> str:
return "Functions don't need to have arguments."
# adds all previously defined overloads/'version' of `func` to `new`
@override(funcs=[func])
def new(str_1: str, int_1: int) -> str:
return str_1 * int_1
assert new("a") == "a" == func("a")
assert new(Some()) == "Hello" == func(Some())
assert new() == "Functions don't need to have arguments." == func()
assert new("house", 2) == "househouse"You can always add a new callable to an existing overloaded callable <func> using the @<func>.add decorator:
@overload
def some_func(str_1: str, int_1: int) -> str:
return str_1 + str(int_1)
@some_func.add
def _(str_1: str) -> str:
return str_1
@some_func.add
def name_does_not_matter() -> str:
return "I return some text."
@some_func.add
def _(str_1: str, str_2: str) -> str:
return str_1 + str_2
assert some_func("This is a number: ", 10) == "This is a number: 10"
assert some_func("cheese") == "cheese"
assert some_func(Some()) == "Hello"The name of the callable's you are adding don't matter and you can also always use the same name, when adding. However, as using the same name for added functions, clashes with [no-redef] error of mypy_, it is recommended to use different ones (this also increases the readability of the code).1
Usage of @override and @<Function>.add is recommended over usage of @overload only.
If you want to get all versions of a certain function <myfunc>, use func_versions_info(<myfunc>), e.g.
>>> print(func_versions_info(new_func))
Following overloads of 'new_func' exist:
(__main__.new_func):
def new_func(var: str):
...
(__main__.new_func):
def new_func(var: int):
...
(__main__.new_func):
def new_func(var_1: int, var_2: str):
...
(__main__.new_func):
def new_func(fl: float):
...- Overloading using overload raises problems with
mypy. This can be circumvented using@overrideinstead of@overload.
Contributions are very welcome.
To learn more, see the Contributor Guide.
- Set up Dependabot to ensure you have the latest dependencies.
- Set up Stale bot for automatic issue closing.
Want to know more about Poetry? Check its documentation.
Details about Poetry
Poetry's commands are very intuitive and easy to learn, like:
poetry add numpy@latestpoetry run pytestpoetry publish --build
etc
Building a new version of the application contains steps:
- Switch to a branch
- Bump the version of your package
poetry version <version>. You can pass the new version explicitly, or a rule such asmajor,minor, orpatch. For more details, refer to the Semantic Versions standard. - Make a commit to
GitHuband push it. - Open a pull request.
- Merge the pull request π
- Supports for
Python 3.7and higher. Poetryas the dependencies manager. See configuration inpyproject.tomlandsetup.cfg.- Automatic codestyle with
black,isortandpyupgrade. - Ready-to-use
pre-commithooks with code-formatting. - Type checks with
mypy; docstring checks withdarglint; security checks withsafetyandbandit - Testing with
pytest. - Automating testing in multiple Python environments, linting, pre-commit, typechecks, doctests and safety checks using
nox - Ready-to-use
.editorconfigand.gitignore. You don't have to worry about those things.
The following table gives an overview of the available Nox sessions:
| Session | Description | Python | Default |
|---|---|---|---|
| coverage | Report coverage with Coverage.py | 3.9 |
(β) |
| docs | Build and serve Sphinx documentation | 3.9 |
|
| docs | Build Sphinx documentation | 3.9 |
β |
| mypy | Type-check with mypy | 3.6 β¦ 3.9 |
β |
| pre-commit | Lint with pre-commit | 3.9 |
β |
| safety | Scan dependencies with Safety | 3.9 |
β |
| tests | Run tests with pytest | 3.[6, 7, 8, 9] β¦ 3.9 |
β |
| typeguard | Type-check with Typeguard | 3.[6, 7, 8, 9] β¦ 3.9 |
β |
| xdoctest | Run examples with xdoctest | 3.[6, 7, 8, 9] β¦ 3.9 |
β |
GitHubintegration: issue and pr templates.Github Actionswith predefined build workflow as the default CI/CD.- Everything is already set up for security checks, codestyle checks, code formatting, testing, linting, docker builds, etc with
nox. More details in makefile-usage. - Dockerfile for your package.
- Always up-to-date dependencies with
@dependabot. You will only enable it. - Automatic drafts of new releases with
Release Drafter. You may see the list of labels inrelease-drafter.yml. Works perfectly with Semantic Versions specification.
- Ready-to-use Pull Requests templates and several Issue templates.
- Files such as:
LICENSE,CONTRIBUTING.md,CODE_OF_CONDUCT.md, andSECURITY.mdare generated automatically. Stale botthat closes abandoned issues after a period of inactivity. (You will only need to setup free plan). Configuration is here.- Semantic Versions specification with
Release Drafter.
You can see the list of available releases on the GitHub Releases page.
We follow Semantic Versions specification.
We use Release Drafter. As pull requests are merged, a draft release is kept up-to-date listing the changes, ready to publish when youβre ready. With the categories option, you can categorize pull requests in release notes using labels.
| Pull Request Label | Section in Release Notes |
|---|---|
breaking |
π₯ Breaking Changes |
enhancement |
π Features |
removal |
π₯ Removals and Deprecations |
bug |
π Fixes |
performance |
π Performance |
testing |
π¨ Testing |
ci |
π· Continuous Integration |
documentation |
π Documentation |
refactoring |
π¨ Refactoring |
style |
π Style |
dependencies |
π¦ Dependencies |
You can update it in release-drafter.yml.
GitHub creates the bug, enhancement, and documentation labels for you.
Dependabot creates the dependencies label.
Create the remaining labels when you need them,
on the Issues tab of your GitHub repository,
This project is licensed under the terms of the MIT license. See LICENSE for more details.
If you encounter any problems, please file an issue along with a detailed description.
@misc{overloadlib,
author = {Niclas D. Gesing},
title = {Overloadlib: A python package to implement overloading of functions in python.},
year = {2021},
publisher = {GitHub},
journal = {GitHub repository},
howpublished = {\url{https://github.com/NicDom/overloadlib}}
}This project was generated with python-package-template
Footnotes
-
It should also be stressed, that
@<func>.addonly works for a with@overloaddecorated function `. β©