{ "info": { "author": "Brad Gill", "author_email": "brad@alteredeffect.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3" ], "description": "[![npm](https://img.shields.io/npm/v/logipar)](https://npmjs.com/package/logipar) [![pypi](https://img.shields.io/pypi/v/logipar)](https://pypi.org/project/logipar/) [![haxelib install logipar](https://img.shields.io/badge/haxelib-logipar-orange, \"haxelib install logipar\")](https://lib.haxe.org/p/logipar/) [![php phar](https://img.shields.io/badge/php-Logipar.phar-orange, \"PHP Logipar.phar\")](https://github.com/altef/logipar/blob/master/php/Logipar.phar)\n\n

\n \"Logipar\"\n

\n\n# Logipar\n/l\u0251\u02d0d\u0292\u026ap\u025c\u02d0r/\n\n*noun*: Your go-to polyglot **logic string parser**.\n\n*verb*: Parse that logic string, my friend.\n\n\n# What is a logic string parser?\n\nHave you ever wanted to filter data based on a string of ANDs and ORs and NOTs? Well now you can! \nIn fact, that's exactly what **Logipar** is here to help you do.\n\nIf that doesn't help, check out our [Cat breed](https://altef.github.io/logipar) demo to see **Logipar** in action! Or you can try it yourself on [Runkit](https://npm.runkit.com/logipar).\n\n**Logipar** supports:\n* AND\n* OR\n* NOT\n* XOR\n* and Parenthises\n\nYou can rename the operatores however you want! You can also use it with basically whatever *literals/values* you want\u00b9. (1. There are *some* restrictions.)\n\nA simple example: `one AND (two OR three)` \nA more complex example: `title=Cat XOR title contains dog`. **Logipar** doesn't care about the literals you use, so you can add whatever complexity you is appropriate for your project there - I'm not judging! In fact, _I endorse it_.\n\nLogipar will automatically merge adjacent literals, _unless you don't want it to_. So `title contains dog` can count as a single literal, even without quotation marks. Or it can count as three `title`, `contains`, and `dog` - the choice is yours! (By default it merges.)\n\n# What can I do with it once it's parsed?\n\nOh, man, whatever you want really! You can test objects against the parse tree (using a function **Logipar** returns - [see the filtering section](#filtering-data)). You can flatten the parse tree to a string of your design (with the help of **Logipar**'s `stringify()` function - [see the stringing section](#stringification)). \nI guess.. I guess that's really all. But come on, what more do you even want??\n\n\n# Okay cool, can I use it though?\n**Logipar** is written in [Haxe](https://haxe.org). **WAIT DON'T BE SCARED**. All that means for you is it can be (and is) compiled to multiple languages. Maybe you've heard of one of these before:\n* Javascript\n* Python\n* PHP\n\nThere are more, but I'm going to focus the examples on those for now.\n\n# Installation\n#### Javascript\n* `npm i logipar`\n* `yarn add logipar`\n* Or you can just download and use [Logipar.js](https://github.com/altef/logipar/blob/master/js/logipar/Logipar.js) - say in a `\n```\n```javascript\n // Include the library however works for you. You can see how I did it in js_sample.html\n var lp = new Logipar();\n lp.parse(\"a AND b\");\n console.log(lp.stringify());\n```\n##### Python\n```python\n import logipar\n lp = logipar.Logipar()\n lp.parse(\"a AND b\")\n print(lp.toString())\n```\n##### Php\nWhen you're using the PHAR, it should take care of loading the classes for you.\n```php\n require_once(\"Logipar.phar\"); \n $lp = new \\logipar\\Logipar();\n $lp->parse(\"a AND b\");\n print($lp->toString());\n```\n\n## Quotations marks\nWhile **Logipar** can automatically merge neigbouring literals, sometimes that's not enough. It also supports quotation marks around literals. This means you can have values that would otherwise be parsed as tokens _in_ the literals - if they're wrapped in quotation marks. The quotation marks become part of the value, for you to deal with however you want. This is also handy for supplying whitespace as a value.\nTake the logic string `a=\"CAT OR DOG\" OR This is a sentance.`.\n`a=\"CAT OR DOG\"` is a literal. Even though it has and an OR in it. You can then use it however is appropriate - split it on the equals sign maybe, and strip the quotation marks to check for the string \"CAT OR DOG\" in the \"a\" column. I don't know, that's your journey!\n\n`This is a sentance.` is also a literal since it'll be automatically merged by default. **Logipar**'s `mergeAdjacentLiterals` (which defaults to `true`) controls this. So set it to false if you don't want to merge them.\n\nSo it'll parse out to: `a=\"CAT OR DOG\"` *OR* `This is a sentance.`.\n\nThe default quotation mark characters are `\"` and `'`. But you can add to or change these through **Logipar**'s aptly-named `quotations` property - which is an array of strings denoting whatever you want to use as valid quotation mark characters.\n\n##### Javascript\n```javascript\nlp.quotations.push(\"`\"); // Add backtick \nlp.mergeAdjacentLiterals = true; // This is its default value\n```\n##### Python\n```python\nlp.quotations.append(\"`\"); // Add backtick \nlp.mergeAdjacentLiterals = true; // This is its default value\n```\n##### Php\n```php\n$lp->quotations[] = \"`\"; // Add backtick\n$lp->mergeAdjacentLiterals = true; // This is its default value\n```\n\n## Case sensitivity\nBy default the **Logipar**'s operators are case-sensitive, but they don't have to be. Simply change the `caseSensitive` property to `false`, and YoU cAn TyPe ThEm HoWeVeR yOu Want!\n##### Javascript\n```javascript\nlp.caseSensitive = false;\n```\n##### Python\n```python\nlp.caseSensitive = False;\n```\n##### Php\n```php\n$lp->caseSensitive = false;\n```\n\n## Custom operators\nYou can also replace the default strings for any or all of **logipar**'s operators. Maybe you want to go the old `^ v` route. Or maybe `&&` and `||`. Or even just `+` and `*`. I don't know what you want!\nValid operators are:\n* `Token.AND` default: `AND`\n* `Token.OR` default: `OR`\n* `Token.XOR` default: `XOR`\n* `Token.NOT` default: `NOT`\n* `Token.OPEN` default: `(`\n* `Token.CLOSE` default: `)`\n\nYou should probably keep the `OPEN` and `CLOSE` (parentheses) operators as single characters, unless you want to enforce whitespace between all tokens tokens. \n\n##### Javascript\n```javascript\nlp.overwrite(logipar.Token.AND, \"&&\");\n```\n##### Python\n```python\nlp.overwrite(logipar.logipar_Token.AND, \"et\")\n```\n##### Php\nPHP is a little more problematic. `AND`, `OR` and `XOR` are keywords in it, which makes it difficult to access those Token constants. Rather than rename those variables, you can just use the string values. (You can do the same with the other ones, if you really want to - **all the string values are the same as the constant after `Token.`.**)\n```php\n$lp->overwrite(\"AND\", \"et\");\n```\n\n## Stringification\nSometimes you want your logic tree flattened; pressed firmly into a string. Maybe you just want to display it, or maybe you'd like to use it in your SQL. I don't know - and I'm not judging. **Logipar** should provide for all your stringifying needs with it's `stringify()` function.\nWhen you call `stringify`, you have the option of passing a function to it - this function is used to convert nodes to strings in any manner you like. It will be called on each node in the tree. Anything you don't account for will use the default `toString()` function provided by **Logipar**.\n\nThat's confusing right? Well here, take a look at this function in Haxe:\n\n```haxe\nfunction mystringer(n:logipar.Node):String {\n\tif (n.token.type == logipar.Token.XOR) {\n\t\treturn \"((\" + n.f(n.left) + \" AND NOT \" + n.f(n.right) + \") OR (NOT \" + n.f(n.left) + \" AND \" + n.f(n.right) + \"))\";\n\t}\n\treturn null;\n}\n```\n\nThere are some things going on there. Definite things. Let's start with the signature: Your function should take a `logipar.Node` as a param, and return a `String`. \nSay whatever we're using doesn't support an `XOR` operation. That's okay, `a XOR b` is just a fancy way of saying `(a AND NOT b) OR (NOT a AND b)`. We can handle that.\nFirst, we need to know if this is the type of node we want to change (`XOR`), so we check if the node passed in (`n`) is currently of type `logipar.Token.XOR`. Then we just return the string the way we want it. In this case:\n`\treturn \"((\" + n.f(n.left) + \" AND NOT \" + n.f(n.right) + \") OR (NOT \" + n.f(n.left) + \" AND \" + n.f(n.right) + \"))\";`\n\nBut wait. What's going on there? Well `XOR` nodes (and all binary nodes) have `left` and `right` properties, representing their preceeding and succeeding operands. (Unary nodes like `NOT` only have a `right` property; or rather, `left` will be null.) So we're just saying:\n`(({LEFT} AND NOT {RIGHT}) OR (NOT {LEFT} AND {RIGHT}))`\n\nWith one added wrinkle. The `left` and `right` properties are nodes themselves. They may contain `XOR`s of their own. So we want to recursively call the same stringification function on them. `f()` is a helper function available for the duration of the stringification process for this very purpose. That's why you see `n.f(n.left)` above.\n\nThe `return null;` lets **Logipar** know it should display any other node as usual. So in this case, anything that's not an `XOR` gets displayed as it normally would.\n\n##### Javascript\n```javascript\nvar str = lp.stringify(function(n) {\n\tif (n.token.type == logipar.Token.XOR) \n\t\treturn \"((\" + n.f(n.left) + \" AND NOT \" + n.f(n.right) + \") OR (NOT \" + n.f(n.left) + \" AND \" + n.f(n.right) + \"))\";\n\treturn null;\n});\n```\n##### Python\n```python\ndef expandXOR(n):\n\tif n.token.type == logipar.logipar_Token.XOR:\n\t\tl = n.f(n.left)\n\t\tr = n.f(n.right)\n\t\treturn \"(({} AND NOT {}) OR (NOT {} AND {}))\".format(l, r, l, r)\n\treturn None\n\nflattened = lp.stringify(expandXOR)\n```\n##### Php\nNote that again I'm just using the string \"XOR\" in PHP. Also, I'm using [call_user_func](https://www.php.net/call_user_func) to call `$n->f()` on the child nodes.\n```php\n$flattened = $lp->stringify(function($n) {\n\tif ($n->token->type == \"XOR\") {\n\t\t$l = call_user_func($n->f, $n->left);\n\t\t$r = call_user_func($n->f, $n->right);\n\t\treturn \"((\" . $l . \" AND NOT \" . $r . \") OR (NOT \" . $l . \" AND \" . $r . \"))\";\n\t}\n\treturn null;\n});\n\n```\n\nThe string returned will make use if minimal parentheses. If for some reason you want everything wrapped in brackets, that's easy too:\n\n```haxe\nlp.walk(function(n:logipar.Node):Void { n.bracketing = logipar.Node.MAXIMAL_BRACKETS; });\n```\nThat'll set the bracketing mode for each node in the tree to `MAXIMAL_BRACKETS`. If you only want to change certain node types, you can check the value in `n.token.type` and act accordingly.\n\n## Filtering data\nSometimes you just want to filter an array of rows. Nothing more, nothing less. Well, maybe more. Maybe you want to do it based on _a logic string_. \n**Logipar**'s `filterFunction` can help. It creates a function you can use to filter your data. But how does it work? You handle the leaves, and we'll handle the logic tree.\n\n> Basically, you just need to decide if a given leaf resolves `true` or `false` for a given row of data. And then we'll figure out if it matches overall.\n\nHere's an example in Haxe:\n\n```haxe\nvar leafresolver = function(row:Dynamic, value:String):Bool {\n\t// This is just checks a leaf node (value) against every column in the data (row), in a case-insensitive way. \n\t// But you can get as complex as you'd like and parse the value variable however you like.\n\tfor (f in Reflect.fields(row)) { // For each property of row\n\t\tif (Std.string(Reflect.field(row, f)).toLowerCase().indexOf(value.toLowerCase()) != -1) // If that property contains the leaf \n\t\t\treturn true;\n\t}\n\treturn false;\n}\nvar myfilter:(Dynamic)->Bool = ls.filterFunction(leafresolver);\n```\nOkay, so you can see above that **Logipar**'s `filterFunction()` takes a function as its argument, and returns a function. The first function (`leafresolver()`) we supply, the second we use to actually do our filtering.\n`leafresolver` takes a `row` of data. This is probably an object of some sort, but that's your journey. For the sake of our example, let's say it's `{title: \"Harry potter\", \"author\": \"J.K. Rowling\"}`.\nIt also takes a string `value`. This is the value of the leaf we're checking. For the sake of our example, `harry`.\n\nThe task of this function is to take `value` and see if it matches for `row`. You can do this however you want. This function is then run on every `LITERAL` (the leaves of the logic tree), and we use its result to decide if the logic tree resolves to `true` or `false` for `row`.\n\nFor this example, it'd check `Har` against each property in `row`: `title`, and `author`. Since the title is `Harry Potter`, and we've specified in the function to convert to lowercase before checking, it'll match and return `true`.\n\n`filterFunction` returns a function, whcih you can then use on your data. For example, `myfilter(data[i])` will return `true` or `false` depending on if it matches the logic of the query.\n\nThat's still pretty confusing, but hopefully some more examples will clear it up.\n\n\n\n\n##### Javascript\n```javascript\nfunction leafresolver(row, value) {\n\t// This is just checks the values against every column, in a case-insensitive way\n\tfor(var field in row)\n\t\tif (row[field].toString().toLowerCase().includes(value.toLowerCase()))\n\t\t\treturn true;\n\treturn false;\n}\nf = lp.filterFunction(leafresolver);\nfiltered_data = sample_data.filter(f); // Javascript arrays have a filter function\n```\n##### Python\n```python\ndef leafresolver(row, value):\n\t# This is just checks the values against every column, in a case-insensitive way\n\tfor field in row:\n\t\tif value.lower() in str(row[field]).lower():\n\t\t\treturn True\n\treturn False\n\nf = lp.filterFunction(leafresolver)\ndata = list(filter(f, data)) # Python has a filter function too\n```\n##### Php\n```php\n$leafresolver = function($row, $value) {\n\tforeach($row as $field=>$v)\n\t\tif (stripos($row[$field], $value) !== false)\n\t\t\treturn true;\n\treturn false;\n};\n\n$f = $lp->filterFunction($leafresolver);\n$data = array_filter($data, $f); // Oh look, so does PHP\n```\n\nNow, let's try a more complex example in Haxe:\n\n```Haxe\nvar f = ls.filterFunction(function(row:Dynamic, value:String):Bool {\n\tvalue = value.replace('\"', ''); // Strip out the quotation marks\n\tif (value.indexOf(\":\") == -1) { // If there's no colon, just check if the value exists in any field\n\t\tfor (f in Reflect.fields(row)) {\n\t\t\tif (Std.string(Reflect.field(row, f)).toLowerCase().indexOf(value.toLowerCase()) != -1)\n\t\t\t\treturn true;\n\t\t}\n\t} else {\n\t\t// There was a colon. Let's split it into field:value.\n\t\tvar chunks = value.split(':');\n\t\tvar field = chunks.shift(); // The field is before the first colon\n\t\tvar val = chunks.join(':'); // Any subsequent colons should be part of the value we look for\n\t\tif (Reflect.hasField(row, field)) { // If that field exists, check if the value is in it\n\t\t\tif (Std.string(Reflect.field(row, field)).toLowerCase().indexOf(val.toLowerCase()) != -1)\n\t\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n});\n```\n\nWhat this function does is it allows for values in the format `column:value` and then checks if `value` exists in that column. For example, a logic string we might support could be: `title:harry and not \"and\"`. This filter function will resolve true for any entries where:\n1. the title column contains \"harry\" (case-insensitive)\n2. the string \"and\" is not in any of the columns (case-insensitive)\n\n**To see some more filtering examples, check out: [docs/filters.md](https://github.com/altef/logipar/blob/master/docs/filters.md).**\n\n## The end\nThat's all for now. Happy parsing!\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/altef/logipar", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "logipar", "package_url": "https://pypi.org/project/logipar/", "platform": "", "project_url": "https://pypi.org/project/logipar/", "project_urls": { "Homepage": "https://github.com/altef/logipar" }, "release_url": "https://pypi.org/project/logipar/0.4.0/", "requires_dist": null, "requires_python": "", "summary": "A logic string parser", "version": "0.4.0" }, "last_serial": 5765537, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "d1485feb1984076358c0f5dae08b1282", "sha256": "d3c3aa18d816ac1ddda047eb52877056292e9ebb394db8c838f0ac41ad18789d" }, "downloads": -1, "filename": "logipar-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "d1485feb1984076358c0f5dae08b1282", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 13133, "upload_time": "2019-08-22T16:49:43", "url": "https://files.pythonhosted.org/packages/bb/15/7b1d2a68f32e001f0c394da74a4feb9bfa7af9159990e80a0446a9b1d338/logipar-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e085e7b010bc4b0668adf7dd8c9d179c", "sha256": "b8e5f6e6035ee2bc725b5c883777dc70668d38bf5f991af94b8c4238d7e8c41b" }, "downloads": -1, "filename": "logipar-0.1.0.tar.gz", "has_sig": false, "md5_digest": "e085e7b010bc4b0668adf7dd8c9d179c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12114, "upload_time": "2019-08-22T16:49:47", "url": "https://files.pythonhosted.org/packages/29/7b/5dadec4c22747303f43dc546c018cea5fa73c1036efadf97b5f5fe9feb60/logipar-0.1.0.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "8217f6cdc20433b2a130b251800b9b89", "sha256": "9e378ca712ba8caf9e10da8b123172ce6106a240aaa9895fa69b517dce6cb0ee" }, "downloads": -1, "filename": "logipar-0.2.0-py3-none-any.whl", "has_sig": false, "md5_digest": "8217f6cdc20433b2a130b251800b9b89", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 13098, "upload_time": "2019-08-22T16:49:45", "url": "https://files.pythonhosted.org/packages/6d/ab/cffbcce591daf909d7b4570bb3e270656f61c9d43175a73be55c2222a4f6/logipar-0.2.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "32fad597dd3867971cdcc1eff61efe52", "sha256": "1e2f2d6729bad99df4daac30546304ff8316d2562c777a22b614e19c107c31e6" }, "downloads": -1, "filename": "logipar-0.2.0.tar.gz", "has_sig": false, "md5_digest": "32fad597dd3867971cdcc1eff61efe52", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12092, "upload_time": "2019-08-22T16:49:49", "url": "https://files.pythonhosted.org/packages/44/f0/daab95c14f04a4970ef1b0c74ac252d36ba9dea26100b121e9ee373c76a8/logipar-0.2.0.tar.gz" } ], "0.2.1": [ { "comment_text": "", "digests": { "md5": "49d4d33df79a08d030f288491b3586fa", "sha256": "2d48bc1ba21198d27928b47ff80328f43477b5289906a21e3e20c94e18014915" }, "downloads": -1, "filename": "logipar-0.2.1-py3-none-any.whl", "has_sig": false, "md5_digest": "49d4d33df79a08d030f288491b3586fa", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 13135, "upload_time": "2019-08-22T16:54:58", "url": "https://files.pythonhosted.org/packages/cc/f1/d216d6e77a820c9e01ce94bc0a2d4dcbd4a49a3d993abce93b643890b7b9/logipar-0.2.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e2b7601293a8d37429d9d2de114e62a4", "sha256": "6246f73a0b8f05cae55461155744fcc199e4c0de43dea46fae6a3ac071ee2766" }, "downloads": -1, "filename": "logipar-0.2.1.tar.gz", "has_sig": false, "md5_digest": "e2b7601293a8d37429d9d2de114e62a4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12108, "upload_time": "2019-08-22T16:55:00", "url": "https://files.pythonhosted.org/packages/c6/67/6fff158148429289c5b5708bc9d3778691f5495442fddbb49c324d8283f3/logipar-0.2.1.tar.gz" } ], "0.2.2": [ { "comment_text": "", "digests": { "md5": "8374e488fc67f1b6b7d58995d2c30ce0", "sha256": "63240dab3441e7cbecf62bb521386018bed49c9ca574760149d6f8a411021778" }, "downloads": -1, "filename": "logipar-0.2.2-py3-none-any.whl", "has_sig": false, "md5_digest": "8374e488fc67f1b6b7d58995d2c30ce0", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 13144, "upload_time": "2019-08-22T17:06:19", "url": "https://files.pythonhosted.org/packages/e2/9f/d8df6b749b8b62e6cdfc017f02b844502b2d5ce863147b5fe92c8ad58dd9/logipar-0.2.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "dbd137da82c64ff840abc83ea606b9f4", "sha256": "fe4609c67636aac1e21ebd9c09cdee042c34e9afcda02d9b49ec8a66de69a4fb" }, "downloads": -1, "filename": "logipar-0.2.2.tar.gz", "has_sig": false, "md5_digest": "dbd137da82c64ff840abc83ea606b9f4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12115, "upload_time": "2019-08-22T17:06:21", "url": "https://files.pythonhosted.org/packages/eb/67/93bfb56431e9fdc52bd3762d6e6c5cd0e92a3a0a373efb401414c619dc1c/logipar-0.2.2.tar.gz" } ], "0.2.3": [ { "comment_text": "", "digests": { "md5": "65734e1042398e0e2ed223553c601c4a", "sha256": "702a110a86aac9790b20a57c6b85f1cd356ac91cc11c41127afa4345e3926e39" }, "downloads": -1, "filename": "logipar-0.2.3-py3-none-any.whl", "has_sig": false, "md5_digest": "65734e1042398e0e2ed223553c601c4a", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 13797, "upload_time": "2019-08-23T19:09:34", "url": "https://files.pythonhosted.org/packages/ee/72/f35ba1e17fb393412b0106ad7809ebb4f79a99d4dbcac567f46cae736e4a/logipar-0.2.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a74da4d1680a34ed2100a5fdb3b43f56", "sha256": "a5a3d048ce6e40db173ce6a11f4089c16b31cfae308985da4b5ac9e5a106baad" }, "downloads": -1, "filename": "logipar-0.2.3.tar.gz", "has_sig": false, "md5_digest": "a74da4d1680a34ed2100a5fdb3b43f56", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12802, "upload_time": "2019-08-23T19:09:37", "url": "https://files.pythonhosted.org/packages/9e/2d/3a73acd682098aff22ccc33c18e66fda296dfd563eb538e761b7c21ed0b8/logipar-0.2.3.tar.gz" } ], "0.2.4": [ { "comment_text": "", "digests": { "md5": "458e2e22875ae7146d40b4670b3c6b63", "sha256": "2f4aa79994a8e4cb402d307f8fe079386430d389419276ce146ae1dd078db515" }, "downloads": -1, "filename": "logipar-0.2.4-py3-none-any.whl", "has_sig": false, "md5_digest": "458e2e22875ae7146d40b4670b3c6b63", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 13800, "upload_time": "2019-08-23T19:20:40", "url": "https://files.pythonhosted.org/packages/2a/b2/9f71bd38709147d5c467e5f0c4cd7022a4992f452b3cba8e4626e72c3c1e/logipar-0.2.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f2a781e907c6b6bcbf1032d39de0c936", "sha256": "c16707f19e6240e08336818b5043507f1e6037ab725317d4e1a67d4a2578d28f" }, "downloads": -1, "filename": "logipar-0.2.4.tar.gz", "has_sig": false, "md5_digest": "f2a781e907c6b6bcbf1032d39de0c936", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12805, "upload_time": "2019-08-23T19:20:44", "url": "https://files.pythonhosted.org/packages/c5/6b/98288a235ba029cec7a189a792e6dc39c197c56e0d869506fb3b123c6994/logipar-0.2.4.tar.gz" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "9d4b3e85c50c90a25f1e905e1399c049", "sha256": "620b86014063769ea8b7de75d8a6b14e9ca6b67b4bfbf511ba9f737a4f6ad095" }, "downloads": -1, "filename": "logipar-0.3.0-py3-none-any.whl", "has_sig": false, "md5_digest": "9d4b3e85c50c90a25f1e905e1399c049", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 14452, "upload_time": "2019-08-27T03:15:38", "url": "https://files.pythonhosted.org/packages/6b/15/47941d0d3a5a4f2cb1496c97268acffec65d12faee05d67bff2939136b16/logipar-0.3.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8ef6b94e74985f6a6dd601039af8b994", "sha256": "08c02af90244e92cd400dea69c449b6efdfc54660b13e91e761210e161aa991c" }, "downloads": -1, "filename": "logipar-0.3.0.tar.gz", "has_sig": false, "md5_digest": "8ef6b94e74985f6a6dd601039af8b994", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13545, "upload_time": "2019-08-27T03:15:44", "url": "https://files.pythonhosted.org/packages/ff/c0/8ebac24db8ab63fb1c2d39a4a16ab3cda8cfc33169b17d6da89ecdbdcd94/logipar-0.3.0.tar.gz" } ], "0.3.1": [ { "comment_text": "", "digests": { "md5": "ae732c7a999a4152beb0110b7ce0feab", "sha256": "57d8974cd782a20237e76a02b8b112004236e1b33c6847396e9920f57caf2897" }, "downloads": -1, "filename": "logipar-0.3.1-py3-none-any.whl", "has_sig": false, "md5_digest": "ae732c7a999a4152beb0110b7ce0feab", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 14267, "upload_time": "2019-08-30T15:25:40", "url": "https://files.pythonhosted.org/packages/31/12/aade909a1e99cdce073cdf0e656369bdcdc8497d2281e17440f5e3b0f26f/logipar-0.3.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "806a1a74eb6036a9606ac5baa0aaf41e", "sha256": "3c4f158742b5522ac04f0e81406ffeeb7b93a416f5c5d3e31934a733a3f96009" }, "downloads": -1, "filename": "logipar-0.3.1.tar.gz", "has_sig": false, "md5_digest": "806a1a74eb6036a9606ac5baa0aaf41e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13323, "upload_time": "2019-08-30T15:25:45", "url": "https://files.pythonhosted.org/packages/a0/cb/c8a69bc21aac08b65ff0c8d443ff50590041f86a7d8fe51c9893115ae301/logipar-0.3.1.tar.gz" } ], "0.4.0": [ { "comment_text": "", "digests": { "md5": "b0243c2747c4efe13417f484046a1db0", "sha256": "da72ab6b920dc9625ce643e2c73e4b217172fec488f96c68db547ca7613eda8b" }, "downloads": -1, "filename": "logipar-0.4.0-py3-none-any.whl", "has_sig": false, "md5_digest": "b0243c2747c4efe13417f484046a1db0", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 14829, "upload_time": "2019-08-31T23:22:16", "url": "https://files.pythonhosted.org/packages/83/da/3444f8b2f61033b7c3f11068b0168b9567ea2f763fc26afc7a79b26a7f98/logipar-0.4.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "48e31270cb3c70abb8adcb906eac3c7f", "sha256": "8d432874e27085ac9e4ba6e031e9ea8148af70b53d291c12fcce68ea6de898cc" }, "downloads": -1, "filename": "logipar-0.4.0.tar.gz", "has_sig": false, "md5_digest": "48e31270cb3c70abb8adcb906eac3c7f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13891, "upload_time": "2019-08-31T23:22:22", "url": "https://files.pythonhosted.org/packages/33/f6/f441c5a2564187c76dd2a240b02284438a72666f4823ac08c3332c4d2c27/logipar-0.4.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b0243c2747c4efe13417f484046a1db0", "sha256": "da72ab6b920dc9625ce643e2c73e4b217172fec488f96c68db547ca7613eda8b" }, "downloads": -1, "filename": "logipar-0.4.0-py3-none-any.whl", "has_sig": false, "md5_digest": "b0243c2747c4efe13417f484046a1db0", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 14829, "upload_time": "2019-08-31T23:22:16", "url": "https://files.pythonhosted.org/packages/83/da/3444f8b2f61033b7c3f11068b0168b9567ea2f763fc26afc7a79b26a7f98/logipar-0.4.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "48e31270cb3c70abb8adcb906eac3c7f", "sha256": "8d432874e27085ac9e4ba6e031e9ea8148af70b53d291c12fcce68ea6de898cc" }, "downloads": -1, "filename": "logipar-0.4.0.tar.gz", "has_sig": false, "md5_digest": "48e31270cb3c70abb8adcb906eac3c7f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13891, "upload_time": "2019-08-31T23:22:22", "url": "https://files.pythonhosted.org/packages/33/f6/f441c5a2564187c76dd2a240b02284438a72666f4823ac08c3332c4d2c27/logipar-0.4.0.tar.gz" } ] }