├── .gitignore ├── __init__.py ├── setup.py ├── tcall.py ├── README.md ├── dispatch.py ├── examples └── primes.py ├── fmap.py ├── docs ├── fmap_README.md └── pattern_match_README.md ├── prelude.py ├── pattern_match.py └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | from .fmap import fmap_for, on_keys 2 | from .dispatch import dispatch_on, instance 3 | from .pattern_match import pattern_match, pattern_matching 4 | from .tcall import tcall 5 | from .prelude import * 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import os 3 | 4 | # allow setup.py to be run from any path 5 | os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) 6 | 7 | setup( 8 | name='concepts', 9 | author="Innes Anderson-Morrison", 10 | author_email='innesdmorrison@gmail.com', 11 | install_requires=[], 12 | package_dir={'concepts': '.'}, 13 | packages=['concepts'], 14 | classifiers=[ 15 | 'Programming Language :: Python :: 3', 16 | 'Development Status :: 4 - Beta' 17 | ] 18 | ) 19 | -------------------------------------------------------------------------------- /tcall.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @tcall is a simple tail call optimisation decorator in pure python 3 | You _will_ loose stack frame information for debugging so be warned! 4 | ''' 5 | from functools import wraps 6 | 7 | 8 | def tcall(func): 9 | ''' 10 | Tail call optimise a function. This decorator will run your function 11 | in a while loop which can also call out to other functions. 12 | Any function being called in the tail call position _must_ return a 13 | tuple of (func, args, kwargs) in order to genreate the tail call: 14 | 15 | `func` is the next function to be called. Setting this to None 16 | will cause the value of args to be returned as the answer. 17 | `args` is a tuple of the next positional arguments to the 18 | function call. 19 | `kwargs` is an optional dictionary of keyword arguments for the 20 | next function call. 21 | ''' 22 | # stash the original function so we can use it in tail calls 23 | # NOTE: if the user returns "the original" we are actually getting the 24 | # decorated version. 25 | original = func 26 | 27 | @wraps(func) 28 | def wrapped(*args, **kwargs): 29 | f = func 30 | 31 | while f: 32 | result = f(*args, **kwargs) 33 | try: 34 | f, *result_or_args = result 35 | if not callable(f): 36 | # we just pulled apart a result 37 | return result 38 | except TypeError: 39 | # got a raw result so return it 40 | return result 41 | 42 | # Allow for chained tailcalling functions 43 | f = f._original if getattr(f, '_tcalling', None) else f 44 | 45 | if len(result_or_args) > 2 or len(result_or_args) == 0: 46 | raise IndexError( 47 | 'tcall functions must return (func, args, kwargs)' 48 | ' or (func, args)') 49 | 50 | try: 51 | args, kwargs = result_or_args 52 | except ValueError: 53 | args, kwargs = result_or_args[0], {} 54 | 55 | wrapped._tcalling = True 56 | wrapped._original = original 57 | 58 | return wrapped 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Concepts 2 | ### LISP and Haskell inspired functional programming concepts for Python 3 | 4 | Originally this was called **fmap** and was only an implementation of the `fmap` function for Python3. Now that I've been tinkering around some more I thought it'd be nice to collect a few things together. (For about a day it was then called `PythFun` but then I saw the error of my ways...) 5 | 6 | - [fmap](docs/fmap_README.md): apply a function to all elements of a collection style object. 7 | - [pattern_match](docs/pattern_match_README.md): A hybrid of Haskell's pattern matching and Clojure's destructuring. 8 | - [dispatch](dispatch.py): single and multiple dispatch for your Python functions. 9 | - [prelude](prelude.py): a collection of functional programming functions. 10 | 11 | Any suggestions for improvements are welcome and if you'd like to hack away and submit a pull request for a feature then raise an issue and let me know! 12 | 13 | I hope you enjoy! 14 | 15 | 16 | ### Some examples... 17 | ```python 18 | def fibgen(): 19 | '''Because EVERYONE needs a list of Fibonacci numbers...''' 20 | yield 1 21 | yield from iscanl(fibgen(), add, 2) 22 | 23 | >>> take(20, fibgen()) 24 | [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946] 25 | 26 | 27 | @tcall 28 | def fact(n, acc=1): 29 | '''And compute some factorials...''' 30 | if n == 0: 31 | return acc 32 | else: 33 | return fact, (n-1, acc*n) 34 | 35 | >>> fact(99) 36 | 9332621544394415268169923885626670049071596826438162146859296389521759999322991560894146397 37 | 61565182862536979208272237582511852109168640000000000000000000000 38 | 39 | >>> import sys 40 | 41 | >>> sys.getrecursionlimit() 42 | 2000 43 | 44 | >>> fact(9999) 45 | 284625968091705451890641321211986889014805140170279923079417999427... 46 | # This one is 35656 digits long... 47 | 48 | 49 | # From prelude.py 50 | def cmap(func, col): 51 | ''' 52 | Concat-Map: map a function that takes a value and returns a list over an 53 | iterable and concatenate the results 54 | ''' 55 | return foldl(map(func, col)) 56 | 57 | 58 | def flatten(col): 59 | ''' 60 | Flatten an arbitrarily nested list of lists into a single list. 61 | ''' 62 | if not iscollection(col): 63 | return [col] 64 | else: 65 | return cmap(flatten, col) 66 | 67 | 68 | >>> l = [1,2,[3,4,[5,6,7],[8,9]],[10,11,12],13] 69 | 70 | >>> flatten(l) 71 | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] 72 | ``` 73 | -------------------------------------------------------------------------------- /dispatch.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Adding simple single & multiple dispatch to Python. 3 | ``````````````````````````````````````````````````` 4 | 5 | Unlike functools.singledispatch, this will not work back through the MRO 6 | to find a matching superclass. Instead it will immediately resort to the 7 | default implementation. 8 | 9 | NOTE: *args and **kwargs are _not_ allowed in the signature of the 10 | function being defined. 11 | ''' 12 | from functools import wraps 13 | 14 | 15 | def dispatch_on(index=0, func=None): 16 | ''' 17 | Allow the implementation of a function to vary based on the type of 18 | of its arguments. 19 | Default is single dispatch on the first argument but you can supply 20 | any of the following as the key for implementation lookup: 21 | any single index --> dispatch_on(1) 22 | a tuple of indices --> dispatch_on((0,2)) 23 | all arguments --> dispatch_on('all') 24 | 25 | Once the decorated function has been defined, you can use 26 | .add() to register an implementation. 27 | 28 | NOTE: must match the form of the original specification 29 | used in @dispatch_on. 30 | 31 | If no implementation is found, then the decorated function is used 32 | as a default. 33 | ''' 34 | # A quick hack to allow using this as a decorator with arguments 35 | if func is None: 36 | return lambda f: dispatch_on(index, f) 37 | 38 | implementations = {} 39 | 40 | if index == 'all': 41 | multi = True 42 | # Horrible but correct so long as func does not use *args or **kwargs 43 | key_len = func.__code__.co_argcount 44 | elif type(index) == tuple: 45 | multi = True 46 | key_len = len(index) 47 | elif type(index) == int: 48 | multi = False 49 | else: 50 | raise ValueError("Invalid argument specification for dispatch") 51 | 52 | def add(key, func=None): 53 | ''' 54 | Add an implementation of func for the given key. The form of key 55 | must match the form given for index above. 56 | ''' 57 | # Same hack as before 58 | if func is None: 59 | return lambda f: add(key, f) 60 | 61 | if multi: 62 | if len(key) != key_len: 63 | raise TypeError( 64 | 'The base case takes {} parameters. ({} supplied)'.format( 65 | key_len, len(key))) 66 | 67 | implementations[key] = func 68 | return func 69 | 70 | @wraps(func) 71 | def wrapped(*args, **kwargs): 72 | ''' 73 | Attempt to use an implementation if there is one, 74 | otherwise use the default. 75 | ''' 76 | if multi: 77 | if index == 'all': 78 | dispatch_key = tuple(type(a) for a in args) 79 | else: 80 | dispatch_key = tuple(type(args[i]) for i in index) 81 | else: 82 | dispatch_key = type(args[index]) 83 | 84 | implementation = implementations.get(dispatch_key, func) 85 | return implementation(*args, **kwargs) 86 | 87 | wrapped.implementations = implementations 88 | wrapped.add = add 89 | return wrapped 90 | 91 | 92 | def instance(func, implementation, arg_type): 93 | ''' 94 | Register a function as the implementation of func for a given type. 95 | i.e instance(fmap, _fmap_my_type, my_type) 96 | 97 | This is an alternative to explicitly wrapping the function definition 98 | in question with @func.add(arg_type) to allow run-time registration 99 | and registration of pre-defined functions. 100 | ''' 101 | if 'dispatch' in dir(func): 102 | func.add(arg_type, implementation) 103 | else: 104 | raise TypeError( 105 | '{} has not been decorated with `dispatch_on`.'.format(func) 106 | ) 107 | -------------------------------------------------------------------------------- /examples/primes.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Some toy examples of composing pure functions involving primes. 3 | ''' 4 | import numpy as np 5 | import pandas as pd 6 | from math import sqrt 7 | from sys import getsizeof 8 | import matplotlib.pyplot as plt 9 | from collections import defaultdict 10 | from concepts import takewhile, itakewhile, foldl, mul, itake, scanl 11 | 12 | 13 | def primes(): 14 | '''Generate an infinite stream of primes''' 15 | sieve = defaultdict(list) 16 | k = 2 17 | while True: 18 | k_factors = sieve.get(k) 19 | if k_factors: 20 | del sieve[k] 21 | for f in k_factors: 22 | sieve[f+k].append(f) 23 | else: 24 | yield k 25 | sieve[k ** 2] = [k] 26 | k += 1 27 | 28 | 29 | def factorise(n, plist=None): 30 | '''factorise an integer''' 31 | if n == 1: 32 | return [1] 33 | 34 | factors = [] 35 | plist = plist if plist is not None else primes() 36 | 37 | for p in itakewhile(lambda x: x < int(sqrt(n)) + 1, plist): 38 | while n % p == 0: 39 | factors.append(p) 40 | n /= p 41 | if n == 1: 42 | break 43 | if n != 1: 44 | factors.append(int(n)) 45 | 46 | return factors 47 | 48 | 49 | def filter_factors(func, ubound): 50 | # Grab all of the primes that we'll need once so we can reuse them 51 | plist = takewhile(lambda x: x <= ubound, primes()) 52 | 53 | return list(map(lambda k: foldl(k, mul), 54 | filter(lambda l: func(l), 55 | map(lambda c: factorise(c, plist), range(ubound))))) 56 | 57 | 58 | def n_or_more_factors(n, ubound=5000): 59 | '''Find all integers less than ubound with at least n factors''' 60 | return filter_factors(lambda l: len(l) >= n, ubound) 61 | 62 | 63 | def n_or_less_factors(n, ubound=5000): 64 | '''Find all integers less than ubound with less than n factors''' 65 | return filter_factors(lambda l: 1 < len(l) <= n, ubound) 66 | 67 | 68 | def n_factors(n, ubound=5000): 69 | '''Find all integers less than ubound with n factors''' 70 | return filter_factors(lambda l: len(l) == n, ubound) 71 | 72 | 73 | def debug_primes(): 74 | ''' 75 | Generate an infinite stream of primes and log out the size of 76 | the sieve as it increases 77 | ''' 78 | max_sieve_size = 0 79 | sieve_size = 0 80 | 81 | sieve = defaultdict(list) 82 | k = 2 83 | 84 | while True: 85 | k_factors = sieve.get(k) 86 | if k_factors: 87 | del sieve[k] 88 | for f in k_factors: 89 | sieve[f+k].append(f) 90 | sieve_size = getsizeof(sieve) 91 | if sieve_size > max_sieve_size: 92 | max_sieve_size = sieve_size 93 | print('{}\tincrease: {} bytes'.format(k, max_sieve_size)) 94 | else: 95 | yield k 96 | sieve[k ** 2] = [k] 97 | k += 1 98 | 99 | 100 | def by_factors(lbound=1, ubound=100): 101 | plist = takewhile(lambda x: x <= ubound, primes()) 102 | return list(map( 103 | lambda k: {'n': foldl(k, mul), 104 | 'factors': k, 105 | 'n_factors': len(k), 106 | 'n_distinct_factors': len(set(k))}, 107 | map(lambda c: factorise(c, plist), range(lbound, ubound)))) 108 | 109 | 110 | def first_largest_distinct(n): 111 | ''' 112 | Find the first number that has k distinct factors for 1 <= k <= n 113 | ''' 114 | return scanl(itake(n, primes()), mul) 115 | 116 | 117 | def get_df(lbound=1, ubound=100): 118 | df = pd.DataFrame(by_factors(lbound, ubound)) 119 | df.set_index('n', inplace=True) 120 | return df 121 | 122 | 123 | def plot_factors(df): 124 | df['n_factors'].plot(kind='line') 125 | df['n_distinct_factors'].plot(kind='line') 126 | plt.show() 127 | 128 | 129 | def plot_by_num_factors(df, unique=False, logy=False): 130 | column = 'n_distinct_factors' if unique else 'n_factors' 131 | fig = plt.figure() 132 | ax = fig.add_subplot(1, 1, 1) 133 | for n_fact in df[column].unique(): 134 | n_df = df[df[column] == n_fact] 135 | n_df['count'] = np.arange(len(n_df)) 136 | n_df.plot(x='n', y='count', ax=ax, logy=logy, 137 | label='{} factors'.format(n_fact)) 138 | plt.show() 139 | -------------------------------------------------------------------------------- /fmap.py: -------------------------------------------------------------------------------- 1 | from types import GeneratorType 2 | from collections import Iterator, deque, defaultdict, \ 3 | Counter, OrderedDict, ChainMap 4 | 5 | from .dispatch import dispatch_on 6 | 7 | 8 | @dispatch_on(index=1) 9 | def fmap(func, col): 10 | ''' 11 | Map a function over the elements of a container. 12 | If no specific implementation is found for the supplied type then 13 | a TypeError is raised along with a description of how to add a case. 14 | 15 | NOTE: As with normal _map_, _func_ must be a function that takes 16 | a single argument. (Dictionaries are a special case, see below) 17 | 18 | Supported collection types: 19 | ``````````````````````````` 20 | list, set, tuple, dict, bytes, bytarray, str, generator, range 21 | - fmapping over an empty collection will return 22 | an empty collection of the same type. 23 | - fmapping over None returns None. 24 | - For dictionaries there are two ways to use fmap: 25 | fmap(func, dict) map over the _values_ 26 | fmap(on_keys(func), dict) map over the _keys_ 27 | fmap(func, dict) map over both if func accepts and returns 28 | two values 29 | ''' 30 | # Allow raw iterators to be mapped over and return an iterator here as 31 | # there is no single `iterator` type to check against. 32 | if isinstance(col, Iterator): 33 | for element in col: 34 | yield func(element) 35 | else: 36 | # Everything else requires its own definition 37 | msg = ('fmap is not currently defined for {t}.\n To add a' 38 | ' definition, use the @fmap_for({t}) decorator.\n' 39 | ' (See the fmap README for examples)') 40 | raise TypeError(msg.format(t=type(col))) 41 | 42 | 43 | fmap_for = fmap.add 44 | 45 | 46 | ######################################################################### 47 | # Helpers for fmapping over dicts: apply to the function being fmapped: # 48 | # i.e. fmap(on_keys(times2), {str(n): n for n in range(10)}) # 49 | # fmap(on_values(times2), {str(n): n for n in range(10)}) # 50 | ######################################################################### 51 | def on_keys(func): 52 | def composed(key, value): 53 | return func(key), value 54 | return composed 55 | 56 | 57 | def on_values(func): 58 | def composed(key, value): 59 | return key, func(value) 60 | return composed 61 | 62 | 63 | ###################################### 64 | # Implementations for built in types # 65 | ###################################### 66 | @fmap_for(type(None)) 67 | def _fmap_none(func, n): 68 | return None 69 | 70 | 71 | @fmap_for(range) 72 | @fmap_for(GeneratorType) 73 | def _fmap_lazy_seq(func, s): 74 | for element in s: 75 | yield func(element) 76 | 77 | 78 | @fmap_for(list) 79 | def _fmap_list(func, l): 80 | return list(map(func, l)) 81 | 82 | 83 | @fmap_for(tuple) 84 | def _fmap_tuple(func, t): 85 | return tuple(map(func, t)) 86 | 87 | 88 | @fmap_for(set) 89 | def _fmap_set(func, s): 90 | return set(map(func, s)) 91 | 92 | 93 | @fmap_for(dict) 94 | def _fmap_dict(func, d): 95 | ''' 96 | If this is a function on single values, apply it to 97 | the values in the dictionary. Otherwise, assume that 98 | it is a function that takes a tuple of two values and 99 | returns a tuple of two values. 100 | (Also see `on_values` and `on_keys`) 101 | ''' 102 | if func.__code__.co_argcount == 1: 103 | func = on_values(func) 104 | fmapped = (func(k, v) for k, v in d.items()) 105 | return {k: v for k, v in fmapped} 106 | 107 | 108 | @fmap_for(str) 109 | def _fmap_str(func, s): 110 | return ''.join(map(func, s)) 111 | 112 | 113 | @fmap_for(bytes) 114 | def _fmap_bytes(func, b): 115 | return bytes(map(func, b)) 116 | 117 | 118 | @fmap_for(bytearray) 119 | def _fmap_bytearray(func, b): 120 | return bytearray(map(func, b)) 121 | 122 | 123 | # Implementations for the collections module 124 | @fmap_for(deque) 125 | def _fmap_deque(func, d): 126 | return deque(map(func, d)) 127 | 128 | 129 | @fmap_for(Counter) 130 | def _fmap_counter(func, c): 131 | if func.__code__.co_argcount == 1: 132 | func = on_values(func) 133 | 134 | fmapped = (func(k, v) for k, v in c.items()) 135 | return Counter({k: v for k, v in fmapped}) 136 | 137 | 138 | @fmap_for(OrderedDict) 139 | def _fmap_ordered_dict(func, o): 140 | if func.__code__.co_argcount == 1: 141 | func = on_values(func) 142 | 143 | fmapped = (func(k, v) for k, v in o.items()) 144 | return OrderedDict(fmapped) 145 | 146 | 147 | @fmap_for(ChainMap) 148 | def _fmap_chain_map(func, c): 149 | if func.__code__.co_argcount == 1: 150 | func = on_values(func) 151 | 152 | fmapped = [] 153 | for m in c.maps: 154 | fmapped.append(dict(func(k, v) for k, v in c.items())) 155 | return ChainMap(fmapped) 156 | 157 | 158 | @fmap_for(defaultdict) 159 | def _fmap_default_dict(func, d): 160 | if func.__code__.co_argcount == 1: 161 | func = on_values(func) 162 | 163 | fmapped = (func(k, v) for k, v in d.items()) 164 | new_d = defaultdict(d.default_factory) 165 | new_d.update(fmapped) 166 | return new_d 167 | -------------------------------------------------------------------------------- /docs/fmap_README.md: -------------------------------------------------------------------------------- 1 | # fmap.py - a single dispatch version of fmap for Python3 2 | 3 | *While there are multiple Haskellesque 'lets put monads in Python!' style libraries out there, most don't seem to focus 4 | on taking the nice bits of Haskell's functional approach and giving them a nice Pythonic interface.
`fmap.py` is a very simple take on `fmap` that lets you remove some unnecesary boiler plate when you are applying a function to each 5 | element of a collection. I hope you like it!* 6 | 7 | 8 | ### They say fmap is an operation on Functors... 9 | "What's a Functor?!", I hear you cry. 10 | 11 | Well, the Haskell docs have this to say on the matter: 12 | ``` 13 | Functors: 14 | Uniform action over a parameterized type, generalizing the map function on lists. 15 | The Functor class is used for types that can be mapped over. Instances of Functor 16 | should satisfy the following laws: 17 | 18 | class Functor f where 19 | fmap id == id 20 | fmap (f . g) == fmap f . fmap g 21 | ``` 22 | 23 | Of course...thanks for that. Now I know exactly what's going on... 24 | 25 | The important part is the second half of the first sentence: `"generalizing the map function on lists."` 26 | 27 | ### And that's all there is to it! 28 | 29 | Python's map returns an iterator that requires you to some boiler plate to get a concrete data structure out again.
30 | This is fine (if annoying and ugly) when you know - with 100% certainty - what you are mapping over. However, if
31 | the argument you are passing to map could be one of several thing then you are in trouble... 32 | 33 | What data structure do you build at the end? 34 | 35 | ### fmap ensures that - for the defined datatypes - you get back what you put in: 36 | ```python 37 | def times2(x): 38 | return x * 2 39 | 40 | # With a list this looks exactly the same as the less pleasing list(map(times2, [1,2,3,4,5])) 41 | fmap(times2, [1,2,3,4,5]) 42 | >>> [2, 4, 6, 8, 10] 43 | 44 | # Works for sets 45 | fmap(times2, {1,2,3,4,5}) 46 | >>> {8, 2, 10, 4, 6} 47 | 48 | # And tuples 49 | fmap(times2, (1,2,3,4,5)) 50 | >>> (2, 4, 6, 8, 10) 51 | 52 | # Strings are a sequence so we can fmap over them as well 53 | fmap(times2, "ffmap me!") 54 | >>> 'ffmmaapp mmee!!' 55 | 56 | # Even bytes! (also bytearrays) 57 | fmap(times2, bytes(range(1, 6))) 58 | >>> b'\x02\x04\x06\x08\n' 59 | 60 | # Dicts are fiddly so there are some helpers. 61 | # By default you fmap over the values: 62 | fmap(times2, {str(n): n for n in range(1, 6)}) 63 | >>> {'2': 4, '1': 2, '5': 10, '3': 6, '4': 8} 64 | 65 | # To fmap over the keys, wrap the function in `on_keys`: 66 | fmap(on_keys(times2), {str(n): n for n in range(1, 6)}) 67 | >>> {'33': 3, '11': 1, '44': 4, '22': 2, '55': 5} 68 | 69 | # Or, if the function takes two values and returns two values you can use it 70 | # directly: 71 | def values_are_awesome(a, b): 72 | return a, 'Awesome!' 73 | 74 | fmap(values_are_awesome, {str(n): n for n in range(1, 6)}) 75 | >>> {'2': 'Awesome!', '1': 'Awesome!', '5': 'Awesome!', '3': 'Awesome!', '4': 'Awesome!'} 76 | 77 | 78 | # Pass in an iterator, range or generator and you'll get out a new generator 79 | fmap(times2, iter([1,2,3,4,5])) 80 | >>> . at 0x7f1511028eb8> 81 | 82 | # fmap applied to None is None...are you surprised? 83 | fmap(times2, None) 84 | >>> 85 | ``` 86 | 87 | #### If you want to use a different data type (including your own user defined classes!) all you need to do is the following. We'll use a (very) simple binary tree class as our example: 88 | 89 | ```python 90 | from concepts import fmap, fmap_for 91 | 92 | 93 | class Btree: 94 | def __init__(self, val, left=None, right=None): 95 | self.val = val 96 | self.left = left 97 | self.right = right 98 | 99 | def __repr__(self): 100 | return str((self.val, self.left, self.right)) 101 | 102 | 103 | @fmap_for(Btree) 104 | def _fmap_btree(func, t): 105 | return Btree(func(t.val), fmap(func, t.left), fmap(func, t.right)) 106 | 107 | 108 | # Lets try it out! 109 | >>> b = Btree(4, Btree(3, Btree(2)), Btree(1, None, Btree(0))) 110 | >>> b 111 | (4, (3, (2, None, None), None), (1, None, (0, None, None))) 112 | >>> b.left 113 | (3, (2, None, None), None) 114 | >>> b.right 115 | (1, None, (0, None, None)) 116 | 117 | >>> fmap(times2, b) 118 | (8, (6, (4, None, None), None), (2, None, (0, None, None))) 119 | ``` 120 | 121 | Or if you prefer using a function instead of a decorator (which also allows you to 122 | register pre-defined functions): 123 | 124 | ```python 125 | from concepts import fmap, instance 126 | 127 | instance(fmap, _fmap_btree, Btree) 128 | ``` 129 | 130 | And that's it! 131 | 132 | You may now fmap away to your heart's content. 133 | 134 | See the awesome and fun [LearnYouAHaskell](http://learnyouahaskell.com/functors-applicative-functors-and-monoids) 135 | for some more details on the Haskell implementation and theory behind functors if you're into that sort of thing. 136 | 137 | 138 | ### A note on implementation 139 | Originally this was implemented using the `functools.singledispatch` decorator but that caused a couple of issues due its use of the MRO to find implementations for subclasses. As a result, I have written a (much simpler and less powerful) version that allows you to dispatch on a chosen argument index or on the types of all arguments. This can be found in dispatch.py if you are interested. 140 | -------------------------------------------------------------------------------- /docs/pattern_match_README.md: -------------------------------------------------------------------------------- 1 | ## Pattern matching in Python as a context manager and decorator 2 | 3 | Pattern matching is awesome. Python has tuple unpacking that can be used to 4 | do some simple pattern matching style operations but unless you want to get 5 | really involved with `try/except` and a lot of fiddling around, it's not 6 | as powerful as it could be. 7 | 8 | More importantly, when you get to that stage your code looks horrible! 9 | To quote the great Raymond: 10 | 11 | "There must be a better way!" 12 | 13 | 14 | ### Enter 'with pattern_match' and '@pattern_matching' 15 | You have a choice of using the `with patter_match(foo) as m: ...` context manager 16 | inside normal Python code or to decorate a function definition with the 17 | `@pattern_matching` decorator that allow you to match against all named parameters 18 | passed to the function by handling the setup of the multi-part with statement for you. 19 | - When using the decorator version, every function parameter gains a partner that is 20 | prefixed with an underscore. (`bar` and `_bar`) 21 | - The parameter can be used as normal but the underscore version is a special match 22 | object that can be used for pattern matching! 23 | - When using the context manager, you get to set the name of the match object using 24 | the `as` clause of the context manager. 25 | 26 | #### A difference in Syntax 27 | When using their context manager, I have (so far) been unable to reliably bind new 28 | variables at runtime. As a result, accessing the results of a match is done using 29 | dict style lookup on the match object (see the example!).
30 | If you use the decorator however, you can access any bound pattern variables 31 | as if they were defined where they are matched. (For details on how this works, have a 32 | look at the source: this is _highly_ cPython specific so far but it should be possible 33 | to extend this. 34 | 35 | ### Match objects are cool! 36 | You can use them as ofter as you want and they can be tested against types using 37 | `>=` or against match templates using `>>`. 38 | - When you test using `>>`, a successful match will bind the variables used in the 39 | template into local scope so you can use them in the rest of your code! 40 | 41 | 42 | #### Templates are tuples of variable names without commas: 43 | In order to specify your match templates, you need to use a *(small)* DSL to describe 44 | the pattern you are looking for: 45 | 46 | `template_example = '(a b c *d (_ f) ...)'` 47 | 48 | The full rules are given below but the 10 second summary is as follows: 49 | - Templates are strings of tuples without commas (to reduce the line noise and save you 50 | some key strokes). 51 | - A template must be a *single* string-tuple (or 'struple' if you like) but it can contain 52 | arbitrary nested sub-templates. (aka 'sub-struples'...) 53 | - Within a template you specify variable names that must be valid Python variable names as 54 | they are going to be bound following a successful match. 55 | - Note that if the same name is used in multiple places, it must match the same *value* 56 | each time for the match to succeed as a whole. 57 | - There are a couple of special elements that you can use that give you some more powerful 58 | matching potential. For those, read on! 59 | 60 | 61 | #### Allowed values in a template and what they do 62 | (...): A template must start and stop with parens. 63 | It may also include any number of nested sub-templates. 64 | : Any valid python variable name is allowed. 65 | These are the names that will be bound into the local scope 66 | on the result of a successful match. 67 | *: Any variable name that starts with a single * is marked as 68 | being greedy. It will consume all remaining elements up to 69 | a sub-template in the same way as Python's native tuple 70 | unpacking. 71 | NOTE: You can have a maximum of one greedy variable per 72 | template or sub-template. 73 | 74 | #### Special values and their meanings 75 | _: Underscore is a special element in a pattern. It denotes a 76 | required position in the template that must be filled but 77 | the result of the match is not bound. You can have any number 78 | of Underscores in a template. 79 | ...: Ellipsis is only valid when following a sub-template. This 80 | causes the sub-template to be repeated greedily to the end of 81 | the template, combining successful matches in a list for each 82 | variable. 83 | i.e. `((a b) ...) >> [[1, 2], [3, 4], [5, 6]]` 84 | will give: `a = [1, 3, 5]` 85 | `b = [2, 4, 6]` 86 | 87 | 88 | ## And now for an example! 89 | ```python 90 | from concepts import pattern_match, pattern_matching 91 | 92 | 93 | examples = [ 94 | [1, 2, 3, 4, (5, 6), (7, 8), (9, 10)], 95 | [1, 2, 3, 2, 1], 96 | [1, 2, 3, 2, 42], 97 | 'exactly this' 98 | ] 99 | 100 | for example in examples: 101 | print('\nTrying to match, {}'.format(example)) 102 | 103 | with pattern_match(example) as m: 104 | if m >> '(*a (b c) ...)': 105 | print( 106 | 'This example starts with {} and then has a list' 107 | ' of pairs where the first elements are {} and' 108 | ' the second elements are {}.'.format(m['a'], m['b'], m['c'])) 109 | elif m >> '(x y z y x)': 110 | print('This one is a palindrome!') 111 | elif m >= list: 112 | print("Well...it's a list! Beyond that I'm not sure...") 113 | elif m == 'exactly this': 114 | print('Exact matches work as well!') 115 | else: 116 | print('Failed matches are silent...') 117 | 118 | 119 | >>> Trying to match, [1, 2, 3, 4, (5, 6), (7, 8), (9, 10)] 120 | >>> This example starts with [1, 2, 3, 4] and then has a list of pairs 121 | where the first elements are [5, 7, 9] and the second elements are [6, 8, 10]. 122 | >>> 123 | >>> Trying to match, [1, 2, 3, 2, 1] 124 | >>> This one is a palindrome! 125 | >>> 126 | >>> Trying to match, [1, 2, 3, 2, 42] 127 | >>> Well...it's a list! Beyond that I'm not sure... 128 | >>> 129 | >>> Trying to match, exactly this 130 | >>> Exact matches work as well! 131 | 132 | 133 | # Alternatively, use the @pattern_matching decorator to match on the arguments to 134 | # a function. This also gives you the pattern variables in local scope. 135 | 136 | from concepts import pattern_matching 137 | 138 | @pattern_matching 139 | def foo(a): 140 | if _a >> '(x *y)': 141 | print('y is ', y) 142 | 143 | foo([1, 2, 3, 4, 5]) 144 | >>> y is [2, 3, 4, 5] 145 | ``` 146 | -------------------------------------------------------------------------------- /prelude.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Some common functional programming (and just useful) functions 3 | inspired by the likes of Clojure, Haskell and LISP. 4 | 5 | NOTE: There is a naming convension of i returning an 6 | iterator and returning a collection. 7 | ''' 8 | from collections import Container 9 | import itertools as itools 10 | import functools as ftools 11 | from copy import deepcopy 12 | import operator as op 13 | 14 | # Bring in functionality from the other modules 15 | from .dispatch import dispatch_on 16 | from .fmap import fmap 17 | 18 | 19 | ############################################################# 20 | # Make some library functions available without namespacing # 21 | ############################################################# 22 | combs_wr = itools.combinations_with_replacement 23 | combs = itools.combinations 24 | perms = itools.permutations 25 | itakewhile = itools.takewhile 26 | idropwhile = itools.dropwhile 27 | groupby = itools.groupby 28 | cprod = itools.product 29 | mask = itools.compress # compress is a terrible name for this! 30 | repeat = itools.repeat 31 | chain = itools.chain 32 | tee = itools.tee 33 | 34 | # Functional built-ins also include: 35 | # map filter lambda 36 | reduce = ftools.reduce 37 | # NOTE: having a shorter name for partial makes it a lot nicer 38 | # to work with in nested function calls. 39 | partial = fn = ftools.partial 40 | 41 | add = op.add 42 | sub = op.sub 43 | mul = op.mul 44 | div = op.truediv 45 | floordiv = op.floordiv 46 | 47 | # Flipping the argument order for comparisons because: 48 | # 1) easier currying/partial application 49 | # 2) as a function call it reads better as lt(x, y) == "is x less than y?" 50 | lt = lambda a, b: op.lt(b, a) 51 | le = lambda a, b: op.le(b, a) 52 | gt = lambda a, b: op.gt(b, a) 53 | ge = lambda a, b: op.ge(b, a) 54 | eq = op.eq 55 | neq = op.ne 56 | 57 | 58 | #################### 59 | # Helper functions # 60 | #################### 61 | def iscol(x): 62 | ''' 63 | Allow distinguishing between string types and "true" containers 64 | ''' 65 | if isinstance(x, Container): 66 | if not isinstance(x, (str, bytes)): 67 | return True 68 | return False 69 | 70 | 71 | ########################## 72 | # Higher order functions # 73 | ########################## 74 | def zipwith(func): 75 | ''' 76 | Returns a function that will combine elements of a zip using func. 77 | `func` must be a binary operation. 78 | ''' 79 | def zipper(*iterables): 80 | return [reduce(func, elems) for elems in zip(*iterables)] 81 | 82 | return zipper 83 | 84 | 85 | def izipwith(func): 86 | ''' 87 | Returns a function that will combine elements of a zip using func. 88 | `func` must be a binary operation. 89 | ''' 90 | def izipper(*iterables): 91 | for elems in zip(*iterables): 92 | yield reduce(func, elems) 93 | 94 | return izipper 95 | 96 | 97 | def compose(f, g): 98 | '''Create a new function from calling f(g(*args, **kwargs))''' 99 | def composition(*args, **kwargs): 100 | return f(g(*args, **kwargs)) 101 | 102 | doc = ('The composition of calling {} followed by {}:\n' 103 | '>>> {}\n"{}"\n\n>>> {}\n"{}"') 104 | fname = f.__name__ 105 | fdoc = f.__doc__ if f.__doc__ else 'No docstring for {}'.format(fname) 106 | gname = g.__name__ 107 | gdoc = g.__doc__ if g.__doc__ else 'No docstring for {}'.format(gname) 108 | composition.__doc__ = doc.format(fname, gname, fname, fdoc, gname, gdoc) 109 | 110 | return composition 111 | 112 | 113 | def flip(func): 114 | '''Flip the arguments to a binary operation''' 115 | if getattr(func, '_before_flip', None): 116 | # Don't nest function calls for flip(flip(func)) 117 | return func._before_flip 118 | 119 | def flipped(a, b): 120 | return func(b, a) 121 | 122 | flipped._before_flip = func 123 | return flipped 124 | 125 | 126 | def revargs(func): 127 | '''reverse the positional argument order of a function''' 128 | pass 129 | 130 | 131 | ################################################ 132 | # Reductions and functions that return a value # 133 | ################################################ 134 | def nth(n, col): 135 | ''' 136 | Return the nth element of a generator 137 | ''' 138 | col = iter(col) 139 | for k in range(n): 140 | try: 141 | element = next(col) 142 | except StopIteration: 143 | raise IndexError 144 | return element 145 | 146 | 147 | def foldl(col, func=add, acc=None): 148 | ''' 149 | Fold a list into a single value using a binary function. 150 | NOTE: This is just an alias for reduce with a reordered signature 151 | Python's reduce is reduce(func, col, acc) which looks wrong to me...! 152 | ''' 153 | if acc is not None: 154 | return reduce(func, col, acc) 155 | else: 156 | return reduce(func, col) 157 | 158 | 159 | def foldr(col, func=add, acc=None): 160 | ''' 161 | Fold a list with a given binary function from the right 162 | NOTE: Right folds and scans will blow up for infinite generators! 163 | ''' 164 | try: 165 | col = reversed(col) 166 | except TypeError: 167 | col = reversed(list(col)) 168 | 169 | if acc is not None: 170 | return reduce(func, col, acc) 171 | else: 172 | return reduce(func, col) 173 | 174 | 175 | def dotprod(v1, v2): 176 | ''' 177 | Compute the dot product of two "vectors" 178 | ''' 179 | if len(v1) != len(v2): 180 | raise IndexError('v1 and v2 must be the same length') 181 | 182 | return sum(map(mul, v1, v2)) 183 | 184 | 185 | def all_equal(iterable): 186 | ''' 187 | Returns True if all the elements in the iterable are the same 188 | ''' 189 | # Taken from the Itertools Recipes section in the docs 190 | # If everything is equal then we should only have one group 191 | g = groupby(iterable) 192 | return next(g, True) and not next(g, False) 193 | 194 | 195 | ################################################## 196 | # Functions that return a collection or iterator # 197 | ################################################## 198 | def take(n, col): 199 | ''' 200 | Return the up to the first n items from a generator 201 | ''' 202 | return list(itools.islice(col, n)) 203 | 204 | 205 | def itake(n, col): 206 | ''' 207 | Return the up to the first n items from a generator 208 | ''' 209 | for element in itools.islice(col, n): 210 | yield element 211 | 212 | 213 | def takewhile(predicate, col): 214 | ''' 215 | Take elements from a collection while the predicate holds. 216 | Return a list of those elements 217 | ''' 218 | return list(itakewhile(predicate, col)) 219 | 220 | 221 | def dropwhile(predicate, col): 222 | ''' 223 | Drop elements from a collection while the predicate holds. 224 | Return a list of those elements that are left 225 | ''' 226 | return list(idropwhile(predicate, col)) 227 | 228 | 229 | def drop(n, col): 230 | ''' 231 | Drop the first n items from a collection and then return the rest 232 | ''' 233 | try: 234 | # Allows for the same call to run against an iterator or collection 235 | return col[n:] 236 | except TypeError: 237 | # This is an iterator: take and discard the first n values 238 | for k in range(n): 239 | try: 240 | next(col) 241 | except StopIteration: 242 | return [] 243 | return list(col) 244 | 245 | 246 | def idrop(n, col): 247 | ''' 248 | Drop the first n items from a collection and then return a generator that 249 | yields the rest of the elements. 250 | ''' 251 | try: 252 | # Allows for the same call to run against an iterator or collection 253 | return (c for c in col[n:]) 254 | except TypeError: 255 | # This is an iterator: take and discard the first n values 256 | for k in range(n): 257 | try: 258 | next(col) 259 | except StopIteration: 260 | return col 261 | return col 262 | 263 | 264 | def scanl(col, func=add, acc=None): 265 | ''' 266 | Fold a collection from the left using a binary function 267 | and an accumulator into a list of values 268 | ''' 269 | if acc is not None: 270 | col = chain([acc], col) 271 | 272 | return list(itools.accumulate(col, func)) 273 | 274 | 275 | def iscanl(col, func=add, acc=None): 276 | ''' 277 | Fold a collection from the left using a binary function 278 | and an accumulator into a stream of values 279 | ''' 280 | if acc is not None: 281 | col = chain([acc], col) 282 | 283 | for element in itools.accumulate(col, func): 284 | yield element 285 | 286 | 287 | def scanr(col, func=add, acc=None): 288 | ''' 289 | Use a given accumulator value to build a list of values obtained 290 | by repeatedly applying acc = func(next(list), acc) from the right. 291 | 292 | WARNING: Right folds and scans will blow up for infinite generators! 293 | ''' 294 | try: 295 | col = reversed(col) 296 | except TypeError: 297 | col = reversed(list(col)) 298 | 299 | if acc is not None: 300 | col = chain([acc], col) 301 | 302 | return list(itools.accumulate(col, func)) 303 | 304 | 305 | def iscanr(col, func=add, acc=None): 306 | ''' 307 | Use a given accumulator value to build a stream of values obtained 308 | by repeatedly applying acc = func(next(list), acc) from the right. 309 | 310 | WARNING: Right folds and scans will blow up for infinite generators! 311 | ''' 312 | try: 313 | col = reversed(col) 314 | except TypeError: 315 | col = reversed(list(col)) 316 | 317 | if acc is not None: 318 | col = chain([acc], col) 319 | 320 | for element in itools.accumulate(col, func): 321 | yield element 322 | 323 | 324 | def windowed(iterable, n): 325 | ''' 326 | Take successive n-tuples from an iterable using a sliding window 327 | ''' 328 | # Take n copies of the iterable 329 | iterables = tee(iterable, n) 330 | 331 | # Advance each to the correct starting position 332 | for step, it in enumerate(iterables): 333 | for s in range(step): 334 | next(it) 335 | 336 | # Zip the modified iterables and build a list of the result 337 | # NOTE: not using zip longest as we want to stop when we reach the end 338 | return list(zip(*iterables)) 339 | 340 | 341 | def iwindowed(iterable, n): 342 | ''' 343 | Take successive n-tuples from an iterable using a sliding window 344 | ''' 345 | # Take n copies of the iterable 346 | iterables = tee(iterable, n) 347 | 348 | # Advance each to the correct starting position 349 | for step, it in enumerate(iterables): 350 | for s in range(step): 351 | next(it) 352 | 353 | # Zip the modified iterables and yield the elements as a genreator 354 | # NOTE: not using zip longest as we want to stop when we reach the end 355 | for t in zip(*iterables): 356 | yield t 357 | 358 | 359 | def chunked(iterable, n, fillvalue=None): 360 | ''' 361 | Split an iterable into fixed-length chunks or blocks 362 | ''' 363 | it = iter(iterable) 364 | chunks = itools.zip_longest(*[it for _ in range(n)], fillvalue=fillvalue) 365 | return list(chunks) 366 | 367 | 368 | def ichunked(iterable, n, fillvalue=None): 369 | ''' 370 | Split an iterable into fixed-length chunks or blocks 371 | ''' 372 | it = iter(iterable) 373 | chunks = itools.zip_longest(*[it for _ in range(n)], fillvalue=fillvalue) 374 | 375 | for chunk in chunks: 376 | yield chunk 377 | 378 | 379 | def cmap(func, col): 380 | ''' 381 | Concat-Map: map a function that takes a value and returns a list over an 382 | iterable and concatenate the results 383 | ''' 384 | return foldl(map(func, col)) 385 | 386 | 387 | def flatten(col): 388 | ''' 389 | Flatten an arbitrarily nested list of lists into a single list. 390 | ''' 391 | if not iscol(col): 392 | return [col] 393 | else: 394 | return cmap(flatten, col) 395 | 396 | 397 | def iflatten(col): 398 | ''' 399 | Flatten an arbitrarily nested list of lists into an iterator of 400 | single values. 401 | ''' 402 | if not iscol(col): 403 | yield col 404 | else: 405 | for sub_col in col: 406 | if not iscol(col): 407 | yield col 408 | else: 409 | yield from iflatten(sub_col) 410 | 411 | 412 | ################################# 413 | # Overloaded dispatch functions # 414 | ################################# 415 | @dispatch_on(index=1) 416 | def conj(head, tail): 417 | ''' 418 | Prepend an element to a collection, returning a new copy 419 | Exact behaviour will differ depending on the collection 420 | ''' 421 | tail_type = type(tail) 422 | return op.concat(tail_type([head]), tail) 423 | 424 | 425 | @conj.add(dict) 426 | def _conj_dict(head, tail): 427 | k, v = head # Allow exception to raise here if this doesn't work 428 | new = deepcopy(tail) 429 | new.update({k: v}) 430 | return new 431 | 432 | 433 | @conj.add(set) 434 | def _conj_set(head, tail): 435 | new = deepcopy(tail) 436 | new.update({head}) 437 | return new 438 | -------------------------------------------------------------------------------- /pattern_match.py: -------------------------------------------------------------------------------- 1 | from copy import copy 2 | from sys import _getframe 3 | from functools import wraps 4 | from collections import Container 5 | from inspect import getfullargspec 6 | from contextlib import contextmanager 7 | from types import CodeType, FunctionType 8 | from ctypes import c_int, pythonapi, py_object 9 | from itertools import chain, zip_longest, takewhile 10 | 11 | 12 | @contextmanager 13 | def pattern_match(target): 14 | ''' 15 | To make it clear where pattern matching is being attempted and 16 | to restrict where matched variables can be bound, creation of 17 | Match_objects is handled by this context manager. 18 | 19 | When the context manager is exited, any matched variables are 20 | left bound in the local scope but the match object itself is 21 | explicitly deleted in order to prevent accidental variable 22 | binding at later points. 23 | 24 | NOTE: Matched variables are bound to the locals of the stack 25 | frame using this context manager. The binding takes 26 | place at the point where a successful match is found. 27 | ''' 28 | matcher = Match_object(target) 29 | yield matcher 30 | del matcher 31 | 32 | 33 | def pattern_matching(func): 34 | ''' 35 | Allow the programmer to define a function that pattern matches against 36 | its arguments. 37 | 38 | This supplies a Match_object for each named argument to the original 39 | function bound to `_arg_name`. 40 | NOTE: **kwargs will work and each keyword argument can be matched against 41 | individually or as a dict. *args is bound as a single match object of 42 | `_args` as that is the only identifier we have to work with. 43 | (If you prefer to use something like *spam then it will correctly 44 | bind _spam instead.) 45 | ''' 46 | def global_to_fast(func): 47 | ''' 48 | Swap global lookups for local ones for pattern variables and 49 | match objects. Only works in a decorated function. 50 | ''' 51 | # cPython Bytecode values for easier readibility of the following code 52 | HAS_ARGS, LOAD_GLOBAL, LOAD_FAST = 90, 116, 124 53 | 54 | code = func.__code__ 55 | old_ops = (opcode for opcode in code.co_code) 56 | lvars = code.co_varnames 57 | 58 | templates = [ 59 | c.replace('(', '').replace(')', '').split() for c in code.co_consts 60 | if isinstance(c, str) 61 | and (c[0], c[-1]) == ('(', ')') 62 | ] 63 | pvars = {p.lstrip('*') for p in sum(templates, [])} 64 | 65 | new_ops = [] 66 | added = 0 67 | 68 | for byte in old_ops: 69 | if byte > HAS_ARGS: 70 | # Opcodes > 90 take the next two bytes as args 71 | lsig, msig = next(old_ops), next(old_ops) 72 | op = byte 73 | 74 | if byte == LOAD_GLOBAL: 75 | index = (msig << 8) + lsig 76 | var = code.co_names[index] 77 | # We want to swap matched variables: match objects are 78 | # already added to the by decorator 79 | if var in pvars: 80 | # Not removing the entry in co_names as that would 81 | # require modifying every other LOAD_GLOBAL index 82 | # for vars after this one in co_names...! 83 | op = LOAD_FAST 84 | lvars += (var,) 85 | added += 1 86 | lsig, msig = (len(lvars) - 1).to_bytes(2, 'little') 87 | new_ops.extend([op, lsig, msig]) 88 | else: 89 | new_ops.append(byte) 90 | 91 | return modify_func( 92 | func, 93 | co_code=bytes(new_ops), 94 | co_varnames=lvars, 95 | co_nlocals=code.co_nlocals + added) 96 | 97 | def modify_func(func, **kwds): 98 | ''' 99 | Modifies elements of a function's __code__, retaining the 100 | original values if no replacement is provided. 101 | ''' 102 | old = func.__code__ 103 | attrs = ['co_argcount', 'co_kwonlyargcount', 'co_nlocals', 104 | 'co_stacksize', 'co_flags', 'co_code', 'co_consts', 105 | 'co_names', 'co_varnames', 'co_filename', 'co_name', 106 | 'co_firstlineno', 'co_lnotab', 'co_freevars', 'co_cellvars'] 107 | new = CodeType(*(kwds.get(attr, getattr(old, attr)) for attr in attrs)) 108 | return FunctionType(new, func.__globals__, func.__name__, 109 | func.__defaults__, func.__closure__) 110 | 111 | func = global_to_fast(func) 112 | spec = getfullargspec(func) 113 | 114 | @wraps(func) 115 | def wrapped(*args, **kwargs): 116 | _globals = copy(func.__globals__) 117 | 118 | for var, val in zip(spec.args, args): 119 | v = '_{}'.format(var) 120 | _globals[v] = Match_object(val, decorated=True) 121 | if spec.varargs: 122 | v = '_{}'.format(spec.varargs) 123 | _globals[v] = Match_object(args, decorated=True) 124 | if spec.varkw: 125 | for var, val in kwargs.items(): 126 | v = '_{}'.format(var) 127 | _globals[v] = Match_object(val, decorated=True) 128 | v = '_{}'.format(spec.varkw) 129 | _globals[v] = Match_object(kwargs, decorated=True) 130 | 131 | func_w_matchers = FunctionType(func.__code__, _globals) 132 | return func_w_matchers(*args, **kwargs) 133 | 134 | return wrapped 135 | 136 | 137 | def non_string_collection(x): 138 | ''' 139 | A simple helper to allow string types to be 140 | distinguished from other collection types. 141 | ''' 142 | if isinstance(x, Container): 143 | if not isinstance(x, (str, bytes)): 144 | return True 145 | return False 146 | 147 | 148 | class Pvar: 149 | ''' 150 | Internal representation of pattern variables. 151 | Pattern variables maintain 152 | ''' 153 | __slots__ = 'greedy greedy_expanded symbol value'.split() 154 | repeating = False 155 | 156 | def __init__(self, symbol, greedy=False): 157 | self.symbol = symbol 158 | self.greedy = greedy 159 | if self.greedy: 160 | self.symbol = self.symbol.lstrip('*') 161 | self.greedy_expanded = False 162 | self.value = None 163 | 164 | def __repr__(self): 165 | return '{} -> {}'.format(self.symbol, self.value) 166 | 167 | def __eq__(self, other): 168 | ''' 169 | Compare and bind a value to the pattern variable 170 | ''' 171 | if self.symbol == '_': 172 | # Underscores match anything 173 | self.value = 'Matched' 174 | return True 175 | else: 176 | if non_string_collection(other): 177 | # Pvars can not match a sub-template 178 | return False 179 | elif self.greedy: 180 | if self.value: 181 | # Greedy pattern variables grow 182 | self.value.append(other) 183 | return True 184 | else: 185 | # Greedy variables always return a list 186 | self.value = [other] 187 | return True 188 | else: 189 | self.value = other 190 | return True 191 | 192 | def _propagate_match(self, attempt): 193 | ''' 194 | Make sure repeated variables have the same value. 195 | ''' 196 | if self.symbol == '_': 197 | # Don't store values matched against underscores. 198 | pass 199 | else: 200 | existing = attempt.get(self.symbol) 201 | 202 | if existing: 203 | # This variable is used more than once in the pattern. 204 | # It must have the same value each time for the match 205 | # to succeed. 206 | if self.value != existing: 207 | raise ValueError('FAILED MATCH') 208 | else: 209 | # There are no conflicts so update the match 210 | attempt[self.symbol] = self.value 211 | 212 | 213 | class Template: 214 | ''' 215 | Specification for the match. 216 | ''' 217 | __slots__ = 'repeating pvars value map'.split() 218 | greedy = False 219 | 220 | def __init__(self, match_template): 221 | self.pvars = [] 222 | self.map = dict() 223 | self.repeating = False 224 | self.value = None 225 | 226 | has_star = False 227 | has_ellipsis = False 228 | 229 | for element in match_template: 230 | next_var_is_greedy = False 231 | 232 | if non_string_collection(element): 233 | # Add a sub-template 234 | self.pvars.append(Template(element)) 235 | else: 236 | # Tag greedy pattern variables 237 | if element.startswith('*'): 238 | if has_star: 239 | raise SyntaxError( 240 | 'Can only have a max of one * per template') 241 | else: 242 | has_star = True 243 | next_var_is_greedy = True 244 | 245 | if element == '...': 246 | # Ellipsis makes the previous sub-template greedy 247 | if not isinstance(self.pvars[-1], Template): 248 | raise SyntaxError( 249 | '... can only be used on a repeating sub template') 250 | if has_ellipsis: 251 | raise SyntaxError( 252 | 'Can only have a maximum of one ... per template') 253 | else: 254 | has_ellipsis = True 255 | self.pvars[-1].repeating = True 256 | else: 257 | self.pvars.append(Pvar(element, greedy=next_var_is_greedy)) 258 | 259 | def __eq__(self, other): 260 | if not non_string_collection(other): 261 | # Convert to a single element list so that we don't accidentally 262 | # split strings into their characters 263 | other = [other] 264 | 265 | pairs = list(zip_longest(self.pvars, other, fillvalue=None)) 266 | try: 267 | return self.compare_and_bind(pairs) 268 | except ValueError as e: 269 | if e.args[0] == 'FAILED MATCH': 270 | return False 271 | else: 272 | raise 273 | 274 | def compare_and_bind(self, pairs): 275 | for _ in range(len(pairs)): 276 | pvar, target = pairs.pop(0) 277 | if pvar is None: # target is longer than the template 278 | return False 279 | 280 | self.check_match(pvar, target) 281 | 282 | if pvar.greedy and not pvar.greedy_expanded: 283 | self.match_greedy(pvar, pairs) 284 | break 285 | elif pvar.repeating: 286 | self.match_repeating(pvar, pairs) 287 | break 288 | 289 | if all([v.value for v in self.pvars]): 290 | self.value = self.pvars 291 | return True 292 | else: 293 | return False 294 | 295 | def match_greedy(self, pvar, pairs): 296 | ''' 297 | Deal with a greedy variable in the middle of a pattern by 298 | caching any later pattern variables as we match and then adding 299 | them back after expanding the greedy variable to fill the gap. 300 | ''' 301 | cached = [] 302 | try: 303 | next_pvar, next_target = pairs.pop(0) 304 | except IndexError: 305 | raise ValueError('FAILED MATCH') 306 | 307 | for _ in range(len(pairs)): 308 | if next_pvar is None or non_string_collection(next_target): 309 | break 310 | cached.append(next_pvar) 311 | self.check_match(pvar, next_target) 312 | try: 313 | next_pvar, next_target = pairs.pop(0) 314 | except IndexError: 315 | # End of the list 316 | break 317 | 318 | # Everything else is unmatched: 319 | # --> match the last one from the while loop first 320 | self.check_match(pvar, next_target) 321 | rem = len(list(takewhile(non_string_collection, pairs))) 322 | if rem: 323 | v, t = pairs.pop(0) 324 | self.check_match(pvar, t) 325 | diff = len(pairs) - len(cached) * rem 326 | pvar.greedy_expanded = True 327 | left_over_pvars = diff * [pvar] + cached 328 | left_over_targets = [r[1] for r in pairs] 329 | new_pairs = list(zip_longest(left_over_pvars, left_over_targets)) 330 | self.compare_and_bind(new_pairs) 331 | 332 | def match_repeating(self, pvar, pairs): 333 | ''' 334 | Handle a repeating sub-template. 335 | Variables in sub-templates return a list of all values that 336 | matched that position in the template. 337 | ''' 338 | values_so_far = {k: [v] for k, v in pvar.map.items()} 339 | for _, next_target in pairs: 340 | # Reset the sub-template match so we can go again 341 | # This gets transferred to the primary template. 342 | pvar.map = {} 343 | for p in pvar.pvars: 344 | p.value = None 345 | self.check_match(pvar, next_target) 346 | # update the map 347 | for p in pvar.pvars: 348 | new = values_so_far.setdefault(p.symbol, []) 349 | new.append(p.value) 350 | # We've now drained pairs so we are done 351 | self.map.update(values_so_far) 352 | 353 | def check_match(self, pvar, target): 354 | ''' 355 | Check for a match and update the current mapping 356 | This works for Pvars and Templates. 357 | ''' 358 | if pvar == target: 359 | if isinstance(pvar, Template): 360 | if pvar.repeating: 361 | for k, v in pvar.map.items(): 362 | new = self.map.setdefault(k, []) 363 | new.append(v) 364 | else: 365 | self.map.update(pvar.map) 366 | else: 367 | pvar._propagate_match(self.map) 368 | 369 | 370 | class Match_object: 371 | def __init__(self, val, decorated=False): 372 | self.val = val 373 | self.decorated = decorated 374 | self.map = {} 375 | 376 | def __getitem__(self, key): 377 | ''' 378 | Provide dict style lookup on the match object for when we 379 | run outside of cPython and binding to local scope might fail. 380 | ''' 381 | return self.map[key] 382 | 383 | def __eq__(self, other): 384 | ''' 385 | Allow for simple direct comparison against values 386 | ''' 387 | return self.val == other 388 | 389 | def __rshift__(self, type_or_types): 390 | ''' 391 | `match >> TYPE` perfoms a type check on the bound value 392 | `match >> (TYPE1, TYPE2...)` returns true if match.val is an 393 | instance of any of the supplied types. 394 | ''' 395 | return isinstance(self.val, type_or_types) 396 | 397 | def __ge__(self, pattern_str): 398 | ''' 399 | Check the supplied pattern against the bound value from the 400 | context manager. If it matches, bind the pattern variables to 401 | the values that they matched. 402 | Returns a bool so that this can be used in an if/else. 403 | ''' 404 | tokens = pattern_str.replace('(', ' ( ').replace(')', ' ) ').split() 405 | pattern = next(self.parse(tokens)) 406 | t = Template(pattern) 407 | if t == self.val: 408 | self.map = t.map 409 | if self.decorated: 410 | self._bind_to_calling_scope() 411 | return True 412 | else: 413 | print(t.map) 414 | return False 415 | 416 | def parse(self, tokens): 417 | ''' 418 | Convert a string representation of the template to 419 | a - potentially nested - tuple that we can iterate over. 420 | ''' 421 | tokens = iter(tokens) 422 | for t in tokens: 423 | if t == '(': 424 | group = [] 425 | t = next(tokens) 426 | if t == ')': 427 | raise SyntaxError('Empty match template') 428 | else: 429 | while t != ')': 430 | tokens = chain([t], tokens) 431 | group.append(next(self.parse(tokens))) 432 | t = next(tokens) 433 | yield tuple(group) 434 | else: 435 | yield t 436 | 437 | def _bind_to_calling_scope(self): 438 | ''' 439 | Inject the result of a successful match into the calling scope. 440 | This only works inside of a decorated function; use dict style 441 | lookup syntax for use in a context manager. 442 | NOTE: This uses some not-so-nice abuse of stack frames and the 443 | ctypes API to make this work and as such it will probably 444 | not run under anything other than cPython. 445 | ''' 446 | # Grab the stack frame that the caller's code is running in 447 | frame = _getframe(2) 448 | # Dump the matched variables and their values into the frame 449 | frame.f_locals.update(self.map) 450 | # Force an update of the frame locals from the locals dict 451 | pythonapi.PyFrame_LocalsToFast( 452 | py_object(frame), 453 | c_int(0) 454 | ) 455 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------