Skip to content

Commit 10f8b20

Browse files
committed
Initial commit
0 parents  commit 10f8b20

15 files changed

+1530
-0
lines changed

.github/workflows/publish.yaml

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Publish Python Package
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
deploy:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- uses: actions/checkout@v2
13+
- name: Set up Python
14+
uses: actions/setup-python@v2
15+
with:
16+
python-version: '3.8'
17+
- name: Install dependencies
18+
run: |
19+
python -m pip install --upgrade pip setuptools pipenv
20+
pipenv install --dev
21+
- name: Build and publish
22+
env:
23+
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
24+
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
25+
run: |
26+
pipenv run python setup.py sdist
27+
pipenv run twine upload dist/*

.github/workflows/push.yml

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Push CI
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
build:
7+
runs-on: ubuntu-latest
8+
strategy:
9+
matrix:
10+
python-version: [3.7, 3.8]
11+
12+
steps:
13+
- uses: actions/checkout@v2
14+
- name: Set up Python ${{ matrix.python-version }}
15+
uses: actions/setup-python@v2
16+
with:
17+
python-version: ${{ matrix.python-version }}
18+
- name: Install dependencies
19+
run: |
20+
python -m pip install -U pip pipenv
21+
pipenv install --dev
22+
- name: Lint with flake8
23+
uses: grantmcconnaughey/lintly-flake8-github-action@v1.0
24+
if: github.event_name == 'pull_request'
25+
- name: Unit tests
26+
run: |
27+
pipenv run python -m unittest

.gitignore

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
build/
12+
develop-eggs/
13+
dist/
14+
downloads/
15+
eggs/
16+
.eggs/
17+
lib/
18+
lib64/
19+
parts/
20+
sdist/
21+
var/
22+
wheels/
23+
pip-wheel-metadata/
24+
share/python-wheels/
25+
*.egg-info/
26+
.installed.cfg
27+
*.egg
28+
MANIFEST
29+
30+
# PyInstaller
31+
# Usually these files are written by a python script from a template
32+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
33+
*.manifest
34+
*.spec
35+
36+
# Installer logs
37+
pip-log.txt
38+
pip-delete-this-directory.txt
39+
40+
# Unit test / coverage reports
41+
htmlcov/
42+
.tox/
43+
.nox/
44+
.coverage
45+
.coverage.*
46+
.cache
47+
nosetests.xml
48+
coverage.xml
49+
*.cover
50+
*.py,cover
51+
.hypothesis/
52+
.pytest_cache/
53+
54+
# Translations
55+
*.mo
56+
*.pot
57+
58+
# Django stuff:
59+
*.log
60+
local_settings.py
61+
db.sqlite3
62+
db.sqlite3-journal
63+
64+
# Flask stuff:
65+
instance/
66+
.webassets-cache
67+
68+
# Scrapy stuff:
69+
.scrapy
70+
71+
# Sphinx documentation
72+
docs/_build/
73+
74+
# PyBuilder
75+
target/
76+
77+
# Jupyter Notebook
78+
.ipynb_checkpoints
79+
80+
# IPython
81+
profile_default/
82+
ipython_config.py
83+
84+
# pyenv
85+
.python-version
86+
87+
# pipenv
88+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
90+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
91+
# install all needed dependencies.
92+
#Pipfile.lock
93+
94+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
95+
__pypackages__/
96+
97+
# Celery stuff
98+
celerybeat-schedule
99+
celerybeat.pid
100+
101+
# SageMath parsed files
102+
*.sage.py
103+
104+
# Environments
105+
.env
106+
.venv
107+
env/
108+
venv/
109+
ENV/
110+
env.bak/
111+
venv.bak/
112+
113+
# Spyder project settings
114+
.spyderproject
115+
.spyproject
116+
117+
# Rope project settings
118+
.ropeproject
119+
120+
# mkdocs documentation
121+
/site
122+
123+
# mypy
124+
.mypy_cache/
125+
.dmypy.json
126+
dmypy.json
127+
128+
# Pyre type checker
129+
.pyre/
130+
131+
# IntelliJ
132+
.DS_Store
133+
.idea

LICENSE

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright 2020 Xevo Inc.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.

Pipfile

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[[source]]
2+
name = 'pypi'
3+
url = 'https://pypi.org/simple'
4+
verify_ssl = true
5+
6+
[packages]
7+
dynamic_dispatch = {editable = true, path = '.'}
8+
9+
[dev-packages]
10+
dynamic_dispatch = {editable = true, path = '.', extras = ['dev']}

README.md

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Dynamic Dispatch
2+
3+
![Build status](https://img.shields.io/github/workflow/status/XevoInc/dynamic_dispatch/Push%20CI/master)
4+
[![PyPI](https://img.shields.io/pypi/v/dynamic-dispatch)](https://pypi.org/project/dynamic-dispatch/)
5+
![PyPI - License](https://img.shields.io/pypi/l/dynamic-dispatch)
6+
7+
A lightweight, dynamic dispatch implementation for classes and functions. This allows a class or function to delegate
8+
its implementation conditioned on the value of its first argument. This is similar to `functools.singledispatch`,
9+
however this library dispatches over value while the other dispatches over type.
10+
11+
## Install
12+
13+
You may install this via the [`dynamic-dispatch`](https://pypi.org/project/dynamic-dispatch/) package on [PyPi](https://pypi.org):
14+
15+
```bash
16+
pip3 install dynamic-dispatch
17+
```
18+
19+
## Usage
20+
21+
22+
## Development
23+
24+
When developing, it is recommended to use Pipenv. To create your development environment:
25+
26+
```bash
27+
pipenv install --dev
28+
```
29+
30+
### Testing
31+
32+
This library uses the `unittest` framework. Tests may be run with the following:
33+
34+
```bash
35+
python3 -m unittest
36+
```

dynamic_dispatch/__init__.py

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
""" Like functools.singledispatch, but dynamic, value-based dispatch. """
2+
3+
__all__ = ('dynamic_dispatch',)
4+
5+
import functools
6+
import inspect
7+
8+
from typing import Union, Callable, Type, Hashable
9+
10+
from dynamic_dispatch._class import class_dispatch
11+
from dynamic_dispatch._func import func_dispatch
12+
13+
from ._typeguard import typechecked
14+
15+
16+
@typechecked(always=True)
17+
def dynamic_dispatch(func: Union[Callable, Type, None] = None, *, default: bool = False):
18+
"""
19+
Value-based dynamic-dispatch class decorator.
20+
21+
Allows a class or function to have different implementations depending on the
22+
value of func's first parameter. The decorated class or function can act as
23+
the default implementation, if desired.
24+
25+
Additional implementations may be registered for dispatch using the register()
26+
attribute of the dispatch class or function. If the implementation has a param
27+
of the same name as the first of func, it will be passed along.
28+
29+
:Example:
30+
31+
>>> @dynamic_dispatch(default=True)
32+
>>> def foo(bar: int):
33+
>>> print(bar)
34+
>>>
35+
>>> @foo.dispatch(on=5)
36+
>>> def _(bar: int, baz: int):
37+
>>> print(bar * baz)
38+
>>>
39+
>>> @foo.dispatch(on=10)
40+
>>> def _():
41+
>>> print(-10)
42+
>>>
43+
>>> foo(1)
44+
1
45+
>>> foo(5, 10)
46+
50
47+
>>> foo(10)
48+
-10
49+
50+
:Example:
51+
52+
>>> @dynamic_dispatch(default=True)
53+
>>> class Foo:
54+
>>> def __init__(self, foo: int):
55+
>>> super().__init__()
56+
>>> print(bar)
57+
>>>
58+
>>> @Foo.dispatch(foo=5)
59+
>>> class Bar(Foo):
60+
>>> def __init__(self, foo, bar):
61+
>>> super().__init__(foo)
62+
>>> print(foo * bar)
63+
>>>
64+
>>> Foo(1)
65+
1
66+
<__main__.Foo object at ...>
67+
>>> Foo(5, 10)
68+
50
69+
<__main__.Bar object at ...>
70+
71+
:param func: class or function to add dynamic dispatch to.
72+
:param default: whether or not to use func as the default implementation.
73+
:returns: func with dynamic dispatch
74+
"""
75+
# Default was specified, wait until func is here too.
76+
if func is None:
77+
return functools.partial(dynamic_dispatch, default=default)
78+
79+
# Delegate depending on wrap type.
80+
if inspect.isclass(func):
81+
return class_dispatch(func, default)
82+
83+
func = func_dispatch(func, default=default)
84+
85+
# Alter register to hide implicit parameter.
86+
dispatch = func.dispatch
87+
88+
def replacement(impl: Callable = None, *, on: Hashable):
89+
if impl is None:
90+
return functools.partial(replacement, on=on)
91+
92+
return dispatch(impl, arguments=inspect.signature(impl).parameters, on=on)
93+
94+
# Type checker complains if we assign directly.
95+
setattr(func, 'dispatch', replacement)
96+
97+
return func

0 commit comments

Comments
 (0)