{ "info": { "author": "Scott Maxwell", "author_email": "scott@codecobblers.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: MacOS :: MacOS X", "Operating System :: POSIX :: BSD :: FreeBSD", "Operating System :: POSIX :: Linux", "Programming Language :: Python", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Topic :: Software Development" ], "description": "Summary\n=======\n\n**papa** is a process kernel. It contains both a client library and a\nserver component for creating sockets and launching processes from a\nstable parent process.\n\nDependencies\n============\n\nPapa has no external dependencies, and it never will! It has been tested\nunder the following Python versions:\n\n- 2.6\n- 2.7\n- 3.2\n- 3.3\n- 3.4\n\nInstallation\n============\n\n::\n\n $> pip install papa\n\nPurpose\n=======\n\nSometimes you want to be able to start a process and have it survive on\nits own, but you still want to be able to capture the output. You could\ndaemonize it and pipe the output to files, but that is a pain and lacks\nflexibility when it comes to handling the output.\n\nProcess managers such as circus and supervisor are very good for\nstarting and stopping processes, and for ensuring that they are\nautomatically restarted when they die. However, if you need to restart\nthe process manager, all of their managed processes must be brought down\nas well. In this day of zero downtime, that is no longer okay.\n\nPapa is a process kernel. It has extremely limited functionality and it\nhas zero external dependencies. If I've done my job right, you should\nnever need to upgrade the papa package. There will probably be a few bug\nfixes before it is really \"done\", but the design goal was to create\nsomething that did NOT do everything, but only did the bare minimum\nrequired. The big process managers can add the remaining features.\n\nPapa has 3 types of things it manages:\n\n- Sockets\n- Values\n- Processes\n\nHere is what papa does:\n\n- Create sockets and close sockets\n- Set, get and clear named values\n- Start processes and capture their stdout/stderr\n- Allow you to retrieve the stdout/stderr of the processes started by\n papa\n- Pass socket file descriptors and port numbers to processes as they\n start\n\nHere is what it does NOT do:\n\n- Stop processes\n- Send signals to processes\n- Restart processes\n- Communicate with processes in any way other than to capture their\n output\n\nSockets\n=======\n\nBy managing sockets, papa can manage interprocess communication. Just\ncreate a socket in papa and then pass the file descriptor to your\nprocess to use it. See the `Circus\ndocs `__ for a\nvery good description of why this is so useful.\n\nPapa can create Unix, INET and INET6 sockets. By default it will create\nan INET TCP socket on an OS-assigned port.\n\nYou can pass either the file descriptor (fileno) or the port of a socket\nto a process by including a pattern like this in the process arguments:\n\n- ``$(socket.my_awesome_socket_name.fileno)``\n- ``$(socket.my_awesome_socket_name.port)``\n\nValues\n======\n\nPapa has a very simple name/value pair storage. This works much like\nenvironment variables. The values must be text, so if you want to store\na complex structure, you will need to encode and decode with something\nlike the `JSON module `__.\n\nThe primary purpose of this facility is to store state information for\nyour process that will survive between restarts. For instance, a process\nmanager can store the current state that all of its managed processes\nare supposed to be in. Then if the process manager is restarted, it can\nrestore its internal state, then go about checking to see if anything on\nthe machine has changed. Are all processes that should be running\nactually running?\n\nProcesses\n=========\n\nProcesses can be started with or without output management. You can\nspecify a maximum size for output to be cached. Each started process has\na management thread in the Papa kernel watching its state and capturing\noutput if necessary.\n\nA Note on Naming (Namespacing)\n==============================\n\nSockets, values and processes all have unique names. A name can only\nrepresent one item per class. So you could have an \"aack\" socket, an\n\"aack\" value and an \"aack\" process, but you cannot have two \"aack\"\nprocesses.\n\nAll of the monitoring commands support a final asterix as a wildcard. So\nyou can get a list of sockets whose names match \"uwsgi\\*\" and you would\nget any socket that starts with \"uwsgi\".\n\nOne good naming scheme is to prefix all names with the name of your own\napplication. So, for instance, the Circus process manager can prefix all\nnames with \"circus.\" and the Supervisor process manager can prefix all\nnames with \"supervisor.\". If you write your own simple process manager,\njust prefix it with \"tweeter.\" or \"facebooklet.\" or whatever your\nproject is called.\n\nIf you need to have multiple copies of something, put a number after a\ndot for each of those as well. For instance, if you are starting 3\nwaitress instances in circus, call them ``circus.waitress.1``,\n``circus.waitress.2``, and ``circus.waitress.3``. That way you can query\nfor all processes named ``circus.*`` to see all processes managed by\ncircus, or query for ``circus.waitress.*`` to see all waitress processes\nmanaged by circus.\n\nStarting the kernel\n===================\n\nThere are two ways to start the kernel. You can run it as a process, or\nyou can just try to access it from the client library and allow it to\nautostart. The client library uses a lock to ensure that multiple\nthreads do not start the server at the same time but there is currently\nno protection against multiple processes doing so.\n\nBy default, the papa kernel process will communicate over port 20202.\nYou can change this by specifying a different port number or a path. By\nspecifying a path, a Unix socket will be used instead.\n\nIf you are going to be creating papa client instances in many places in\nyour code, you may want to just call ``papa.set_default_port`` or\n``papa.set_default_path`` once when your application is starting and\nthen just instantiate the Papa object with no parameters.\n\nTelnet interface\n================\n\nPapa has been designed so that you can communicate with the process\nkernel entirely without code. Just start the Papa server, then do this:\n\n::\n\n telnet localhost 20202\n\nYou should get a welcome message and a prompt. Type \"help\" to get help.\nType \"help process\" to get help on the process command.\n\nThe most useful commands from a monitoring standpoint are:\n\n- list sockets\n- list processes\n- list values\n\nAll of these can by used with no arguments, or can be followed by a list\nof names, including wildcards. For instance, to see all of the values in\nthe circus and supervisor namespaces, do this:\n\n::\n\n list values circus.* supervisor.*\n\nYou can abbreviate every command as short as you like. So \"l p\" means\n\"list processes\" and \"h l p\" means \"help list processes\"\n\nCreating a Connection\n=====================\n\nYou can create either long-lived or short-lived connections to the Papa\nkernel. If you want to have a long-lived connection, just create a Papa\nobject to connect and close it when done, like this:\n\n::\n\n class MyObject(object):\n def __init__(self):\n self.papa = Papa()\n\n def start_stuff(self):\n self.papa.make_socket('uwsgi')\n self.papa.make_process('uwsgi', 'env/bin/uwsgi', args=('--ini', 'uwsgi.ini', '--socket', 'fd://$(socket.uwsgi.fileno)'), working_dir='/Users/aackbar/awesome', env=os.environ)\n self.papa.make_process('http_receiver', sys.executable, args=('http.py', '$(socket.uwsgi.port)'), working_dir='/Users/aackbar/awesome', env=os.environ)\n\n def close(self):\n self.papa.close()\n\nIf you want to just fire off a few commands and leave, it is better to\nuse the ``with`` mechanism like this:\n\n::\n\n from papa import Papa\n\n with Papa() as p:\n print(p.list_sockets())\n print(p.make_socket('uwsgi', port=8080))\n print(p.list_sockets())\n print(p.make_process('uwsgi', 'env/bin/uwsgi', args=('--ini', 'uwsgi.ini', '--socket', 'fd://$(socket.uwsgi.fileno)'), working_dir='/Users/aackbar/awesome', env=os.environ))\n print(p.make_process('http_receiver', sys.executable, args=('http.py', '$(socket.uwsgi.port)'), working_dir='/Users/aackbar/awesome', env=os.environ))\n print(p.list_processes())\n\nThis will make a new connection, do a bunch of work, then close the\nconnection.\n\nSocket Commands\n===============\n\nThere are 3 socket commands.\n\n``p.list_sockets(*args)``\n-------------------------\n\nThe ``sockets`` command takes a list of socket names to get info about.\nAll of these are valid:\n\n- ``p.list_sockets()``\n- ``p.list_sockets('circus.*')``\n- ``p.list_sockets('circus.uwsgi', 'circus.nginx.*', 'circus.logger')``\n\nA ``dict`` is returned with socket names as keys and socket details as\nvalues.\n\n``p.make_socket(name, host=None, port=None, family=None, socket_type=None, backlog=None, path=None, umask=None, interface=None, reuseport=None)``\n-------------------------------------------------------------------------------------------------------------------------------------------------\n\nAll parameters are optional except for the name. To create a standard\nTCP socket on port 8080, you can do this:\n\n::\n\n p.make_socket('circus.uwsgi', port=8080)\n\nTo make a Unix socket, do this:\n\n::\n\n p.make_socket('circus.uwsgi', path='/tmp/uwsgi.sock')\n\nA path for a Unix socket must be an absolute path or ``make_socket``\nwill raise a ``papa.Error`` exception.\n\nYou can also leave out the path and port to create a standard TCP socket\nwith an OS-assigned port. This is really handy when you do not care what\nport is used.\n\nIf you call ``make_socket`` with the name of a socket that already\nexists, papa will return the original socket if all parameters match, or\nraise a ``papa.Error`` exception if some parameters differ.\n\nSee the ``make_sockets`` method of the Papa object for other parameters.\n\n``p.remove_sockets(*args)``\n---------------------------\n\nThe ``remove_sockets`` command also takes a list of socket names. All of\nthese are valid:\n\n- ``p.remove_sockets('circus.*')``\n- ``p.remove_sockets('circus.uwsgi', 'circus.nginx.*', 'circus.logger')``\n\nRemoving a socket will prevent any future processes from using it, but\nany processes that were already started using the file descriptor of the\nsocket will continue to use the copy they inherited.\n\nValue Commands\n==============\n\nThere are 4 value commands.\n\n``p.list_values(*args)``\n------------------------\n\nThe ``list_values`` command takes a list of values to retrieve. All of\nthese are valid:\n\n- ``p.list_values()``\n- ``p.list_values('circus.*')``\n- ``p.list_list_values('circus.uwsgi', 'circus.nginx.*', 'circus.logger')``\n\nA ``dict`` will be returned with all matching names and values.\n\n``p.set(name, value=None)``\n---------------------------\n\nTo set a value, do this:\n\n::\n\n p.set('circus.uswgi', value)\n\nYou can clear a single value by setting it to ``None``.\n\n``p.get(name)``\n---------------\n\nTo retrieve a value, do this:\n\n::\n\n value = p.get('circus.uwsgi')\n\nIf no value is stored by that name, ``None`` will be returned.\n\n``p.remove_values(*args)``\n--------------------------\n\nTo remove a value or values, do something like this:\n\n- ``p.remove_values('circus.*')``\n- ``p.remove_values('circus.uwsgi', 'circus.nginx.*', 'circus.logger')``\n\nYou cannot remove all variables so passing no names or passing ``*``\nwill raise a ``papa.Error`` exception.\n\nProcess Commands\n================\n\nThere are 4 process commands:\n\n``p.list_processes(*args)``\n---------------------------\n\nThe ``list_processes`` command takes a list of process names to get info\nabout. All of these are valid:\n\n- ``p.list_processes()``\n- ``p.list_processes('circus.*')``\n- ``p.list_processes('circus.uwsgi', 'circus.nginx.*', 'circus.logger')``\n\nA ``dict`` is returned with process names as keys and process details as\nvalues.\n\n``p.make_process(name, executable, args=None, env=None, working_dir=None, uid=None, gid=None, rlimits=None, stdout=None, stderr=None, bufsize=None, watch_immediately=None)``\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n\nEvery process must have a unique ``name`` and an ``executable``. All\nother parameters are optional. The ``make_process`` method returns a\n``dict`` that contains the pid of the process.\n\nThe ``args`` parameter should be a tuple of command-line arguments. If\nyou have only one argument, papa conveniently supports passing that as a\nstring.\n\nYou will probably want to pass ``working_dir``. If you do not, the\nworking directory will be that of the papa kernel process.\n\nBy default, stdout and stderr are captured so that you can retrieve them\nwith the ``watch`` command. By default, the ``bufsize`` for the output\nis 1MB.\n\nValid values for ``stdout`` and ``stderr`` are ``papa.DEVNULL`` and\n``papa.PIPE`` (the default). You can also pass ``papa.STDOUT`` to\n``stderr`` to merge the streams.\n\nIf you pass ``bufsize=0``, not output will be recorded. Otherwise,\nbufsize can be the number of bytes, or a number followed by 'k', 'm' or\n'g'. If you want a 2 MB buffer, you can pass ``bufsize='2m'``, for\ninstance. If you do not retrieve the output quicky enough and the buffer\noverflows, older data is removed to make room.\n\nIf you specify ``uid``, it can be either the numeric id of the user or\nthe username string. Likewise, ``gid`` can be either the numeric group\nid or the group name string.\n\nIf you want to specify ``rlimits``, pass a ``dict`` with rlimit names\nand numeric values. Valid rlimit names can be found in the ``resources``\nmodule. Leave off the ``RLIMIT_`` prefix. On my system, valid names are\n``as``, ``core``, ``cpu``, ``data``, ``fsize``, ``memlock``, ``nofile``,\n``nproc``, ``rss``, and ``stack``.\n\n::\n\n rlimit={'cpu': 2, 'nofile': 1024}\n\nThe ``env`` parameter also takes a ``dict`` with names and values. A\nuseful trick is to do ``env=os.environ`` to copy your environment to the\nnew process.\n\nIf you want to run a Python application and you wish to use the same\nPython executable as your client application, a useful trick is to pass\n``sys.executable`` as the ``executable`` and the path to the Python\nscript as the first element of your ``args`` tuple. If you have no other\nargs, just pass the path as a string to ``args``.\n\n::\n\n p.make_process('write3', sys.executable, args='executables/write_three_lines.py', working_dir=here, uid=os.environ['LOGNAME'], env=os.environ)\n\nThe final argument that needs mention is ``watch_immediately``. If you\npass ``True`` for this, papa will make the process and return a\n``Watcher``. This is effectively the same as doing\n``p.make_process(name, ...)`` followed immediately by ``p.watch(name)``,\nbut it has one fewer round-trip communication with the kernel. If all\nyou want to do is launch an application and monitor its output, this is\na good way to go.\n\n``p.remove_processes(*args)``\n-----------------------------\n\nIf you do not care about retrieving the output or the exit code for a\nprocess, you can use ``remove_processes`` to tell the papa kernel to\nclose the output buffers and automatically remove the process from the\nprocess list when it exits.\n\n- ``p.remove_processes('circus.logger')``\n- ``p.remove_processes('circus.uwsgi', 'circus.nginx.*', 'circus.logger')``\n\n``p.watch_processes(*args)``\n----------------------------\n\nThe ``watch_processes`` command returns a ``Watcher`` object for the\nspecified process or processes. That object uses a separate socket to\nretrieve the output of the processes it is watching.\n\n*Optimization Note:* Actually, it hijacks the socket of your ``Papa``\nobject. If you issue any other commands to the ``Papa`` object that\nrequire a connection to the kernel, the ``Papa`` object will silently\ncreate a new socket and connect up for the additional commands. If you\nclose the ``Watcher`` and the ``Papa`` object has not already created a\nnew connection, the socket will be returned to the ``Papa`` object. So\nif you launch an application, use ``watch`` to grab all of its output\nuntil it closes, then use the ``set`` command to update your saved\nstatus, all of that can occur with a single connection.\n\nThe Watcher object\n==================\n\nWhen you use ``watch_processes`` or when you do ``make_process`` with\n``watch_immediately=True``, you get back a ``Watcher`` object.\n\nYou can use watchers manually or with a context manager. Here is an\nexample without a context manager:\n\n::\n\n class MyLogger(object):\n def __init__(self, watcher):\n self.watcher = watcher\n\n def save_stuff(self):\n if self.watcher and self.watcher.ready:\n out, err, closed = self.watcher.read()\n ... save it ...\n self.watcher.acknowledge() # remove it from the buffer\n\n def close(self):\n self.watcher.close()\n\nIf you are running your logger in a separate thread anyway, you might\nwant to just use a context manager, like this:\n\n::\n\n with p.watch_processes('aack') as watcher:\n while watcher:\n out, err, closed = watcher.read() # block until something arrives\n ... save it ...\n watcher.acknowledge() # remove it from the buffer\n\nThe ``Watcher`` object has a ``fileno`` method, so it can be used with\n``select.select``, like this:\n\n::\n\n watchers = []\n\n watchers.append(p.watch_processes('circus.uwsgi'))\n watchers.append(p.watch_processes('circus.nginx'))\n watchers.append(p.watch_processes('circus.mongos.*'))\n\n while watchers:\n ready_watchers = select.select(watchers, [], [])[0] # wait for one of these\n for watcher in ready_watchers: # iterate through all that are ready\n out, err, closed = watcher.read()\n ... save it ...\n watcher.acknowledge()\n if not watcher: # if it is done, remove this watcher from the list\n watcher.close()\n del watchers[watcher]\n\nOf course, in the above example it would have been even more efficient\nto just use a single watcher, like this:\n\n::\n\n with p.watch_processes('circus.uwsgi', 'circus.nginx', 'circus.mongos.*') as watcher:\n while watcher:\n out, err, closed = watcher.read()\n ... save it ...\n # watcher.acknowledge() - no need since watcher.read will do it for us\n\n``w.ready``\n-----------\n\nThis property is ``True`` if the ``Watcher`` has data available to read\non the socket.\n\n``w.read()``\n------------\n\nRead will grab all waiting output from the ``Watcher`` and return a\n``tuple`` of ``(out, err, closed)``. Each of these is an array of\n``papa.ProcessOutput`` objects. An output object is actually a\n``namedtuple`` with 3 values: ``name``, ``timestamp``, and ``data``.\n\nThe ``name`` element is the ``name`` of the process. The ``timestamp``\nis a ``float`` of when the data was captured by the papa kernel. The\n``data`` is a binary string if found in either the ``out`` or ``err``\narray. It is the exit code if found in the ``closed`` array. Using all\nof these elements, you can write proper timestamps into your logs, even\nif data was captured by the papa kernel minutes, hours or days earlier.\n\nThe ``read`` method will block if no data is ready to read. If you do\nnot want to block, use either the ``ready`` property or a mechanism such\nas ``select.select`` before calling ``read``.\n\n``w.acknowledge()``\n-------------------\n\nJust because your have read output from a process, the papa kernel\ncannot know that you successfully logged it. Maybe you crashed or were\nshutdown before you had the chance. So the papa kernel will hold onto\nthe data until you acknowledge receipt. This can be done either by\ncalling ``acknowledge``, or by doing a subsequent ``read`` or a\n``close``.\n\n``w.close()``\n-------------\n\nWhen you are done with a ``Watcher``, be sure to close it. That will\nrelease the socket and potentially even return the socket back to the\noriginal ``Papa`` object. It will also send off a final ``acknowledge``\nif necessary.\n\nIf you use a context manager, the ``close`` happens automatically.\n\n``if watcher:``\n---------------\n\nA boolean check on the ``Watcher`` object will return ``True`` if it is\nstill active and ``False`` if it has received and acknowledged a close\nmessage from all processes it is monitoring.\n\nWARNING: There should be only one\n---------------------------------\n\nYou will get very screwy results if you have multiple watchers for the\nsame process. Each will get the available data, then acknowledge receipt\nat some point, removing that data from the queue. Based on timing, both\nwill get overlapping results, but neither is likely to get everything.\n\nShutting Down\n=============\n\nPapa is meant to be a long-lived process and it is meant to be usable by\nmultiple client apps. If you would like to shut Papa down, you can try\n``p.exit_if_idle()``. This call will only exit Papa if there are no\nprocesses, sockets or values. So if your app cleaned everything up and\nno other app is using Papa, ``exit_if_idle`` will allow Papa to die. It\nwill return ``True`` if Papa has indicated that it will exit when the\nconnection closes.\n\nIf you want to do a complete cleanup, kill all of your processes however\nyou like, then do:\n\n::\n\n p.remove_processes('myapp.*')\n p.remove_sockets('myapp.*')\n p.remove_values('myapp.*')\n if p.exit_if_idle():\n print('Papa says it will shutdown!')\n\nWARNING: If another process connects to Papa before the connection is\nclosed, Papa will remain open. The ``exit_if_idle`` command will drop\nthe connection if it returns True so this is a very narrow window of\nopportunity for failure.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/scottkmaxwell/papa", "keywords": null, "license": "MIT", "maintainer": null, "maintainer_email": null, "name": "papa", "package_url": "https://pypi.org/project/papa/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/papa/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/scottkmaxwell/papa" }, "release_url": "https://pypi.org/project/papa/1.0.6/", "requires_dist": null, "requires_python": null, "summary": "Simple socket and process kernel", "version": "1.0.6" }, "last_serial": 1914188, "releases": { "0.9.0": [ { "comment_text": "", "digests": { "md5": "4b210ab35dae958af5cc5b44b01a2066", "sha256": "e8e144d8ff4f0721009c3635c77ff75fa6d4180546da1923b04285311aa7fbab" }, "downloads": -1, "filename": "papa-0.9.0.tar.gz", "has_sig": false, "md5_digest": "4b210ab35dae958af5cc5b44b01a2066", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21962, "upload_time": "2014-09-04T23:21:02", "url": "https://files.pythonhosted.org/packages/4e/0a/8e2707d5bbdd8a9540b32e47af6e006b120f44ff7f4134eba80bf7ddd886/papa-0.9.0.tar.gz" } ], "0.9.1": [ { "comment_text": "", "digests": { "md5": "6268c6489df1ed3dc7aab30334b0c4e6", "sha256": "d34442d245e7b51d57c1f533d4d567bddd17d80371fdf153afed833df7942b16" }, "downloads": -1, "filename": "papa-0.9.1.tar.gz", "has_sig": false, "md5_digest": "6268c6489df1ed3dc7aab30334b0c4e6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22328, "upload_time": "2014-09-05T01:15:00", "url": "https://files.pythonhosted.org/packages/8b/23/98d5d34d5a420e713772825491f4431104ef1cf5f86f245fdde2e62a5469/papa-0.9.1.tar.gz" } ], "0.9.2": [ { "comment_text": "", "digests": { "md5": "12362d6d645dc6a0def28e2533b8a54b", "sha256": "e0617df8ec72d598887a3a854ea8bf413558663531cdc700b44d070398e04c8c" }, "downloads": -1, "filename": "papa-0.9.2.tar.gz", "has_sig": false, "md5_digest": "12362d6d645dc6a0def28e2533b8a54b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 24262, "upload_time": "2014-09-05T01:21:28", "url": "https://files.pythonhosted.org/packages/cb/21/1ad0b9166dbbc0fd178c968035da7d1005d4a6ab57ed8e5bec3a8453bf6e/papa-0.9.2.tar.gz" } ], "0.9.3": [ { "comment_text": "", "digests": { "md5": "d64567acd32304f2e7ee229fc6005a48", "sha256": "209e4d67d7f5832c9c11c48fa50a2fb99188ddac5db338cb14ac1f0993ba3b55" }, "downloads": -1, "filename": "papa-0.9.3.tar.gz", "has_sig": false, "md5_digest": "d64567acd32304f2e7ee229fc6005a48", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 25362, "upload_time": "2014-09-05T01:38:49", "url": "https://files.pythonhosted.org/packages/66/80/5bafb908355e78b354bbe9c15d77c88b49eca92417fc7e6fdcc125f71e04/papa-0.9.3.tar.gz" } ], "1.0.0": [ { "comment_text": "", "digests": { "md5": "bd70db5a344e3136df31b9837fc61522", "sha256": "737a77209bb74c04fd87ca9205cf5a7eda004b4e4871f384c6107702d963850f" }, "downloads": -1, "filename": "papa-1.0.0.tar.gz", "has_sig": false, "md5_digest": "bd70db5a344e3136df31b9837fc61522", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30932, "upload_time": "2014-11-07T06:28:37", "url": "https://files.pythonhosted.org/packages/ed/04/2c7d9bac1087948e22867990e7aca960d2fe6db234b766600f95b745aec2/papa-1.0.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "721d3e2e210e67a520c78bc246f41e72", "sha256": "d3dfb8b10aa65d40920033ff65257e6add3522c79088753a42894ee5ea333ef1" }, "downloads": -1, "filename": "papa-1.0.1.tar.gz", "has_sig": false, "md5_digest": "721d3e2e210e67a520c78bc246f41e72", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31033, "upload_time": "2014-11-07T07:25:58", "url": "https://files.pythonhosted.org/packages/18/9d/b5c524de66e627b9fe2ae5261ab07341f5f63428152d21b5dadf6b60a629/papa-1.0.1.tar.gz" } ], "1.0.2": [ { "comment_text": "", "digests": { "md5": "954dfb0781ada59291e5adb0940e64b7", "sha256": "75c507f574a077b5792492a6f425091c390f791ac111b686c6fa9ec6547dac70" }, "downloads": -1, "filename": "papa-1.0.2.tar.gz", "has_sig": false, "md5_digest": "954dfb0781ada59291e5adb0940e64b7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31036, "upload_time": "2014-11-08T06:29:39", "url": "https://files.pythonhosted.org/packages/13/72/442451b7e4fb6ee97d0953254ea86d35dc767a852685027f53ce9a9109b3/papa-1.0.2.tar.gz" } ], "1.0.3": [ { "comment_text": "", "digests": { "md5": "c2853a585dc0ce21e143794b5cf4f6a6", "sha256": "541711aa68753185a32ef899650808ab02671ddb1ed9f3cbb6c25e6852c4f855" }, "downloads": -1, "filename": "papa-1.0.3.tar.gz", "has_sig": false, "md5_digest": "c2853a585dc0ce21e143794b5cf4f6a6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31121, "upload_time": "2014-11-18T06:14:01", "url": "https://files.pythonhosted.org/packages/cd/f1/032e02bb2b44a4196f4505e550deddc72393fc7a8f9ff73420df29ba21fa/papa-1.0.3.tar.gz" } ], "1.0.4": [ { "comment_text": "", "digests": { "md5": "0e55ddc1e4acd3463ec6bb291a770464", "sha256": "c434bbd5df59bac5ac1b471b213f5efb510bc73f30166d124ab3c3683cb9b082" }, "downloads": -1, "filename": "papa-1.0.4.tar.gz", "has_sig": false, "md5_digest": "0e55ddc1e4acd3463ec6bb291a770464", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31119, "upload_time": "2014-11-18T22:38:45", "url": "https://files.pythonhosted.org/packages/8b/a4/410475e6176c4c0e03235fbb759258020f826bd59c5a141826ed7c795eb7/papa-1.0.4.tar.gz" } ], "1.0.5": [ { "comment_text": "", "digests": { "md5": "6ec425f426dde50a461a7fad7322fe62", "sha256": "a4a723fd61d5a49a8653ba1a1727941c4806567b8b0f999cea915d09fbeb0823" }, "downloads": -1, "filename": "papa-1.0.5.tar.gz", "has_sig": false, "md5_digest": "6ec425f426dde50a461a7fad7322fe62", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31208, "upload_time": "2014-11-19T04:01:00", "url": "https://files.pythonhosted.org/packages/b0/fe/5665073b564447deee4b8491ff81a297fec08197242c82feecdf29988c02/papa-1.0.5.tar.gz" } ], "1.0.6": [ { "comment_text": "", "digests": { "md5": "d35869a19802d80ebe0df8022d0b2500", "sha256": "20558791c6a69fd619b4696cc7476466de1d5c5e570ed2c536695cb0e64a542f" }, "downloads": -1, "filename": "papa-1.0.6.tar.gz", "has_sig": false, "md5_digest": "d35869a19802d80ebe0df8022d0b2500", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31240, "upload_time": "2016-01-20T22:53:21", "url": "https://files.pythonhosted.org/packages/a3/ca/7103fb7a57457107d403c016e6d4be364bb76daf3be771ccd240ade6d9f3/papa-1.0.6.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "d35869a19802d80ebe0df8022d0b2500", "sha256": "20558791c6a69fd619b4696cc7476466de1d5c5e570ed2c536695cb0e64a542f" }, "downloads": -1, "filename": "papa-1.0.6.tar.gz", "has_sig": false, "md5_digest": "d35869a19802d80ebe0df8022d0b2500", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31240, "upload_time": "2016-01-20T22:53:21", "url": "https://files.pythonhosted.org/packages/a3/ca/7103fb7a57457107d403c016e6d4be364bb76daf3be771ccd240ade6d9f3/papa-1.0.6.tar.gz" } ] }