{ "info": { "author": "Andrew Gree", "author_email": "andrew@criticalhop.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.7", "Topic :: Scientific/Engineering :: Artificial Intelligence", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "# Poodle - AI Planning in Python\n\n![PyPI - Status](https://img.shields.io/pypi/status/poodle) [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![PyPI version](https://badge.fury.io/py/poodle.svg)](https://badge.fury.io/py/poodle) [![Build Status](https://travis-ci.org/criticalhop/poodle.svg?branch=master)](https://travis-ci.org/criticalhop/poodle)\n\nPoodle is the Python-to-PDDL compiler and automated programming framework in an early stage of development.\n\n# Rationale\n\n[PDDL](https://en.wikipedia.org/wiki/Planning_Domain_Definition_Language) is a widely-used language to describe [AI planning](https://en.wikipedia.org/wiki/Automated_planning_and_scheduling) [domains](http://www.cs.toronto.edu/~sheila/2542/w09/A1/introtopddl2.pdf). The applications include various [robotic planning problems](https://kcl-planning.github.io/ROSPlan/), scheduling, [logistics](https://github.com/pellierd/pddl4j/wiki/Logistics:-a-simple-running-example) and [manufacturing](https://ocharles.org.uk/posts/2018-12-25-fast-downward.html) optimization, writing intelligent agents in [computer games](https://www.researchgate.net/publication/228724581_Real-Time_Planning_for_Video-Games_A_Purpose_for_PDDL), real-time decision making, and even automated unix administration [[1]](https://www.youtube.com/watch?v=2veFbpiQv4k) [[2]](http://www.aiai.ed.ac.uk/project/oplan/oplan/applications.html). AI planning, and specifically model-based planning, can be explained as a problem-solving method where the software developer describes (models) a problem, rather than codes the algorithm to solve the problem - which is radically different from how the conventional software development is practically-always done today. Not having to invent and code the algorithm has obvious benefits: developer productivity goes to extremes, you can write software with humanly-impossible complexity of algorithms, any tasks that require combining actions into meaningful chains can now be automated.\n\nBut despite these extreme gains, AI planning-based software is virtually nonexistent. And there are reasons why imperative programming is so popular and logic programming is not. Imperative programming has a much lower barrier of entry. Realistically, the majority of problems are much easier to code in a \"usual\" imperative way rather than modeling the full domain. The tooling, ecosystem, coding paradigms, and the language itself are much more polished and well-designed. Finally, many software libraries and components were written, and are readily available, in imperative programming languages. \n\nPoodle aims to change that. The goal is to create a \"native merge\" of Python and model-based planning. This means that the developer will have an option to either write the algorithm or describe the problem and let the AI figure out the algorithm - with the result as usable in both options. The goal is to develop all the necessary tooling to enable full-scale production use of AI planning in real-world computing tasks - building on the top of a strong foundation created by the Python community.\n\nTranslating full Python programs into planning domain enables the use of efficient search methods to compose pre-built Python libraries into new algorithms. And a developer always gets an alternative to use the code imperatively - whenever she desires to switch. \n\n# Quickstart\n\n```shell\n$ pip install poodle # needs Python 3.7+\n```\n\nLet's say you have:\n\n```python\nfrom poodle import Object, xschedule\n\nclass World(Object): \n prepared: int\n said: bool \n\ndef hello(world: World):\n assert world.said == False\n world.prepared += 1\n\ndef world(world: World):\n assert world.prepared > 0\n world.said = True\n return \"Hello, world!\"\n\nw = World()\nw.prepared = 0\nw.said = False\n```\n\nNow you have two options:\n\n1. (obvious) execute natively, if you know the algorithm\n\n```python\nhello(w)\nprint(world(w)) \n# -> \"Hello, World!\"\n```\n\n2. if you don't know the parameters and/or sequence of execution - ask AI to figure out\n\n```python\nprint(xschedule(methods=[world, hello], space=[w], goal=lambda:w.said==True))\n# -> \"Hello, World!\"\n```\n\nThis will run the code on a hosted solver. To run a local solver, please scroll down to *Installation* section.\n\n# Overview\n\n## Introduction\n\nPoodle is a Python module that enables construction of complex planning and constraint satisfaction problems using familiar Pythonic paradigms in production environments. It is still in the early stage of development, but is already powering [*kubectl‑val*, our tool to prevent Kubernetes configuration errors](https://github.com/criticalhop/kubectl-val).\n\nPoodle introduces a pair of Python functions called `xschedule` and `schedule` that implement an automated planning mechanism, and a new base object `Object`:\n\n```python\nxschedule(\n methods=[...], # methods\n space=[...], # objects\n goal=lambda: ... # condition for final object state\n)\n```\n\nwhere `methods` is the list of methods that the planner should use to try to reach the goal state; `space` contains the list of `Object` objects that the planner will try to use as parameters for the methods, and `goal` is a simple end-state condition expressed as Python logical expression, usually a `lambda` function.\n\n`Object` is a special object type that knows how to translate itself to PDDL.\n\nTo understand how to construct a problem, let's start with a classic \"Hello, World\" function:\n\n```python\nfrom poodle import Object, xschedule\n\nclass World(Object): # a class that defines object that will hold final state\n said: bool # declaration of a bollean variable (Python 3 type hints)\n\ndef hello(world: World): # annotated function that mutates the state of `world`\n assert world.said == False # hint for the planner when this call is valid\n print(\"Hello, World!\")\n world.said = True # mutate the state of the parameter object\n\nw = World() # create first object instance\nw.said = False # define the value for `said` attribute\n\n# now execute this in an unfamiliar way ... \nxschedule(methods=[hello], space=[w], goal=lambda:w.said==True)\n```\n\nThis program will immediately print \"Hello, World!\" to the console, which looks obvious at first. What actually happened is that Poodle compiled your Python method into PDDL domain + problem and used AI planner to find that the final state is achievable by simply executing the only method, and all `assert`s are satisfied with our hero object `w`.\n\nIt is important to note that the more precisely you describe your task, the easier it is for the AI planner to figure out the algorithm. That is why Poodle enforces fully statically typed interface for all objects and methods in search space as a minimum selectivity requirement. This also saves you from a lot of bugs in bigger projects.\n\nLet's now jump to a more sophisticated example:\n\n## Monkey and Banana problem\n\n

\n\nWe need to plan monkey actions to get the hanging banana: move the box, climb the box and grasp the bananas. We also need to take into account some basic laws of nature: like if the banana is on the tree, it's location is where the tree is.\n\n```python\nfrom poodle import Object, schedule\nfrom typing import Set\n\nclass Position(Object):\n def __str__(self):\n if not hasattr(self, \"locname\"): return \"unknown\"\n return self.locname\nclass HasHeight(Object):\n height: int\nclass HasPosition(Object):\n at: Position\nclass Monkey(HasHeight, HasPosition): pass\nclass PalmTree(HasHeight, HasPosition): \n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self.height = 2\nclass Box(HasHeight, HasPosition): pass\nclass Banana(HasHeight, HasPosition): \n owner: Monkey\n attached: PalmTree \nclass World(Object):\n locations: Set[Position]\n\n\np1 = Position()\np1.locname = \"Position A\"\np2 = Position()\np2.locname = \"Position B\"\np3 = Position()\np3.locname = \"Position C\"\n\nw = World()\nw.locations.add(p1)\nw.locations.add(p2)\nw.locations.add(p3)\n\nm = Monkey()\nm.height = 0 # ground\nm.at = p1\n\nbox = Box()\nbox.height = 2\nbox.at = p2\n\np = PalmTree()\np.at = p3\n\nb = Banana()\nb.attached = p\n\ndef go(monkey: Monkey, where: Position):\n assert where in w.locations\n assert monkey.height < 1, \"Monkey can only move while on the ground\"\n monkey.at = where\n return f\"Monkey moved to {where}\"\n\ndef push(monkey: Monkey, box: Box, where: Position):\n assert monkey.at == box.at\n assert where in w.locations\n assert monkey.height < 1, \"Monkey can only move the box while on the ground\"\n monkey.at = where\n box.at = where\n return f\"Monkey moved box to {where}\"\n\ndef climb_up(monkey: Monkey, box: Box):\n assert monkey.at == box.at\n monkey.height += box.height\n return \"Monkey climbs the box\"\n\ndef grasp(monkey: Monkey, banana: Banana):\n assert monkey.height == banana.height\n assert monkey.at == banana.at\n banana.owner = monkey\n return \"Monkey takes the banana\"\n\ndef infer_owner_at(palmtree: PalmTree, banana: Banana):\n assert banana.attached == palmtree\n banana.at = palmtree.at\n return \"Remembered that if banana is on palm tree, its location is where palm tree is\"\n\ndef infer_banana_height(palmtree: PalmTree, banana: Banana):\n assert banana.attached == palmtree\n banana.height = palmtree.height\n return \"Remembered that if banana is on the tree, its height equals tree's height\"\n\nprint('\\n'.join(x() for x in schedule(\n [go, push, climb_up, grasp, infer_banana_height, infer_owner_at],\n [w,p1,p2,p3,m,box,p,b],\n goal=lambda: b.owner == m)))\n```\n\nthis program solves the slightly modified [\"Monkey and banana\" planning problem](http://www.inf.ed.ac.uk/teaching/courses/ai2/module4/stripsEG.pdf) and produces the result:\n\n```\n$ pip install poodle\n$ python ./monkey.py\nMonkey moved to Position B\nRemembered that if banana is on the tree, its height equals tree's height\nRemembered that if banana is on palm tree, its location is where palm tree is\nMonkey moved box to Position C\nMonkey climbs the box\nMonkey takes the banana\n```\n\n## Kubernetes Configuration Problem\n\n

\n\nIn this example, we are checking what are the consequences of configuration changes in a Kubernetes cluster. For example, the Kubernetes current state may be that once you load a new DaemonSet, important services will get evicted from the cluster in order to place your new microservice.\n\nFor a complete program, feel free to check out [`kubectl-val` source code](https://github.com/criticalhop/kubectl-val).\n\n# Principles and Architecture\n\nPoodle compiles Python into PDDL and uses [fast-downward](http://www.fast-downward.org/) to run the search. As a typical real-world problem requires huge amounts of RAM, the whole solver bundle is running as an HTTP service in current architecture.\n\n## Composability\n\nSupport for nested `xschedule` is on the roadmap for planning code composability, although Python already provides excellent composability mechanisms.\n\n## Readability and Debuggability\n\nBad readability and debuggability have always plagued logic languages, and Poodle is not an exception: it is hard to tell what the result would be just by reading the code, as multiple methods can be executed concurrently and in any order. To address this problem, adding a visual debugger based on [VOWL](http://vowl.visualdataweb.org/webvowl.html) is planned. Although a combination of good code design and classical Python REPL, plus some CLIPS inferencer tricks, allowed us to rapidly develop quite sophisticated AI planning-based software.\n\n# Documentation\n\nThere is no documentation at this point, but we promise to provide it as `poodle` evolves. If you would like to experiment with Poodle, the general recommendation is to start from reading the examples, unit tests and the `kubectl-val` project source.\n\n# Installation\n\n```shell\npip install poodle\n```\n\nPoodle requires Python 3.7+ and will drop support for Python 3.7 as soon as 3.8 is stable due to heavy use of type hinting features.\n\n# Running Local Solver\n\nBy default, Poodle will check if local port `16009` is open and use a solver running on localhost. If it can not find a local solver it will use a hosted solver environment from [CriticalHop](https://criticalhop.com), which has some limitations in its free-to-use version. \n\nTo run a local solver, you must first install [fast-downward](http://www.fast-downward.org/). After you have fast-downward running, run `poodleserver` (included with `poodle`) from the fast-downward folder:\n\n```shell\ncd fast-downward\npoodleserver\n```\n\nYou can also specify the solver URL by environment variable `POODLE_SOLVER_URL`, e.g.:\n\n```shell\nexport POODLE_SOLVER_URL=http://localhost:8082\n```\n\n## Problem Sharing\n\nIf you would like to support development of AI planners, we kindly ask you to opt-in for sharing of anonymized PDDL problem data sets. This will help us to continuously improve result waiting times for everyone in the community. To enable this, please set `POODLE_STATS=1` environment variable when launching `poodleserver`. Your privacy is our highest priority, therefore we only collect generated anonymized PDDL data-sets.\n\n# Developing\n\nPoodle development is set up with `tox` and `poetry`. To run all tests locally, compile [fast-downward](http://www.fast-downward.org/) in a folder named `downward` near your poodle clone, then do\n\n```shell\nPOODLE_SOLVER_URL=http://localhost:12345 tox\n```\n\nwhere `12345` is the port of your choosing. This command will run local solver and all tests sequentially. You may then use `poetry` to manage your local installation. \n\nThe correct folder structure for poodle development is:\n\n```\npoodle-dev/\n downward/ -> Fast-Downward installation\n poodle/ -> Poodle repository clone\n```\n\n# Contacts\n\nPoodle is maintained by [CriticalHop](https://criticalhop.com), a team of dedicated AI planning engineers. If you have any questions, feel free to open a [github issue](https://github.com/criticalhop/poodle/issues) and chat with @grandrew and the team at `##poodle` on [freenode](https://freenode.net/).\n\nPoodle is a very large project and will not be possible without community support. Translating full Python into planning domain, and writing a fast solver that understands itself, is an absolutely wild idea that one day may become an important step towards the Artificial General Intelligence. So we are committed to Open Source and welcome any help.\n\nIf you are interested in joining the project, please write us at info@criticalhop.com or reach out directly to me at andrew@criticalhop.com or [@Andrew_Gree](https://twitter.com/Andrew_Gree) on twitter.\n\n-- \nAndrew Gree and the team\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/criticalhop/poodle", "keywords": "ai,ai-planning,constraint-programming,automated-planning,optimization,compilers", "license": "BSD-3-Clause", "maintainer": "Andrew Gree", "maintainer_email": "andrew@criticalhop.com", "name": "poodle", "package_url": "https://pypi.org/project/poodle/", "platform": "", "project_url": "https://pypi.org/project/poodle/", "project_urls": { "Homepage": "https://github.com/criticalhop/poodle", "Repository": "https://github.com/criticalhop/poodle" }, "release_url": "https://pypi.org/project/poodle/0.1.10/", "requires_dist": [ "requests (>=2.22,<3.0)", "flask (>=1.1,<2.0)" ], "requires_python": ">=3.7", "summary": "Python AI Planning and automated programming", "version": "0.1.10" }, "last_serial": 5956375, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "b3e76ad717db11efec53b572bbc9b502", "sha256": "fc863496fdb460a1500f1ae3144a3a764f7c4fa657cd63db81112c8af5d3a224" }, "downloads": -1, "filename": "poodle-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "b3e76ad717db11efec53b572bbc9b502", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.7", "size": 34941, "upload_time": "2019-09-10T02:52:40", "url": "https://files.pythonhosted.org/packages/25/4c/c11675408c6f1c66c887ca1eb8d19743f5bbdb459fe197d620fef08387e6/poodle-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "106449d892e6c1a55b85fcd13b516a34", "sha256": "7e4a7c71ff3a3d3df9a15135c1d8fc71532eb1b2d7e2d38255a82558f28fb1e8" }, "downloads": -1, "filename": "poodle-0.1.0.tar.gz", "has_sig": false, "md5_digest": "106449d892e6c1a55b85fcd13b516a34", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.7", "size": 34388, "upload_time": "2019-09-10T02:52:42", "url": "https://files.pythonhosted.org/packages/b1/bb/9812320679d3824181f4ae535a0664a95ef7f1295bd5e63995aa2880cd18/poodle-0.1.0.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "7a6592f5cb7b47ba69a3590353cbea08", "sha256": "7953b71b4e758a9a09936016211807c06738026fd246e6dae5f8eefa76cb5d2d" }, "downloads": -1, "filename": "poodle-0.1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "7a6592f5cb7b47ba69a3590353cbea08", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.7", "size": 35042, "upload_time": "2019-09-10T03:07:32", "url": "https://files.pythonhosted.org/packages/9c/16/7439b9827474261837ef52d803fd682a85e30f7eac9ffb477589173b12b7/poodle-0.1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "cdeb2204dd652762c35caa58c935cd07", "sha256": "4b8b4c5abd7db81a473f948350d6e29ecdee0153640c0406d234055b063788a9" }, "downloads": -1, "filename": "poodle-0.1.1.tar.gz", "has_sig": false, "md5_digest": "cdeb2204dd652762c35caa58c935cd07", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.7", "size": 34557, "upload_time": "2019-09-10T03:07:34", "url": "https://files.pythonhosted.org/packages/2b/00/7a6fd243ae7a409573b9e6359ebb597fd4c596ea40431e46e034899dbe73/poodle-0.1.1.tar.gz" } ], "0.1.10": [ { "comment_text": "", "digests": { "md5": "66a0ff2746d4628e5c5d0c37151a2786", "sha256": "eb1db2b4097868d7e4e740abfd4e07e02f02e1b5cabbb1e0c273635187258fd1" }, "downloads": -1, "filename": "poodle-0.1.10-py3-none-any.whl", "has_sig": false, "md5_digest": "66a0ff2746d4628e5c5d0c37151a2786", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.7", "size": 48417, "upload_time": "2019-10-10T19:03:14", "url": "https://files.pythonhosted.org/packages/39/40/8da15101109e68a5fbc6d0afc2b130df5c938ea401645ae31b2ee335287a/poodle-0.1.10-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "cc1930806fbf0c680b01e801449840d4", "sha256": "a85c892ccabb7d6fc7f7974b98f714c0409b61483f0377287c806a30919c6d87" }, "downloads": -1, "filename": "poodle-0.1.10.tar.gz", "has_sig": false, "md5_digest": "cc1930806fbf0c680b01e801449840d4", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.7", "size": 52376, "upload_time": "2019-10-10T19:03:16", "url": "https://files.pythonhosted.org/packages/7f/9c/9a151e359c7a2fb0ab0b704d25b25b81aaac61fb7ead6d1bdb436049d459/poodle-0.1.10.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "d18ce0666125eb13fa73a70a558ec40e", "sha256": "4aaf7013b0912f57f1e083fb8ea12bee77e74023210afa80ee4120c508805d1c" }, "downloads": -1, "filename": "poodle-0.1.2-py3-none-any.whl", "has_sig": false, "md5_digest": "d18ce0666125eb13fa73a70a558ec40e", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.7", "size": 43015, "upload_time": "2019-09-10T23:26:34", "url": "https://files.pythonhosted.org/packages/a3/8b/46b62f120306c4df434881da9f3e92d2d52c3380369546a502275268eea1/poodle-0.1.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "fe24da2e3fef356396548c2484f53061", "sha256": "9ca03bd68ca917792c8c48af4ba5437904ad58beaa683f422d2d09997d58c64f" }, "downloads": -1, "filename": "poodle-0.1.2.tar.gz", "has_sig": false, "md5_digest": "fe24da2e3fef356396548c2484f53061", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.7", "size": 42204, "upload_time": "2019-09-10T23:26:36", "url": "https://files.pythonhosted.org/packages/7a/5d/4c396cbf257a88e226f29b43020f5c57838b6fff0e438124299a05148b92/poodle-0.1.2.tar.gz" } ], "0.1.3": [ { "comment_text": "", "digests": { "md5": "dc02be2e6145cf7bd866b22ee572085b", "sha256": "31720f6c821317a420e66ac4fb934795818db7bffb9978af1e2cd1e7545af0b4" }, "downloads": -1, "filename": "poodle-0.1.3-py3-none-any.whl", "has_sig": false, "md5_digest": "dc02be2e6145cf7bd866b22ee572085b", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.7", "size": 43116, "upload_time": "2019-09-10T23:34:36", "url": "https://files.pythonhosted.org/packages/20/0f/2d764adfc5d129551f8b2afc124f3c4556efe21daac18f3b224cdfa30191/poodle-0.1.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "aa467f441f7707f4d10c1fcd7a9df4c4", "sha256": "8cc89ed0cb655ee8a3bd3fa9ead434502812631a58abd138223752456dd5f902" }, "downloads": -1, "filename": "poodle-0.1.3.tar.gz", "has_sig": false, "md5_digest": "aa467f441f7707f4d10c1fcd7a9df4c4", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.7", "size": 42283, "upload_time": "2019-09-10T23:34:38", "url": "https://files.pythonhosted.org/packages/91/2d/ade364b79d669f4439b505e513e47538338bcb9df426bc92ec6d6cd670eb/poodle-0.1.3.tar.gz" } ], "0.1.4": [ { "comment_text": "", "digests": { "md5": "78570d8f4bfd92a74c4fed83cc658b5c", "sha256": "5331f265922bb0cf4b6f503ec217e6d001cdbd96cab3b946ba198ef10109be6b" }, "downloads": -1, "filename": "poodle-0.1.4-py3-none-any.whl", "has_sig": false, "md5_digest": "78570d8f4bfd92a74c4fed83cc658b5c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.7", "size": 43775, "upload_time": "2019-09-11T04:10:31", "url": "https://files.pythonhosted.org/packages/80/6a/3ce0780f34f10d824c1a95748a0dc20c332368049f928d0989c5a541a915/poodle-0.1.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "6030c1c7684ca0f7ac1e4a84e8646af0", "sha256": "44c5003f37a2ee9a8ffa74e51bed868750b6f53fc8234bf00810cbafc39a7ae7" }, "downloads": -1, "filename": "poodle-0.1.4.tar.gz", "has_sig": false, "md5_digest": "6030c1c7684ca0f7ac1e4a84e8646af0", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.7", "size": 43167, "upload_time": "2019-09-11T04:10:33", "url": "https://files.pythonhosted.org/packages/76/70/19c55f3a3e81bbd9e19cfbc45c4bc71bf744f4f63ab147bc3d46254eb9af/poodle-0.1.4.tar.gz" } ], "0.1.5": [ { "comment_text": "", "digests": { "md5": "7e0cf262e90aae86bb7c3e48a7db3cb4", "sha256": "a17d3e8f70f465fa026ad4c3a5e9611428fb43bf90db9b95f8eb1d8637641663" }, "downloads": -1, "filename": "poodle-0.1.5-py3-none-any.whl", "has_sig": false, "md5_digest": "7e0cf262e90aae86bb7c3e48a7db3cb4", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.7", "size": 47054, "upload_time": "2019-09-17T01:21:23", "url": "https://files.pythonhosted.org/packages/68/15/1ba1476474f7ea403d3c102606d744cc4777f877b02f6f37f6e2ca3e62ef/poodle-0.1.5-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "49ab3f82a0ab4b25fb74638de97827e1", "sha256": "cfa6c1600b357634f8e894c07cace1d9845e0a2126d0859c686b1c0e94a67c78" }, "downloads": -1, "filename": "poodle-0.1.5.tar.gz", "has_sig": false, "md5_digest": "49ab3f82a0ab4b25fb74638de97827e1", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.7", "size": 49789, "upload_time": "2019-09-17T01:21:25", "url": "https://files.pythonhosted.org/packages/51/0b/6cb59b8111135fc4d7b7c438a784c2ea16f2cfc633ebd7f9aafd4467364f/poodle-0.1.5.tar.gz" } ], "0.1.6": [ { "comment_text": "", "digests": { "md5": "da365ea3aa1ee8598bf4b40b83cce2fa", "sha256": "0d59858f47bf843dacd0c2d30ec348fc4bf6f02061b14cab022b5b9e77a5916f" }, "downloads": -1, "filename": "poodle-0.1.6-py3-none-any.whl", "has_sig": false, "md5_digest": "da365ea3aa1ee8598bf4b40b83cce2fa", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.7", "size": 47340, "upload_time": "2019-09-18T02:14:48", "url": "https://files.pythonhosted.org/packages/b0/25/7d38f5d38cdcc29cf66680ce93afe541612c41337baeecd9ef1a5b4a3c62/poodle-0.1.6-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "76868e6c27ec6c6be7574b6777c8bd80", "sha256": "aa413aebaceb6d034b16e5c18aac9fa3d524c5ad95960b539e88923858e55bcf" }, "downloads": -1, "filename": "poodle-0.1.6.tar.gz", "has_sig": false, "md5_digest": "76868e6c27ec6c6be7574b6777c8bd80", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.7", "size": 50349, "upload_time": "2019-09-18T02:14:50", "url": "https://files.pythonhosted.org/packages/d0/9f/e555be1ce8660c0ea27d6fefcb56365f653d5c36f3d1404773d68568c7be/poodle-0.1.6.tar.gz" } ], "0.1.7": [ { "comment_text": "", "digests": { "md5": "13da39517f6cf25aea588d367ab3397b", "sha256": "d4c5276b88ce610324cc1a66087bac99370e830d81cefa120868e94d6775b8be" }, "downloads": -1, "filename": "poodle-0.1.7-py3-none-any.whl", "has_sig": false, "md5_digest": "13da39517f6cf25aea588d367ab3397b", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.7", "size": 47340, "upload_time": "2019-09-18T02:35:30", "url": "https://files.pythonhosted.org/packages/fc/db/aac2b0df1c122d3c8ce30c63cee20dd7ce66d572df94c29825c41dbeed85/poodle-0.1.7-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c30d1f0e4add60a6d01c582a9242ecee", "sha256": "6756b23d0e8d34849248dd26a3d6a3727f2b69ac0c48420d5f35d8061563a4e6" }, "downloads": -1, "filename": "poodle-0.1.7.tar.gz", "has_sig": false, "md5_digest": "c30d1f0e4add60a6d01c582a9242ecee", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.7", "size": 50344, "upload_time": "2019-09-18T02:35:33", "url": "https://files.pythonhosted.org/packages/77/a1/d0371451c5cc4dba515a9e47d7114867b9cc8e601f20876bca10edab39d8/poodle-0.1.7.tar.gz" } ], "0.1.8": [ { "comment_text": "", "digests": { "md5": "35f4a979c009a25ce345b08f4f902166", "sha256": "f304e5d68b81383adfda1afa3e59dc7e33b563155a767f343c88afe0ae9f900e" }, "downloads": -1, "filename": "poodle-0.1.8-py3-none-any.whl", "has_sig": false, "md5_digest": "35f4a979c009a25ce345b08f4f902166", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.7", "size": 47460, "upload_time": "2019-09-19T01:56:06", "url": "https://files.pythonhosted.org/packages/eb/2e/77433ab39c668742acb627a0d07eb146535c66daa03b25d256ca7044b187/poodle-0.1.8-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a1f79a1d7e825dbe1641f488bc631e78", "sha256": "207bec7be7ab53eef460887e49497682540682c5cef6e1d73fd6166346f37f5e" }, "downloads": -1, "filename": "poodle-0.1.8.tar.gz", "has_sig": false, "md5_digest": "a1f79a1d7e825dbe1641f488bc631e78", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.7", "size": 50456, "upload_time": "2019-09-19T01:56:08", "url": "https://files.pythonhosted.org/packages/3a/01/664e31522722c134a7483b18bc6375757319c87629b5996cca37d67c97c0/poodle-0.1.8.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "66a0ff2746d4628e5c5d0c37151a2786", "sha256": "eb1db2b4097868d7e4e740abfd4e07e02f02e1b5cabbb1e0c273635187258fd1" }, "downloads": -1, "filename": "poodle-0.1.10-py3-none-any.whl", "has_sig": false, "md5_digest": "66a0ff2746d4628e5c5d0c37151a2786", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.7", "size": 48417, "upload_time": "2019-10-10T19:03:14", "url": "https://files.pythonhosted.org/packages/39/40/8da15101109e68a5fbc6d0afc2b130df5c938ea401645ae31b2ee335287a/poodle-0.1.10-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "cc1930806fbf0c680b01e801449840d4", "sha256": "a85c892ccabb7d6fc7f7974b98f714c0409b61483f0377287c806a30919c6d87" }, "downloads": -1, "filename": "poodle-0.1.10.tar.gz", "has_sig": false, "md5_digest": "cc1930806fbf0c680b01e801449840d4", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.7", "size": 52376, "upload_time": "2019-10-10T19:03:16", "url": "https://files.pythonhosted.org/packages/7f/9c/9a151e359c7a2fb0ab0b704d25b25b81aaac61fb7ead6d1bdb436049d459/poodle-0.1.10.tar.gz" } ] }