{ "info": { "author": "Claudio Santini", "author_email": "hireclaudio@gmail.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3" ], "description": "![Pampy in Star Wars](https://raw.githubusercontent.com/santinic/pampy/master/imgs/pampy.png \"Pampy in Star Wars\")\n\n# Pampy: Pattern Matching for Python\n[![License MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/santinic/pampy/blob/master/LICENSE)\n[![Travis-CI Status](https://api.travis-ci.org/santinic/pampy.svg?branch=master)](https://travis-ci.org/santinic/pampy)\n[![Coverage Status](https://coveralls.io/repos/github/santinic/pampy/badge.svg?branch=master)](https://coveralls.io/github/santinic/pampy?branch=master)\n[![PyPI version](https://badge.fury.io/py/pampy.svg)](https://badge.fury.io/py/pampy)\n\nPampy is pretty small (150 lines), reasonably fast, and often makes your code more readable\nand hence easier to reason about. [There is also a JavaScript version, called Pampy.js](https://github.com/santinic/pampy.js).\n\n\n \n\n\n## You can write many patterns\n\nPatterns are evaluated in the order they appear.\n\n\n \n\n\n\n## You can write Fibonacci\nThe operator _ means \"any other case I didn't think of\".\n\n```python\nfrom pampy import match, _\n\ndef fibonacci(n):\n return match(n,\n 1, 1,\n 2, 1,\n _, lambda x: fibonacci(x-1) + fibonacci(x-2)\n )\n```\n\n## You can write a Lisp calculator in 5 lines\n\n```python\nfrom pampy import match, REST, _\n\ndef lisp(exp):\n return match(exp,\n int, lambda x: x,\n callable, lambda x: x,\n (callable, REST), lambda f, rest: f(*map(lisp, rest)),\n tuple, lambda t: list(map(lisp, t)),\n )\n\nplus = lambda a, b: a + b\nminus = lambda a, b: a - b\nfrom functools import reduce\n\nlisp((plus, 1, 2)) \t# => 3\nlisp((plus, 1, (minus, 4, 2))) \t# => 3\nlisp((reduce, plus, (range, 10))) # => 45\n```\n\n## You can match so many things!\n\n```python\nmatch(x,\n 3, \"this matches the number 3\",\n\n int, \"matches any integer\",\n\n (str, int), lambda a, b: \"a tuple (a, b) you can use in a function\",\n\n [1, 2, _], \"any list of 3 elements that begins with [1, 2]\",\n\n {'x': _}, \"any dict with a key 'x' and any value associated\",\n\n _, \"anything else\"\n)\n```\n\n## You can match [HEAD, TAIL]\n\n```python\nfrom pampy import match, HEAD, TAIL, _\n\nx = [1, 2, 3]\n\nmatch(x, [1, TAIL], lambda t: t) # => [2, 3]\n\nmatch(x, [HEAD, TAIL], lambda h, t: (h, t)) # => (1, [2, 3])\n\n```\n`TAIL` and `REST` actually mean the same thing.\n\n## You can nest lists and tuples\n\n```python\nfrom pampy import match, _\n\nx = [1, [2, 3], 4]\n\nmatch(x, [1, [_, 3], _], lambda a, b: [1, [a, 3], b]) # => [1, [2, 3], 4]\n```\n\n## You can nest dicts. And you can use _ as key!\n\n```python\n\npet = { 'type': 'dog', 'details': { 'age': 3 } }\n\nmatch(pet, { 'details': { 'age': _ } }, lambda age: age) # => 3\n\nmatch(pet, { _ : { 'age': _ } }, lambda a, b: (a, b)) # => ('details', 3)\n```\n\nIt feels like putting multiple _ inside dicts shouldn't work. Isn't ordering in dicts not guaranteed ?\nBut it does because\n[in Python 3.7, dict maintains insertion key order by default](https://mail.python.org/pipermail/python-dev/2017-December/151283.html)\n\n## You can match class hierarchies\n\n```python\nclass Pet: pass\nclass Dog(Pet): pass\nclass Cat(Pet): pass\nclass Hamster(Pet): pass\n\ndef what_is(x):\n return match(x,\n Dog, \t\t'dog',\n Cat, \t\t'cat',\n Pet, \t\t'any other pet',\n _, \t\t'this is not a pet at all',\n )\n\nwhat_is(Cat()) # => 'cat'\nwhat_is(Dog()) # => 'dog'\nwhat_is(Hamster()) # => 'any other pet'\nwhat_is(Pet()) # => 'any other pet'\nwhat_is(42) # => 'this is not a pet at all'\n```\n\n## All the things you can match\n\nAs Pattern you can use any Python type, any class, or any Python value.\n\nThe operator `_` and built-in types like `int` or `str`, extract variables that are passed to functions.\n\nTypes and Classes are matched via `instanceof(value, pattern)`.\n\n`Iterable` Patterns match recursively through all their elements. The same goes for dictionaries.\n\n| Pattern Example | What it means | Matched Example | Arguments Passed to function | NOT Matched Example |\n| --------------- | --------------| --------------- | ----------------------------- | ------------------ |\n| `\"hello\"` | only the string `\"hello\"` matches | `\"hello\"` | nothing | any other value |\n| `None` | only `None` | `None` | nothing | any other value |\n| `int` | Any integer | `42` | `42` | any other value |\n| `float` | Any float number | `2.35` | `2.35` | any other value |\n| `str` | Any string | `\"hello\"` | `\"hello\"` | any other value |\n| `tuple` | Any tuple | `(1, 2)` | `(1, 2)` | any other value |\n| `list` | Any list | `[1, 2]` | `[1, 2]` | any other value |\n| `MyClass` | Any instance of MyClass. **And any object that extends MyClass.** | `MyClass()` | that instance | any other object |\n| `_` | Any object (even None) | | that value | |\n| `ANY` | The same as `_` | | that value | |\n| `(int, int)` | A tuple made of any two integers | `(1, 2)` | `1` and `2` | (True, False) |\n| `[1, 2, _]` | A list that starts with 1, 2 and ends with any value | `[1, 2, 3]` | `3` | `[1, 2, 3, 4]` |\n| `[1, 2, TAIL]` | A list that start with 1, 2 and ends with any sequence | `[1, 2, 3, 4]`| `[3, 4]` | `[1, 7, 7, 7]` |\n| `{'type':'dog', age: _ }` | Any dict with `type: \"dog\"` and with an age | `{\"type\":\"dog\", \"age\": 3}` | `3` | `{\"type\":\"cat\", \"age\":2}` |\n| `{'type':'dog', age: int }` | Any dict with `type: \"dog\"` and with an `int` age | `{\"type\":\"dog\", \"age\": 3}` | `3` | `{\"type\":\"dog\", \"age\":2.3}` |\n| `re.compile('(\\w+)-(\\w+)-cat$')` | Any string that matches that regular expression expr | `\"my-fuffy-cat\"` | `\"my\"` and `\"puffy\"` | `\"fuffy-dog\"` | \n| `Pet(name=_, age=7)` | Any Pet dataclass with `age == 7` | `Pet('rover', 7)` | `['rover']` | `Pet('rover', 8)` |\n\n## Using strict=False\n\nBy default `match()` is strict. If no pattern matches, it raises a `MatchError`.\n\nYou can prevent it using `strict=False`. In this case `match` just returns `False` if nothing matches.\n\n```\n>>> match([1, 2], [1, 2, 3], \"whatever\")\nMatchError: '_' not provided. This case is not handled: [1, 2]\n\n>>> match([1, 2], [1, 2, 3], \"whatever\", strict=False)\nFalse\n```\n\n## Using Regular Expressions\nPampy supports Python's Regex. You can pass a compiled regex as pattern, and Pampy is going to run `patter.search()`, and then pass to the action function the result of `.groups()`.\n\n```python \ndef what_is(pet):\n return match(pet,\n re.compile('(\\w+)-(\\w+)-cat$'), lambda name, my: 'cat '+name,\n re.compile('(\\w+)-(\\w+)-dog$'), lambda name, my: 'dog '+name,\n _, \"something else\"\n )\n\nwhat_is('fuffy-my-dog') # => 'dog fuffy'\nwhat_is('puffy-her-dog') # => 'dog puffy'\nwhat_is('carla-your-cat') # => 'cat carla'\nwhat_is('roger-my-hamster') # => 'something else'\n```\n\n## Using Dataclasses\nPampy supports Python 3.7 dataclasses. You can pass the operator `_` as arguments and it will match those fields.\n\n```python\n@dataclass\nclass Pet:\n name: str\n age: int\n\npet = Pet('rover', 7)\n\nmatch(pet, Pet('rover', _), lambda age: age) # => 7\nmatch(pet, Pet(_, 7), lambda name: name) # => 'rover'\nmatch(pet, Pet(_, _), lambda name, age: (name, age)) # => ('rover', 7)\n```\n\n## Install\n\nCurrently it works only in Python >= 3.6 [Because dict matching can work only in the latest Pythons](https://mail.python.org/pipermail/python-dev/2017-December/151283.html).\n\nI'm currently working on a backport with some minor syntax changes for Python2.\n\nTo install it:\n\n```$ pip install pampy```\n\nor\n```$ pip3 install pampy```\n\n\n\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/santinic/pampy", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "backports.pampy", "package_url": "https://pypi.org/project/backports.pampy/", "platform": "any", "project_url": "https://pypi.org/project/backports.pampy/", "project_urls": { "Homepage": "https://github.com/santinic/pampy" }, "release_url": "https://pypi.org/project/backports.pampy/0.1.10/", "requires_dist": [ "six", "typing" ], "requires_python": "", "summary": "The Pattern Matching for Python you always dreamed of", "version": "0.1.10" }, "last_serial": 4621755, "releases": { "0.1.10": [ { "comment_text": "", "digests": { "md5": "f14fe0e7ee3b84d883b7ee7d5a9c3336", "sha256": "55e4d6f98dac6bad3fc537ee816820c87a03cd8be85cf0d617b6d17474b58da2" }, "downloads": -1, "filename": "backports.pampy-0.1.10-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "f14fe0e7ee3b84d883b7ee7d5a9c3336", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 9722, "upload_time": "2018-12-20T17:39:13", "url": "https://files.pythonhosted.org/packages/31/89/cf98bde4a64aa70910958019011cf77f73fa18043cb6242251f5811b2f88/backports.pampy-0.1.10-py2.py3-none-any.whl" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "f14fe0e7ee3b84d883b7ee7d5a9c3336", "sha256": "55e4d6f98dac6bad3fc537ee816820c87a03cd8be85cf0d617b6d17474b58da2" }, "downloads": -1, "filename": "backports.pampy-0.1.10-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "f14fe0e7ee3b84d883b7ee7d5a9c3336", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 9722, "upload_time": "2018-12-20T17:39:13", "url": "https://files.pythonhosted.org/packages/31/89/cf98bde4a64aa70910958019011cf77f73fa18043cb6242251f5811b2f88/backports.pampy-0.1.10-py2.py3-none-any.whl" } ] }