Skip to content

Commit

Permalink
Merge pull request #5 from brandonwillard/update-requirements-and-ver…
Browse files Browse the repository at this point in the history
…sioning

Update README, refactor project setup, CI testing, versioning, and requirements.
  • Loading branch information
brandonwillard committed Dec 22, 2019
2 parents 8bd80ba + 62b1520 commit a176cee
Show file tree
Hide file tree
Showing 13 changed files with 2,771 additions and 168 deletions.
1 change: 1 addition & 0 deletions .gitattributes
@@ -0,0 +1 @@
unification/_version.py export-subst
200 changes: 197 additions & 3 deletions .gitignore
@@ -1,6 +1,200 @@
*.pyc
# Created by https://www.gitignore.io/api/vim,emacs,python
# Edit at https://www.gitignore.io/?templates=vim,emacs,python

### Emacs ###
# -*- mode: gitignore; -*-
*~
\#*\#
/.emacs.desktop
/.emacs.desktop.lock
*.elc
auto-save-list
tramp
.\#*

# Org-mode
.org-id-locations
*_archive

# flymake-mode
*_flymake.*

# eshell files
/eshell/history
/eshell/lastdir

# elpa packages
/elpa/

# reftex files
*.rel

# AUCTeX auto folder
/auto/

# cask packages
.cask/
dist/

# Flycheck
flycheck_*.el

# server auth directory
/server/

# projectiles files
.projectile

# directory configuration
.dir-locals.el

# network security
/network-security.data


### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
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
.hypothesis/
.pytest_cache/
testing-report.html
.tox
*.egg-info

# 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

# celery beat schedule file
celerybeat-schedule

# 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/

### Vim ###
# Swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]

# Session
Session.vim
Sessionx.vim

# Temporary
.netrwhist
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~

# End of https://www.gitignore.io/api/vim,emacs,python
28 changes: 11 additions & 17 deletions .travis.yml
@@ -1,27 +1,21 @@
dist: xenial
language: python

matrix:
include:
- name: "Python 3.6 Lint"
python: 3.6
env: PYTHON_VERSION=3.6 NAME="LINT"
- name: "Python 3.6 Unit Test"
python: 3.6
env: PYTHON_VERSION=3.6 NAME="UNIT"
python:
- "3.6"
- "3.7"
- "3.8"
- "pypy3"

install:
- pip install -r requirements.txt
- pip install -r requirements-dev.txt
- if [[ $NAME == UNIT ]]; then pip install coveralls; fi
- pip freeze

script:
- if [[ $NAME == UNIT ]]; then pytest -v tests/ --cov=unification/; fi
- if [[ $NAME == LINT ]]; then make lint; fi
- pylint unification/ tests/
- if [[ `command -v black` ]]; then
black --check unification tests;
fi
- pytest -v tests/ --cov=unification/

after_success:
- echo $(pwd)
- ls -la
- if [[ $NAME == UNIT ]]; then sed -i 's/\/opt\/unification/\/home\/travis\/build\/brandonwillard\/unification/g' .coverage; fi
- if [[ $NAME == UNIT ]]; then coveralls; fi
- coveralls
2 changes: 2 additions & 0 deletions MANIFEST.in
@@ -0,0 +1,2 @@
include versioneer.py
include unification/_version.py
129 changes: 129 additions & 0 deletions README.md
@@ -0,0 +1,129 @@
# Logical Unification

[![Build Status](https://travis-ci.org/pythological/unification.svg?branch=master)](https://travis-ci.org/pythological/unification) [![Coverage Status](https://coveralls.io/repos/github/pythological/unification/badge.svg?branch=master)](https://coveralls.io/github/pythological/unification?branch=master) [![PyPI](https://img.shields.io/pypi/v/logical-unification)](https://pypi.org/project/logical-unification/)

Logical [`unification`](https://en.wikipedia.org/wiki/Unification_(computer_science)) in Python, extensible via dispatch.

## Examples

`unification` has built-in support for most Python data types:

```python
>>> from unification import *
>>> unify(1, 1)
{}
>>> unify(1, 2)
False
>>> x = var('x')
>>> unify((1, x), (1, 2))
{~x: 2}
>>> unify((x, x), (1, 2))
False
```

Custom classes can be made "unifiable" with the `unifiable` decorator:

```python
@unifiable
class Account(object):
def __init__(self, id, name, balance):
self.id = id
self.name = name
self.balance = balance

>>> data = [Account(1, 'Alice', 100),
Account(2, 'Bob', 0),
Account(2, 'Charlie', 0),
Account(2, 'Denis', 400),
Account(2, 'Edith', 500)]
>>> id, name, balance = var('id'), var('name'), var('balance')
>>> [unify(Account(id, name, balance), acct) for acct in data]
[{~name: 'Alice', ~balance: 100, ~id: 1},
{~name: 'Bob', ~balance: 0, ~id: 2},
{~name: 'Charlie', ~balance: 0, ~id: 2},
{~name: 'Denis', ~balance: 400, ~id: 2},
{~name: 'Edith', ~balance: 500, ~id: 2}]
>>> [unify(Account(id, name, 0), acct) for acct in data]
[False,
{~name: 'Bob', ~id: 2},
{~name: 'Charlie', ~id: 2},
False,
False]
```

`unification` also supports function dispatch through pattern matching:

```python
>> from unification.match import *
>>> n = var('n')

@match(0)
def fib(n):
return 0


@match(1)
def fib(n):
return 1


@match(n)
def fib(n):
return fib(n - 1) + fib(n - 2)

>>> map(fib, [0, 1, 2, 3, 4, 5, 6, 7, 8, 0])
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
```

The pattern matching can be fairly complex:

```python
>> name, amount = var('name'), var('amount')

@match({'status': 200, 'data': {'name': name, 'credit': amount}})
def respond(name, amount):
balance[name] += amount


@match({'status': 200, 'data': {'name': name, 'debit': amount}})
def respond(name, amount):
balance[name] -= amount


@match({'status': 404})
def respond():
print("Bad Request")

```

See the full example in the [examples directory](https://github.com/pythological/unification#examples).


## Performance and Reliability

Unification stresses extensibility over performance, preliminary benchmarks show that this is 2-5x slower than straight tuple-based unification.

`unification`'s approach is reliable; although one caveat is set unification, which is challenging to do in general. It should work well in moderately complex cases, but it may break down under very complex ones.

## Installation

Using `pip`:
```bash
pip install python-unification
```

To install from source:
```bash
git clone git@github.com:pythological/unification.git
cd unification
pip install -r requirements.txt
```

Tests can be run with the provided `Makefile`:
```bash
make check
```

## About

This project is a fork of [`unification`](https://github.com/mrocklin/unification/).

0 comments on commit a176cee

Please sign in to comment.