{ "info": { "author": "Cristian Garcia", "author_email": "cgarcia.e88@gmail.com", "bugtrack_url": null, "classifiers": [], "description": "# Phi\nPhi library for functional programming in Python that intends to remove as much of the pain as possible from your functional programming experience in Python.\n\n## Import\nFor demonstration purposes we will import right now everything we will need for the rest of the exercises like this\n```python\nfrom phi.api import *\n```\nbut you can also import just what you need from the `phi` module.\n\n## Math-like Lambdas\n\n#### Operators\n\nUsing the `P` object you can create quick lambdas using any operator. You can write things like\n\n```python\nf = (P * 6) / (P + 2) #lambda x: (x * 6) / (x + 2)\n\nassert f(2) == 3 # (2 * 6) / (2 + 2) == 12 / 4 == 3\n```\n\nwhere the expression for `f` is equivalent to\n\n```python\nf = lambda x: (x * 6) / (x + 2)\n```\n\n#### getitem\nYou can also use the `P` object to create lambdas that access the items of a collection\n```python\nf = P[0] + P[-1] #lambda x: x[0] + x[-1]\n\nassert f([1,2,3,4]) == 5 #1 + 4 == 5\n```\n\n#### field access\nIf you want create lambdas that access the field of some entity you can use the `Rec` (for Record) object an call that field on it\n```python\nfrom collections import namedtuple\nPoint = namedtuple('Point', ['x', 'y'])\n\nf = Rec.x + Rec.y #lambda p: p.x + p.y\n\nassert f(Point(3, 4)) == 7 #point.x + point.y == 3 + 4 == 7\n```\n#### method calling\nIf you want to create a lambda that calls the method of an object you use the `Obj` object and call that method on it with the parameters\n```python\nf = Obj.upper() + \", \" + Obj.lower() #lambda s: s.upper() + \", \" + s.lower()\n\nassert f(\"HEllo\") == \"HELLO, hello\" # \"HEllo\".upper() + \", \" + \"HEllo\".lower() == \"HELLO\" + \", \" + \"hello\" == \"HELLO, hello\"\n```\nHere no parameters were needed but in general\n```python\nf = Obj.some_method(arg1, arg2, ...) #lambda obj: obj.some_method(arg1, arg2, ...)\n```\nis equivalent to\n```python\nf = lambda obj: obj.some_method(arg1, arg2, ...)\n```\n## Composition\n#### >> and <<\nYou can use the `>>` operator to *forward* compose expressions\n\n```python\nf = P + 7 >> math.sqrt #executes left to right\n\nassert f(2) == 3 # math.sqrt(2 + 7) == math.sqrt(9) == 3\n```\nThis is preferred because it is more readable, but you can use the `<<` to compose them *backwards* just like the mathematical definition of function composition\n\n```python\nf = math.sqrt << P + 7 #executes right to left\n\nassert f(2) == 3 # math.sqrt(2 + 7) == math.sqrt(9) == 3\n```\n\n#### Seq and Pipe\nIf you need to do a long or complex composition you can use `Seq` (for 'Sequence') instead of many chained `>>`\n\n```python\nf = Seq(\n str,\n P + \"00\",\n int,\n math.sqrt\n)\n\nassert f(1) == 10 # sqrt(int(\"1\" + \"00\")) == sqrt(100) == 10\n```\nIf you want to create a composition and directly apply it to an initial value you can use `Pipe`\n\n```python\nassert 10 == Pipe(\n 1, #input\n str, # \"1\"\n P + \"00\", # \"1\" + \"00\" == \"100\"\n int, # 100\n math.sqrt #sqrt(100) == 10\n)\n```\n\n## Combinators\n#### List, Tuple, Set, Dict\nThere are a couple of combinators like `List`, `Tuple`, `Set`, `Dict` that help you create compound functions that return the container types `list`, `tuple`, `set` and `dict` respectively. For example, you can pass `List` a couple of expressions to get a function that returns a list with the values of these functions\n\n```python\nf = List( P + 1, P * 10 ) #lambda x: [ x +1, x * 10 ]\n\nassert f(3) == [ 4, 30 ] # [ 3 + 1, 3 * 10 ] == [ 4, 30 ]\n```\nThe same logic applies for `Tuple` and `Set`. With `Dict` you have to use keyword arguments\n\n```python\nf = Dict( x = P + 1, y = P * 10 ) #lambda x: [ x +1, x * 10 ]\n\nd = f(3)\n\nassert d == { 'x': 4, 'y': 30 } # { 'x': 3 + 1, 'y': 3 * 10 } == { 'x': 4, 'y': 30 }\nassert d.x == 4 #access d['x'] via field access as d.x\nassert d.y == 30 #access d['y'] via field access as d.y\n```\nAs you see, `Dict` returns a custom `dict` that also allows *field access*, this is useful because you can use it in combination with `Rec`.\n\n#### State: Read and Write\nInternally all these expressions are implemented in such a way that they not only pass their computed values but also pass a **state** dictionary between them in a functional manner. By reading from and writing to this state dictionary the `Read` and `Write` combinators can help you \"save\" the state of intermediate computations to read them later\n\n```python\nassert [70, 30] == Pipe(\n 3,\n Write(s = P * 10), #s = 3 * 10 == 30\n P + 5, #30 + 5 == 35\n List(\n P * 2 # 35 * 2 == 70\n ,\n Read('s') #s == 30\n )\n)\n```\nIf you need to perform many reads inside a list -usually for output- you can use `ReadList` instead\n```python\nassert [2, 4, 22] == Pipe(\n 1,\n Write(a = P + 1), #a = 1 + 1 == 2\n Write(b = P * 2), #b = 2 * 2 == 4\n P * 5, # 4 * 5 == 20\n ReadList('a', 'b', P + 2) # [a, b, 20 + 2] == [2, 4, 22]\n)\n```\n`ReadList` interprets string elements as `Read`s, so the previous is translated to\n```python\nList(Read('a'), Read('b'), P + 2)\n```\n\n#### Then, Then2, ..., Then5, ThenAt\nTo create a partial expression from a function e.g.\n```python\ndef repeat_word(word, times, upper=False):\n if upper:\n word = word.upper()\n\n return [ word ] * times\n```\nuse the `Then` combinator which accepts a function plus all but the *1st* of its `*args` + `**kwargs`\n```python\nf = P[::-1] >> Then(repeat_word, 3)\ng = P[::-1] >> Then(repeat_word, 3, upper=True)\n\nassert f(\"ward\") == [\"draw\", \"draw\", \"draw\"]\nassert g(\"ward\") == [\"DRAW\", \"DRAW\", \"DRAW\"]\n```\nand assumes that the *1st* argument of the function will be applied last, e.g. `word` in the case of `repeat_word`. If you need the *2nd* argument to be applied last use `Then2`, and so on. In general you can use `ThenAt(n, f, *args, **kwargs)` where `n` is the position of the argument that will be applied last. Example\n```python\n# since map and filter receive the iterable on their second argument, you have to use `Then2`\nf = Then2(filter, P % 2 == 0) >> Then2(map, P**2) >> list #lambda x: map(lambda z: z**2, filter(lambda z: z % 2 == 0, x))\n\nassert f([1,2,3,4,5]) == [4, 16] #[2**2, 4**2] == [4, 16]\n```\nBe aware that `P` already has the `map` and `filter` methods so you can write the previous more easily as\n```python\nf = P.filter(P % 2 == 0) >> P.map(P**2) >> list #lambda x: map(lambda z: z**2, filter(lambda z: z % 2 == 0, x))\n\nassert f([1,2,3,4,5]) == [4, 16] #[2**2, 4**2] == [4, 16]\n```\n\n#### Val\nIf you need to create a constant function with a given value use `Val`\n```python\nf = Val(42) #lambda x: 42\n\nassert f(\"whatever\") == 42\n```\n\n#### Others\nCheck out the `With`, `If` and more, combinators on the documentation. The `P` object also offers some useful combinators as methods such as `Not`, `First`, `Last` plus **almost all** python built in functions as methods:\n\n```python\nf = Obj.split(' ') >> P.map(len) >> sum >> If( (P < 15).Not(), \"Great! Got {0} letters!\".format).Else(\"Too short, need at-least 15 letters\")\n\nassert f(\"short frase\") == \"Too short, need at-least 15 letters\"\nassert f(\"some longer frase\") == \"Great! Got 15 letters!\"\n```\n\n## The DSL\nPhi has a small omnipresent DSL that has these simple rules:\n\n1. Any element of the class `Expression` is an element of the DSL. `P` and all the combinators are of the `Expression` class.\n2. Any callable of arity 1 is an element of the DSL.\n3. The container types `list`, `tuple`, `set`, and `dict` are elements of the DSL. They are translated to their counterparts `List`, `Tuple`, `Set` and `Dict`, their internal elements are forwarded.\n4. Any value `x` that does not comply with any of the previous rules is also an element of the DSL and is translated to `Val(x)`.\n\nUsing the DSL, the expression\n\n```python\nf = P**2 >> List( P, Val(3), Val(4) ) #lambda x: [ x**2]\n\nassert f(10) == [ 100, 3, 4 ] # [ 10**2, 3, 4 ] == [ 100, 3, 4 ]\n```\ncan be rewritten as\n```python\nf = P**2 >> [ P, 3, 4 ]\n\nassert f(10) == [ 100, 3, 4 ] # [ 10 ** 2, 3, 4 ] == [ 100, 3, 4 ]\n```\nHere the values `3` and `4` are translated to `Val(3)` and `Val(4)` thanks to the *4th* rule, and `[...]` is translated to `List(...)` thanks to the *3rd* rule. Since the DSL is omnipresent you can use it inside any core function, so the previous can be rewritten using `Pipe` as\n```python\nassert [ 100, 3, 4 ] == Pipe(\n 10,\n P**2, # 10**2 == 100\n [ P, 3, 4 ] #[ 100, 3, 4 ]\n)\n```\n\n#### F\nYou can *compile* any element to an `Expression` using `F`\n```python\nf = F((P + \"!!!\", 42, Obj.upper())) #Tuple(P + \"!!!\", Val(42), Obj.upper())\n\nassert f(\"some tuple\") == (\"some tuple!!!\", 42, \"SOME TUPLE\")\n```\nOther example\n```python\nf = F([ P + n for n in range(5) ]) >> [ len, sum ] # lambda x: [ len([ x, x+1, x+2, x+3, x+4]), sum([ x, x+1, x+2, x+3, x+4]) ]\n\nassert f(10) == [ 5, 60 ] # [ len([10, 11, 12, 13, 14]), sum([10, 11, 12, 13, 14])] == [ 5, (50 + 0 + 1 + 2 + 3 + 4) ] == [ 5, 60 ]\n```\n\n## Fluent Programming\nAll the functions you've seen are ultimately methods of the `PythonBuilder` class which inherits from the `Expression`, therefore you can also [fluently](https://en.wikipedia.org/wiki/Fluent_interface) chain methods instead of using the `>>` operator. For example\n\n```python\nf = Dict(\n x = 2 * P,\n y = P + 1\n).Tuple(\n Rec.x + Rec.y,\n Rec.y / Rec.x\n)\n\nassert f(1) == (4, 1) # ( x + y, y / x) == ( 2 + 2, 2 / 2) == ( 4, 1 )\n```\nThis more complicated previous example\n```python\nf = Obj.split(' ') >> P.map(len) >> sum >> If( (P < 15).Not(), \"Great! Got {0} letters!\".format).Else(\"Too short, need at-least 15 letters\")\n\nassert f(\"short frase\") == \"Too short, need at-least 15 letters\"\nassert f(\"some longer frase\") == \"Great! Got 15 letters!\"\n```\ncan be be rewritten as\n```python\nf = (\n Obj.split(' ')\n .map(len)\n .sum()\n .If( (P < 15).Not(),\n \"Great! Got {0} letters!\".format\n ).Else(\n \"Too short, need at-least 15 letters\"\n )\n)\n\nassert f(\"short frase\") == \"Too short, need at-least 15 letters\"\nassert f(\"some longer frase\") == \"Great! Got 15 letters!\"\n```\n\n## Integrability\n#### Register, Register2, ..., Register5, RegistarAt\nIf you want to have custom expressions to deal with certain data types, you can create a custom class that inherits from `Builder` or `PythonBuilder`\n```python\nfrom phi import PythonBuilder\n\nclass MyBuilder(PythonBuilder):\n pass\n\nM = MyBuilder()\n```\nand register your function in it using the `Register` class method\n\n```python\ndef remove_longer_than(some_list, n):\n return [ elem from elem in some_list if len(elem) <= n ]\n\nMyBuilder.Register(remove_longer_than, \"my.lib.\")\n```\nOr better even use `Register` as a decorator\n```python\n@MyBuilder.Register(\"my.lib.\")\ndef remove_longer_than(some_list, n):\n return [ elem for elem in some_list if len(elem) <= n ]\n```\n\nNow the method `MyBuilder.remove_longer_than` exists on this class. You can then use it like this\n```python\nf = Obj.lower() >> Obj.split(' ') >> M.remove_longer_than(6)\n\nassert f(\"SoMe aRe LONGGGGGGGGG\") == [\"some\", \"are\"]\n```\nAs you see the argument `n = 6` was partially applied to `remove_longer_than`, an expression which waits for the `some_list` argument to be returned. Internally the `Registar*` method family uses the `Then*` method family.\n\n#### PatchAt\nIf you want to register a batch of functions from a module or class automatically you can use the `PatchAt` class method. It's an easy way to integrate an entire module to Phi's DSL. See `PatchAt`.\n\n#### Libraries\nPhi currently powers the following libraries that integrate with its DSL:\n\n* [PythonBuilder](https://cgarciae.github.io/phi/python_builder.m.html) : helps you integrate Python's built-in functions and keywords into the phi DSL and it also includes a bunch of useful helpers for common stuff. `phi`'s global `P` object is an instance of this class. [Shipped with Phi]\n* [TensorBuilder](https://github.com/cgarciae/tensorbuilder): a TensorFlow library enables you to easily create complex deep neural networks by leveraging the phi DSL to help define their structure.\n* NumpyBuilder: Comming soon!\n\n## Documentation\nCheck out the [complete documentation](https://cgarciae.github.io/phi/).\n\n## More Examples\nThe global `phi.P` object exposes most of the API and preferably should be imported directly. The most simple thing the DSL does is function composition:\n\n```python\nfrom phi.api import *\n\ndef add1(x): return x + 1\ndef mul3(x): return x * 3\n\nx = Pipe(\n 1.0, #input 1\n add1, #1 + 1 == 2\n mul3 #2 * 3 == 6\n)\n\nassert x == 6\n```\n\nUse phi [lambdas](https://cgarciae.github.io/phi/lambdas.m.html) to create the functions\n\n```python\nfrom phi.api import *\n\nx = Pipe(\n 1.0, #input 1\n P + 1, #1 + 1 == 2\n P * 3 #2 * 3 == 6\n)\n\nassert x == 6\n```\n\nCreate a branched computation instead\n\n```python\nfrom phi.api import *\n\n[x, y] = Pipe(\n 1.0, #input 1\n [\n P + 1 #1 + 1 == 2\n ,\n P * 3 #1 * 3 == 3\n ]\n)\n\nassert x == 2\nassert y == 3\n```\n\nCompose it with a function equivalent to `f(x) = (x + 3) / (x + 1)`\n\n```python\nfrom phi.api import *\n\n[x, y] = Pipe(\n 1.0, #input 1\n (P + 3) / (P + 1), #(1 + 3) / (1 + 1) == 4 / 2 == 2\n [\n P + 1 #2 + 1 == 3\n ,\n P * 3 #2 * 3 == 6\n ]\n)\n\nassert x == 3\nassert y == 6\n```\n\nGive names to the branches\n\n```python\nfrom phi.api import *\n\nresult = Pipe(\n 1.0, #input 1\n (P + 3) / (P + 1), #(1 + 3) / (1 + 1) == 4 / 2 == 2\n dict(\n x = P + 1 #2 + 1 == 3\n ,\n y = P * 3 #2 * 3 == 6\n )\n)\n\nassert result.x == 3\nassert result.y == 6\n```\n\nDivide `x` by `y`.\n\n```python\nfrom phi.api import *\n\nresult = Pipe(\n 1.0, #input 1\n (P + 3) / (P + 1), #(1 + 3) / (1 + 1) == 4 / 2 == 2\n dict(\n x = P + 1 #2 + 1 == 3\n ,\n y = P * 3 #2 * 3 == 6\n ),\n Rec.x / Rec.y #3 / 6 == 0.5\n)\n\nassert result == 0.5\n```\n\nSave the value from the `(P + 3) / (P + 1)` computation as `s` and load it at the end in a branch\n\n```python\nfrom phi.api import *\n\n[result, s] = Pipe(\n 1.0, #input 1\n Write(s = (P + 3) / (P + 1)), #s = 4 / 2 == 2\n dict(\n x = P + 1 #2 + 1 == 3\n ,\n y = P * 3 #2 * 3 == 6\n ),\n [\n Rec.x / Rec.y #3 / 6 == 0.5\n ,\n Read('s') #s == 2\n ]\n)\n\nassert result == 0.5\nassert s == 2\n```\n\nAdd 3 to the loaded `s` for fun and profit\n\n```python\nfrom phi.api import *\n\n[result, s] = Pipe(\n 1.0, #input 1\n Write(s = (P + 3) / (P + 1)), #s = 4 / 2 == 2\n dict(\n x = P + 1 #2 + 1 == 3\n ,\n y = P * 3 #2 * 3 == 6\n ),\n [\n Rec.x / Rec.y #3 / 6 == 0.5\n ,\n Read('s') + 3 # 2 + 3 == 5\n ]\n)\n\nassert result == 0.5\nassert s == 5\n```\n\nUse the `Read` and `Write` field access lambda style just because\n\n```python\nfrom phi.api import *\n\n[result, s] = Pipe(\n 1.0, #input 1\n (P + 3) / (P + 1), #4 / 2 == 2\n Write.s, #s = 2\n dict(\n x = P + 1 #2 + 1 == 3\n ,\n y = P * 3 #2 * 3 == 6\n ),\n [\n Rec.x / Rec.y #3 / 6 == 0.5\n ,\n Read.s + 3 # 2 + 3 == 5\n ]\n)\n\nassert result == 0.5\nassert s == 5\n```\n\nAdd an input `Val` of 9 on a branch and add to it 1 just for the sake of it\n\n```python\nfrom phi.api import *\n\n[result, s, val] = Pipe(\n 1.0, #input 1\n (P + 3) / (P + 1), Write.s, #4 / 2 == 2, saved as 's'\n dict(\n x = P + 1 #2 + 1 == 3\n ,\n y = P * 3 #2 * 3 == 6\n ),\n [\n Rec.x / Rec.y #3 / 6 == 0.5\n ,\n Read.s + 3 # 2 + 3 == 5\n ,\n Val(9) + 1 #input 9 and add 1, gives 10\n ]\n)\n\nassert result == 0.5\nassert s == 5\nassert val == 10\n```\n\nDo the previous only if `y > 7` else return `\"Sorry, come back latter.\"`\n\n```python\nfrom phi.api import *\n\n[result, s, val] = Pipe(\n 1.0, #input 1\n (P + 3) / (P + 1), Write.s, #4 / 2 == 2, saved as 's'\n dict(\n x = P + 1 #2 + 1 == 3\n ,\n y = P * 3 #2 * 3 == 6\n ),\n [\n Rec.x / Rec.y #3 / 6 == 0.5\n ,\n Read.s + 3 # 2 + 3 == 5\n ,\n If( Rec.y > 7,\n Val(9) + 1 #input 9 and add 1, gives 10 \n ).Else(\n \"Sorry, come back latter.\"\n )\n ]\n)\n\nassert result == 0.5\nassert s == 5\nassert val == \"Sorry, come back latter.\"\n```\n\nNow, what you have to understand that everything you've done with these expression is to create and apply a single function. Using `Seq` we can get the standalone function and then use it to get the same values as before\n\n```python\nfrom phi.api import *\n\nf = Seq(\n (P + 3) / (P + 1), Write.s, #4 / 2 == 2, saved as 's'\n dict(\n x = P + 1 #2 + 1 == 3\n ,\n y = P * 3 #2 * 3 == 6\n ),\n [\n Rec.x / Rec.y #3 / 6 == 0.5\n ,\n Read.s + 3 # 2 + 3 == 5\n ,\n If( Rec.y > 7,\n Val(9) + 1 #input 9 and add 1, gives 10 \n ).Else(\n \"Sorry, come back latter.\"\n )\n ]\n)\n\n[result, s, val] = f(1.0)\n\nassert result == 0.5\nassert s == 5\nassert val == \"Sorry, come back latter.\"\n```\n### Even More Examples\n\n```python\nfrom phi.api import *\n\navg_word_length = Pipe(\n \"1 22 333\",\n Obj.split(\" \"), # ['1', '22', '333']\n P.map(len), # [1, 2, 3]\n P.sum() / P.len() # sum([1,2,3]) / len([1,2,3]) == 6 / 3 == 2\n)\n\nassert 2 == avg_word_length\n```\n\n```python\nfrom phi.api import *\n\nassert False == Pipe(\n [1,2,3,4], P\n .filter(P % 2 != 0) #[1, 3], keeps odds\n .Contains(4) #4 in [1, 3] == False\n)\n```\n\n```python\nfrom phi.api import *\n\nassert {'a': 97, 'b': 98, 'c': 99} == Pipe(\n \"a b c\", Obj\n .split(' ').Write.keys # keys = ['a', 'b', 'c']\n .map(ord), # [ord('a'), ord('b'), ord('c')] == [97, 98, 99]\n lambda it: zip(Ref.keys, it), # [('a', 97), ('b', 98), ('c', 99)]\n dict # {'a': 97, 'b': 98, 'c': 99}\n)\n```\n\n## Installation\n\n pip install phi\n\n\n#### Bleeding Edge\n\n pip install git+https://github.com/cgarciae/phi.git@develop\n\n## Status\n* Version: **0.6.4**.\n* Documentation coverage: 100%. Please create an issue if documentation is unclear, it is a high priority of this library.\n* Milestone: reach 1.0.0 after feedback from the community.\n", "description_content_type": "", "docs_url": null, "download_url": "https://github.com/cgarciae/phi/tarball/0.6.7", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/cgarciae/phi", "keywords": "functional programming", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "phi", "package_url": "https://pypi.org/project/phi/", "platform": "", "project_url": "https://pypi.org/project/phi/", "project_urls": { "Download": "https://github.com/cgarciae/phi/tarball/0.6.7", "Homepage": "https://github.com/cgarciae/phi" }, "release_url": "https://pypi.org/project/phi/0.6.7/", "requires_dist": null, "requires_python": "", "summary": "Phi is a library for fluent functional programming in Python which includes a DSL + facilities to create libraries that integrate with it.", "version": "0.6.7" }, "last_serial": 3744803, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "5dd82398a8eb7de9b71d2cfd14ee8735", "sha256": "0198b33c294459fa700d594cdea9ce206da03b5a2f53d45bb41a22b574a60544" }, "downloads": -1, "filename": "phi-0.1.0.tar.gz", "has_sig": false, "md5_digest": "5dd82398a8eb7de9b71d2cfd14ee8735", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10672, "upload_time": "2016-11-14T22:32:06", "url": "https://files.pythonhosted.org/packages/32/b5/a3bbf79edbcebafe24a4f9c63b05dcb40ca3aa8f8d63ff762d51fe9e8a21/phi-0.1.0.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "8f1d339e867190f1426a6f7419567ecf", "sha256": "d47b175825c0b74cd189326755d19816706321ca4bd8a73c1af7bd3d3281cd1f" }, "downloads": -1, "filename": "phi-0.2.0.tar.gz", "has_sig": false, "md5_digest": "8f1d339e867190f1426a6f7419567ecf", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14902, "upload_time": "2016-12-04T01:29:48", "url": "https://files.pythonhosted.org/packages/58/e6/24a4dbc182508ab1f1c2470071ffe4f49cc54ef5929472eb4f7d26d863bf/phi-0.2.0.tar.gz" } ], "0.2.1": [ { "comment_text": "", "digests": { "md5": "f366946c9b431d23f0c50195f48c7e9e", "sha256": "4d2982ec8cd7a0f6b019b661fa436a4ed3db5537d3715e491a5693bdc2be4f70" }, "downloads": -1, "filename": "phi-0.2.1.tar.gz", "has_sig": false, "md5_digest": "f366946c9b431d23f0c50195f48c7e9e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20697, "upload_time": "2016-12-09T16:33:16", "url": "https://files.pythonhosted.org/packages/d4/50/d63bee3c0756e8aab9848e90cf6a3fec563f12823c09496416191c874d87/phi-0.2.1.tar.gz" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "87976064c35cff35237b8d97dae18552", "sha256": "970fa28ed347ce50c15f328697f32814dc276784410b56e40067009c1947eab5" }, "downloads": -1, "filename": "phi-0.3.0.tar.gz", "has_sig": false, "md5_digest": "87976064c35cff35237b8d97dae18552", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22284, "upload_time": "2016-12-11T19:13:49", "url": "https://files.pythonhosted.org/packages/4d/99/4d310fcd5d253455249021d14e47367da0abe4a7f0e356fc304620a46376/phi-0.3.0.tar.gz" } ], "0.3.2": [ { "comment_text": "", "digests": { "md5": "fbf0aa394a692f5a067c51708e974db1", "sha256": "5c232ee91b99df5d63ccf8d9eefe7c884f8f525a1683edaae918b06f52386957" }, "downloads": -1, "filename": "phi-0.3.2.tar.gz", "has_sig": false, "md5_digest": "fbf0aa394a692f5a067c51708e974db1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 24481, "upload_time": "2016-12-12T14:57:47", "url": "https://files.pythonhosted.org/packages/ef/e1/d87808702c41a5850d650bf6510387bdd019395658ab4e54b66b2515a8ed/phi-0.3.2.tar.gz" } ], "0.3.3": [ { "comment_text": "", "digests": { "md5": "79d3b31dbef83408c69787e36ae189c4", "sha256": "37208a4e6ba98e8c94ba91bfb8fd01dae92984a8abea940bd07c7d4b8a11ff69" }, "downloads": -1, "filename": "phi-0.3.3.tar.gz", "has_sig": false, "md5_digest": "79d3b31dbef83408c69787e36ae189c4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 24998, "upload_time": "2016-12-13T04:08:48", "url": "https://files.pythonhosted.org/packages/67/6f/b4c97eb14a2b813d5be6324d4d9d19086fd5fe3cef90b0e9efc216c25b20/phi-0.3.3.tar.gz" } ], "0.4.1": [ { "comment_text": "", "digests": { "md5": "23659b0fa4cecc6758f52d67939f2373", "sha256": "038fd9f85564f2709fbdf6df76518400f53db0a0f8eb274454d3e482bc929598" }, "downloads": -1, "filename": "phi-0.4.1.tar.gz", "has_sig": false, "md5_digest": "23659b0fa4cecc6758f52d67939f2373", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 32717, "upload_time": "2016-12-17T19:38:05", "url": "https://files.pythonhosted.org/packages/89/5d/a2d7084a85112e65bcbfdb3551042f256fc3ade192e7da598b22f2b555fa/phi-0.4.1.tar.gz" } ], "0.5.0": [ { "comment_text": "", "digests": { "md5": "c04446b036153f14899f55c9361eee2b", "sha256": "dc843ded1ab6578f626c3d1ada696aa9142f46a7a0f917a0f8fb6c665adc6418" }, "downloads": -1, "filename": "phi-0.5.0.tar.gz", "has_sig": false, "md5_digest": "c04446b036153f14899f55c9361eee2b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 37679, "upload_time": "2016-12-26T19:40:06", "url": "https://files.pythonhosted.org/packages/24/38/f8af1ac7fe1450850c2aa8bdbd6d35f79089f6efc4de3b429f5a98ab9e9a/phi-0.5.0.tar.gz" } ], "0.6.0": [ { "comment_text": "", "digests": { "md5": "361d3e084bb865081c16b03ab077679b", "sha256": "739bfe7115143196fdcd05082f2bb8a9052f64aec7fcd280e45c120e6b6dd6f5" }, "downloads": -1, "filename": "phi-0.6.0.tar.gz", "has_sig": false, "md5_digest": "361d3e084bb865081c16b03ab077679b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 37324, "upload_time": "2016-12-27T18:04:24", "url": "https://files.pythonhosted.org/packages/95/5b/753f4e55ffd21c98c288209aab89b0f0fb08a74b7ad374e79b7694f246fe/phi-0.6.0.tar.gz" } ], "0.6.1": [ { "comment_text": "", "digests": { "md5": "0de1bc7e7ffbf2694fa1723dda749f1f", "sha256": "f93aaa161358b95667a7992b1f5d0634b5e9d8ab240f5966fad7c1fbff13b8e0" }, "downloads": -1, "filename": "phi-0.6.1.tar.gz", "has_sig": false, "md5_digest": "0de1bc7e7ffbf2694fa1723dda749f1f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 37418, "upload_time": "2016-12-28T03:26:30", "url": "https://files.pythonhosted.org/packages/6a/fa/098074e0316e2a216f45610c4b035388c3a225a238e2a0995ad4ae6e2399/phi-0.6.1.tar.gz" } ], "0.6.2": [ { "comment_text": "", "digests": { "md5": "8172482dd6d4a61108b91781d93ce330", "sha256": "83986a3b9d9c7ad4b3fa4e0d6ab215725781c34b4ee66e7b3ced746efb22c7b4" }, "downloads": -1, "filename": "phi-0.6.2.tar.gz", "has_sig": false, "md5_digest": "8172482dd6d4a61108b91781d93ce330", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 37647, "upload_time": "2016-12-28T13:48:06", "url": "https://files.pythonhosted.org/packages/39/d7/e52f62682ea701a9c54c0ce1d68c19f1a8692be2d95a3021717c5337d48c/phi-0.6.2.tar.gz" } ], "0.6.3": [ { "comment_text": "", "digests": { "md5": "760edc03706fea84251c27ae7bb506c7", "sha256": "a9f5f5e2ffd90ac5e87e86c0593a49ef6693e15498760bc4c8196f74df7902d4" }, "downloads": -1, "filename": "phi-0.6.3.tar.gz", "has_sig": false, "md5_digest": "760edc03706fea84251c27ae7bb506c7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 38303, "upload_time": "2016-12-30T13:57:09", "url": "https://files.pythonhosted.org/packages/52/36/ab0e92cb2ce1692400e775a812d957ef311effef9b2c15113a4665fe5dcd/phi-0.6.3.tar.gz" } ], "0.6.4": [ { "comment_text": "", "digests": { "md5": "1299c884ff7725a95d21cf284d5a794f", "sha256": "1696cad07983a6cf668234617ffe0206e86105f10711d175051ef9e3eb7a599e" }, "downloads": -1, "filename": "phi-0.6.4.tar.gz", "has_sig": false, "md5_digest": "1299c884ff7725a95d21cf284d5a794f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 38235, "upload_time": "2017-01-20T04:21:38", "url": "https://files.pythonhosted.org/packages/93/69/568aeabe3f22e28bf7dadf3dc20bb9e26d7201ec3b862420d2ceb837a4c4/phi-0.6.4.tar.gz" } ], "0.6.5": [ { "comment_text": "", "digests": { "md5": "6aea56094b217aa68caee866d9772b1a", "sha256": "2208eb8deb91703ef2a649170ab2942f1510c37556072d52a53c193926ce0404" }, "downloads": -1, "filename": "phi-0.6.5.tar.gz", "has_sig": false, "md5_digest": "6aea56094b217aa68caee866d9772b1a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 37950, "upload_time": "2017-06-13T16:37:49", "url": "https://files.pythonhosted.org/packages/22/00/24fff593273347abaeed60c8c0a9d72a452f91f5d4274174a04c45bf28fb/phi-0.6.5.tar.gz" } ], "0.6.7": [ { "comment_text": "", "digests": { "md5": "9bb9dc1ecbf03d365b8a0c703c4396d4", "sha256": "d49b9919ec4034b51c68960f7ead6a38c79104fcebb966508f06d43858df2066" }, "downloads": -1, "filename": "phi-0.6.7.tar.gz", "has_sig": false, "md5_digest": "9bb9dc1ecbf03d365b8a0c703c4396d4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 37825, "upload_time": "2018-04-08T03:23:15", "url": "https://files.pythonhosted.org/packages/57/b6/e6f9d363eab0bde823a1bde58ee65a080927e6d590c869b0e0782d5f88f7/phi-0.6.7.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "9bb9dc1ecbf03d365b8a0c703c4396d4", "sha256": "d49b9919ec4034b51c68960f7ead6a38c79104fcebb966508f06d43858df2066" }, "downloads": -1, "filename": "phi-0.6.7.tar.gz", "has_sig": false, "md5_digest": "9bb9dc1ecbf03d365b8a0c703c4396d4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 37825, "upload_time": "2018-04-08T03:23:15", "url": "https://files.pythonhosted.org/packages/57/b6/e6f9d363eab0bde823a1bde58ee65a080927e6d590c869b0e0782d5f88f7/phi-0.6.7.tar.gz" } ] }