{ "info": { "author": "", "author_email": "", "bugtrack_url": null, "classifiers": [], "description": "Single-beat\n---------\nSingle-beat is a nice little application that ensures only one instance of your process runs across your servers.\n\nSuch as celerybeat (or some kind of daily mail sender, orphan file cleaner etc...) needs to be running only on one server,\nbut if that server gets down, well, you go and start it at another server etc. \n\nAs we all hate manually doing things, single-beat automates this process.\n\n\nHow\n---------\n\nWe use redis as a lock server, and wrap your process with single-beat, in two servers,\n\n```bash\nsingle-beat celery beat\n```\n\non the second server\n\n```bash\nsingle-beat celery beat\n```\n\non the third server\n\n```bash\nsingle-beat celery beat\n```\n\nThe second process will just wait until the first one dies etc.\n\nInstallation\n------------\n\n```bash\nsudo pip install single-beat\n```\n\nConfiguration\n-------------\n\nYou can configure single-beat with environment variables, like\n\n```bash\nSINGLE_BEAT_REDIS_SERVER='redis://redis-host:6379/1' single-beat celery beat\n```\n\n- SINGLE_BEAT_REDIS_SERVER\n\n you can give redis host url, we pass it to from_url of [redis-py](http://redis-py.readthedocs.org/en/latest/#redis.StrictRedis.from_url)\n\n- SINGLE_BEAT_REDIS_SENTINEL\n\n use redis sentinel to select the redis host to use, sentinels are defined as colon-separated list of hostname and port pairs, e.g. `192.168.1.10:26379;192.168.1.11:26379;192.168.1.12:26379`\n\n- SINGLE_BEAT_REDIS_SENTINEL_MASTER (default `mymaster`)\n- SINGLE_BEAT_REDIS_SENTINEL_DB (default 0)\n- SINGLE_BEAT_IDENTIFIER\n\n the default is we use your process name as the identifier, like\n\n ```bash\n single-beat celery beat\n ```\n\n all processes checks a key named, SINGLE_BEAT_celery but in some cases you might need to give another identifier, eg. your project name etc.\n\n ```bash\n SINGLE_BEAT_IDENTIFIER='celery-beat' single-beat celery beat\n ```\n\n- SINGLE_BEAT_LOCK_TIME (default 5 seconds)\n- SINGLE_BEAT_INITIAL_LOCK_TIME (default 2 * SINGLE_BEAT_LOCK_TIME seconds)\n- SINGLE_BEAT_HEARTBEAT_INTERVAL (default 1 second)\n\n when starting your process, we set a key with 10 second expiration (INITIAL_LOCK_TIME) in redis server, \n other single-beat processes checks if that key exists - if it exists they won't spawn children. \n\n We continue to update that key every 1 second (HEARTBEAT_INTERVAL) setting it with a ttl of 5 seconds (LOCK_TIME)\n\n This should work, but you might want to give more relaxed intervals, like:\n\n ```bash\n SINGLE_BEAT_LOCK_TIME=300 SINGLE_BEAT_HEARTBEAT_INTERVAL=60 single-beat celery beat\n ```\n\n- SINGLE_BEAT_HOST_IDENTIFIER (default socket.gethostname)\n\n we set the name of the host and the process id as lock keys value, so you can check where your process lives.\n\n ```bash\n SINGLE_BEAT_IDENTIFIER='celery-beat' single-beat celery beat\n ```\n\n ```bash\n (env)$ redis-cli\n redis 127.0.0.1:6379> keys *\n 1) \"_kombu.binding.celeryev\"\n 2) \"celery\"\n 3) \"_kombu.binding.celery\"\n 4) \"SINGLE_BEAT_celery-beat\"\n redis 127.0.0.1:6379> get SINGLE_BEAT_celery-beat\n \"0:aybarss-MacBook-Air.local:43213\"\n redis 127.0.0.1:6379>\n ```\n\n ```bash\n SINGLE_BEAT_HOST_IDENTIFIER='192.168.1.1' SINGLE_BEAT_IDENTIFIER='celery-beat' single-beat celery beat\n ```\n\n ```bash\n (env)$ redis-cli\n redis 127.0.0.1:6379> keys *\n 1) \"SINGLE_BEAT_celery-beat\"\n redis 127.0.0.1:6379> get SINGLE_BEAT_celery-beat\n \"0:192.168.1.1:43597\"\n ```\n\n- SINGLE_BEAT_LOG_LEVEL (default warn)\n\n change log level to debug if you want to see the heartbeat messages.\n\n- SINGLE_BEAT_WAIT_MODE (default heartbeat)\n- SINGLE_BEAT_WAIT_BEFORE_DIE (default 60 seconds)\n\n singlebeat has two different modes:\n - heartbeat (default)\n - supervised\n\n In heartbeat mode, single-beat is responsible for everything, spawning a process checking its status, publishing etc.\n In supervised mode, single-beat starts, checks if the child is running somewhere and waits for a while and then exits. So supervisord - or another scheduler picks up and restarts single-beat.\n\n on first server\n\n ```bash\n SINGLE_BEAT_LOG_LEVEL=debug SINGLE_BEAT_WAIT_MODE=supervised SINGLE_BEAT_WAIT_BEFORE_DIE=10 SINGLE_BEAT_IDENTIFIER='celery-beat' single-beat celery beat -A example.tasks\n DEBUG:singlebeat.beat:timer called 0.100841999054 state=WAITING\n [2014-05-05 16:28:24,099: INFO/MainProcess] beat: Starting...\n DEBUG:singlebeat.beat:timer called 0.999553918839 state=RUNNING\n DEBUG:singlebeat.beat:timer called 1.00173187256 state=RUNNING\n DEBUG:singlebeat.beat:timer called 1.00134801865 state=RUNNING\n ```\n\n this will heartbeat every second, on your second server\n\n ```bash\n SINGLE_BEAT_LOG_LEVEL=debug SINGLE_BEAT_WAIT_MODE=supervised SINGLE_BEAT_WAIT_BEFORE_DIE=10 SINGLE_BEAT_IDENTIFIER='celery-beat' single-beat celery beat -A example.tasks\n DEBUG:singlebeat.beat:timer called 0.101243019104 state=WAITING\n DEBUG:root:already running, will exit after 60 seconds\n ```\n\n so if you do this in your supervisor.conf\n\n ```bash\n [program:celerybeat]\n environment=SINGLE_BEAT_IDENTIFIER=\"celery-beat\",SINGLE_BEAT_REDIS_SERVER=\"redis://localhost:6379/0\",SINGLE_BEAT_WAIT_MODE=\"supervised\", SINGLE_BEAT_WAIT_BEFORE_DIE=10\n command=single-beat celery beat -A example.tasks\n numprocs=1\n stdout_logfile=./logs/celerybeat.log\n stderr_logfile=./logs/celerybeat.err\n autostart=true\n autorestart=true\n startsecs=10\n ```\n\n it will try to spawn celerybeat every 60 seconds.\n\nCli\n-------------\nSingle-beat also has a simple cli, that gives info about where your process is living - also can pause single-beat, restart your process etc.\n\n\"info\" will show where the process is running, the first node identifier is the ip address connecting to redis - by default.\n\n```\n(venv3) $ SINGLE_BEAT_IDENTIFIER=echo SINGLE_BEAT_LOG_LEVEL=critical SINGLE_BEAT_REDIS_SERVER=127.0.0.1 single-beat-cli info\n127.0.0.1:95779 | WAITING |\n127.0.0.1:95776 | RUNNING | pid: 95778\n127.0.0.1:95784 | WAITING |\n```\n\n\n\"stop\", will stop your child process, so any node will pick it up again\n\n```\n(venv3) $ SINGLE_BEAT_IDENTIFIER=echo SINGLE_BEAT_LOG_LEVEL=critical SINGLE_BEAT_REDIS_SERVER=127.0.0.1 single-beat-cli stop\n127.0.0.1:95776 | PAUSED | killed\n127.0.0.1:95779 | WAITING |\n127.0.0.1:95784 | WAITING |\n\n(venv3) $ SINGLE_BEAT_IDENTIFIER=echo SINGLE_BEAT_LOG_LEVEL=critical SINGLE_BEAT_REDIS_SERVER=127.0.0.1 single-beat-cli info\n127.0.0.1:95776 | WAITING |\n127.0.0.1:95779 | WAITING |\n127.0.0.1:95784 | RUNNING | pid: 95877\n```\n\n\"restart\" will restart the child process in the active node.\n\n```\n(venv3) $ SINGLE_BEAT_IDENTIFIER=echo SINGLE_BEAT_LOG_LEVEL=critical SINGLE_BEAT_REDIS_SERVER=127.0.0.1 single-beat-cli info\n127.0.0.1:95776 | WAITING |\n127.0.0.1:95779 | WAITING |\n127.0.0.1:95784 | RUNNING | pid: 95877\n\n(venv3) $ SINGLE_BEAT_IDENTIFIER=echo SINGLE_BEAT_LOG_LEVEL=critical SINGLE_BEAT_REDIS_SERVER=127.0.0.1 single-beat-cli restart\n127.0.0.1:95776 | WAITING |\n127.0.0.1:95779 | WAITING |\n127.0.0.1:95784 | RESTARTING | killed\n\n(venv3) $ SINGLE_BEAT_IDENTIFIER=echo SINGLE_BEAT_LOG_LEVEL=critical SINGLE_BEAT_REDIS_SERVER=127.0.0.1 single-beat-cli info\n127.0.0.1:95776 | WAITING |\n127.0.0.1:95779 | WAITING |\n127.0.0.1:95784 | RUNNING | pid: 95905\n```\n\n\n\"pause\" will kill the child, and put all single-beat nodes in pause state. This is useful for when deploying, to ensure that no \"old version of the code\"\nis running while the deploy process is in place. after the deploy you can \"resume\" so any node will pick the child.\n\n```\n(venv3) $ SINGLE_BEAT_IDENTIFIER=echo SINGLE_BEAT_LOG_LEVEL=critical SINGLE_BEAT_REDIS_SERVER=127.0.0.1 single-beat-cli info\n127.0.0.1:95776 | WAITING |\n127.0.0.1:95779 | WAITING |\n127.0.0.1:95784 | RUNNING | pid: 95905\n\n(venv3) $ SINGLE_BEAT_IDENTIFIER=echo SINGLE_BEAT_LOG_LEVEL=critical SINGLE_BEAT_REDIS_SERVER=127.0.0.1 single-beat-cli pause\n127.0.0.1:95776 | PAUSED |\n127.0.0.1:95779 | PAUSED |\n127.0.0.1:95784 | PAUSED | killed\n\n(venv3) $ SINGLE_BEAT_IDENTIFIER=echo SINGLE_BEAT_LOG_LEVEL=critical SINGLE_BEAT_REDIS_SERVER=127.0.0.1 single-beat-cli info\n127.0.0.1:95776 | PAUSED |\n127.0.0.1:95779 | PAUSED |\n127.0.0.1:95784 | PAUSED |\n```\n\n\"resume\" will put single-beat nodes in waiting state - so any node will pick up the child\n\n```\n(venv3) $ SINGLE_BEAT_IDENTIFIER=echo SINGLE_BEAT_LOG_LEVEL=critical SINGLE_BEAT_REDIS_SERVER=127.0.0.1 single-beat-cli info\n127.0.0.1:95776 | PAUSED |\n127.0.0.1:95779 | PAUSED |\n127.0.0.1:95784 | PAUSED |\n\n(venv3) $ SINGLE_BEAT_IDENTIFIER=echo SINGLE_BEAT_LOG_LEVEL=critical SINGLE_BEAT_REDIS_SERVER=127.0.0.1 single-beat-cli resume\n127.0.0.1:95776 | WAITING |\n127.0.0.1:95784 | WAITING |\n127.0.0.1:95779 | WAITING |\n\n(venv3) $ SINGLE_BEAT_IDENTIFIER=echo SINGLE_BEAT_LOG_LEVEL=critical SINGLE_BEAT_REDIS_SERVER=127.0.0.1 single-beat-cli info\n127.0.0.1:95776 | WAITING |\n127.0.0.1:95784 | WAITING |\n127.0.0.1:95779 | RUNNING | pid: 96025\n```\n\n\n\"quit\" will terminate all child processes and then the parent process itself. So there will be no live single-beat nodes. Its useful to\nhave some sort of hand-brake - also might be useful when you have blue/green, or canary style deployments.\n\n```\n(venv3) $ SINGLE_BEAT_IDENTIFIER=echo SINGLE_BEAT_LOG_LEVEL=critical SINGLE_BEAT_REDIS_SERVER=127.0.0.1 single-beat-cli quit\n127.0.0.1:95784 | RUNNING |\n\n(venv3) $\n(venv3) $ SINGLE_BEAT_IDENTIFIER=echo SINGLE_BEAT_LOG_LEVEL=critical SINGLE_BEAT_REDIS_SERVER=127.0.0.1 single-beat-cli info\n```\n\n\nUsage Patterns\n--------------\n\nYou can see an example usage with supervisor at example/celerybeat.conf\n\nWhy\n--------\n\nThere are some other solutions but either they are either complicated, or you need to modify the process. And I couldn't find a simpler solution for this https://github.com/celery/celery/issues/251 without modifying or adding locks to my tasks.\n\nYou can also check uWsgi's [Legion Support](http://uwsgi-docs.readthedocs.org/en/latest/AttachingDaemons.html#legion-support) which can do the same thing.\n\nCredits\n----------\n * [ybrs](https://github.com/ybrs)\n * [edmund-wagner](https://github.com/edmund-wagner)\n * [lowks](https://github.com/lowks)\n * [rangermeier](https://github.com/rangermeier)\n * [joekohlsdorf](https://github.com/joekohlsdorf)\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/ybrs/single-beat", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "single-beat", "package_url": "https://pypi.org/project/single-beat/", "platform": "", "project_url": "https://pypi.org/project/single-beat/", "project_urls": { "Homepage": "https://github.com/ybrs/single-beat" }, "release_url": "https://pypi.org/project/single-beat/0.4.1/", "requires_dist": [ "tornado (<6.0,>=4.2.1)", "redis (>=2.9.1)", "tornadis (>=0.8.1)", "Click (>=7.0)" ], "requires_python": "", "summary": "ensures only one instance of your process across your servers", "version": "0.4.1" }, "last_serial": 4849717, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "39812c7e956b782c05fa1d7bf074ee25", "sha256": "085fedaba2fba2c43f5792dfddabc678b51e91e7dc095b6afd424da40037eae0" }, "downloads": -1, "filename": "single-beat-0.1.tar.gz", "has_sig": false, "md5_digest": "39812c7e956b782c05fa1d7bf074ee25", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2004, "upload_time": "2014-05-05T11:16:22", "url": "https://files.pythonhosted.org/packages/36/51/eebf9658b210011b5ff44804dc12667daba2f2cf1f96043f27f1117155bd/single-beat-0.1.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "ed7316d8dd673dd59f7112b6460943bb", "sha256": "16a5b5eadd0510143b690d8512d7c52459abb74dd70ace8b457ce7945f9e2861" }, "downloads": -1, "filename": "single-beat-0.1.1.tar.gz", "has_sig": false, "md5_digest": "ed7316d8dd673dd59f7112b6460943bb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2051, "upload_time": "2014-05-05T11:40:28", "url": "https://files.pythonhosted.org/packages/51/08/162d3846c26261122a1ecd11cd32bd872393903bee7587c6a8e51abdf105/single-beat-0.1.1.tar.gz" } ], "0.1.3": [ { "comment_text": "", "digests": { "md5": "6bf00b0dac7dca9d2d17bdeb8b5b951f", "sha256": "845537d026428b8eddc75fe05c53de5d8867b785f54a5cff884b8ee02f02e14e" }, "downloads": -1, "filename": "single-beat-0.1.3.tar.gz", "has_sig": false, "md5_digest": "6bf00b0dac7dca9d2d17bdeb8b5b951f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2232, "upload_time": "2014-05-16T16:29:37", "url": "https://files.pythonhosted.org/packages/76/48/9ea993aa8a5bef7ec2fbf48ffb02b01f8ca14b930563bdfd02c34ab938c0/single-beat-0.1.3.tar.gz" } ], "0.1.4": [ { "comment_text": "", "digests": { "md5": "d77c792b096635e47dab669247832401", "sha256": "a98506b09249d16f28b4fcf17dd17a010e755862734a5b75d74ef77f4b52ecf0" }, "downloads": -1, "filename": "single-beat-0.1.4.tar.gz", "has_sig": false, "md5_digest": "d77c792b096635e47dab669247832401", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2354, "upload_time": "2014-12-09T11:15:44", "url": "https://files.pythonhosted.org/packages/b4/1c/6934e404402b5d8bc49e4dbee11b0d490c52be5e6deca35e9e1f3b0627d9/single-beat-0.1.4.tar.gz" } ], "0.1.5": [ { "comment_text": "", "digests": { "md5": "eda2667d7239b384ad3124c05aa2a926", "sha256": "2ba6bfa10fadb6331e6b1f0c8f2a1c65b0ed9bce725ef0f7987527953050a9d8" }, "downloads": -1, "filename": "single-beat-0.1.5.tar.gz", "has_sig": false, "md5_digest": "eda2667d7239b384ad3124c05aa2a926", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2365, "upload_time": "2014-12-09T14:09:06", "url": "https://files.pythonhosted.org/packages/90/68/5dc481df173d3cff421b11c04215049ad37e413ba0a030148338403b9a21/single-beat-0.1.5.tar.gz" } ], "0.1.6": [ { "comment_text": "", "digests": { "md5": "02334f3380be0b97da30a2d001cc5549", "sha256": "bc362c6137b1b3bf63c5ba1ea70f15ec4d7de5c80cbc948cbc2e968485e0364d" }, "downloads": -1, "filename": "single-beat-0.1.6.tar.gz", "has_sig": false, "md5_digest": "02334f3380be0b97da30a2d001cc5549", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2414, "upload_time": "2015-09-14T15:38:56", "url": "https://files.pythonhosted.org/packages/cf/d8/9293c501ad2b35dce8604908cacdd71d4a73c86555ed86952244b28434d9/single-beat-0.1.6.tar.gz" } ], "0.1.7": [ { "comment_text": "", "digests": { "md5": "29ad6aba1c136c516f13b63ba6f277cc", "sha256": "c2e4835f63ab2d4d1af57512ae2199a091a31d0b9259324f73ba149d8cba9c5b" }, "downloads": -1, "filename": "single-beat-0.1.7.tar.gz", "has_sig": false, "md5_digest": "29ad6aba1c136c516f13b63ba6f277cc", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2416, "upload_time": "2016-02-25T13:24:29", "url": "https://files.pythonhosted.org/packages/96/1f/3c576c7b2c58901f54356367674bf74ddb7aed7c28f4278b09e9e7113de1/single-beat-0.1.7.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "bd24dfc21daa45cb387e28688ecf0a6f", "sha256": "ce9a34b54c0d2ffb61e6ae1c55440a65603da563a4987156c74051b4be0ff2c0" }, "downloads": -1, "filename": "single-beat-0.2.0.tar.gz", "has_sig": false, "md5_digest": "bd24dfc21daa45cb387e28688ecf0a6f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 3331, "upload_time": "2017-05-22T08:23:35", "url": "https://files.pythonhosted.org/packages/d8/f3/03f81fb16cd1be6112eab2dd4e480574b972345e5f46e4810cd9a0fb3656/single-beat-0.2.0.tar.gz" } ], "0.2.1": [ { "comment_text": "", "digests": { "md5": "28afa74cbfecc710f0a831908a14692c", "sha256": "939d85ced7881c32ea737a5fb39b4fe6264894cbc72d05e15e66ca78cb81c268" }, "downloads": -1, "filename": "single-beat-0.2.1.tar.gz", "has_sig": false, "md5_digest": "28afa74cbfecc710f0a831908a14692c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 3561, "upload_time": "2017-08-07T14:14:13", "url": "https://files.pythonhosted.org/packages/fb/da/3ef5c0d7ae050450f9efd0d399f1dd69068d390cc45ff4d4c33b014f534d/single-beat-0.2.1.tar.gz" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "f5cbc9531c26675eb35fd8b3ad90e7d0", "sha256": "83cd6ef66cc82583aeeabaa00870c5e9cf250d358ef2013fdbba8215fbab4f84" }, "downloads": -1, "filename": "single_beat-0.3.0-py2-none-any.whl", "has_sig": false, "md5_digest": "f5cbc9531c26675eb35fd8b3ad90e7d0", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 9359, "upload_time": "2018-10-08T10:36:30", "url": "https://files.pythonhosted.org/packages/c5/6f/5daec98cbef2e144c1bbbd24d60f7da0f09ebaa3b5605468fa50ca0163a4/single_beat-0.3.0-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "4fdf221b77d26e02d95da57095f21c8b", "sha256": "8694cdf9aabbb25d8962c13e387857c1961595edd6946cfb92e3c7b79faef636" }, "downloads": -1, "filename": "single_beat-0.3.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "4fdf221b77d26e02d95da57095f21c8b", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 4952, "upload_time": "2018-10-08T10:31:07", "url": "https://files.pythonhosted.org/packages/01/59/e85fc9baad881f868a06e27089d13ae05e023ce0837bf537e766bcb873cb/single_beat-0.3.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "6eb5c59180b39ca3c4a81f9cf9dc5776", "sha256": "befa7e99d6b976a10eb71da5a539d21653b1d7680e8f0d42a937e4c4c0ca1e8f" }, "downloads": -1, "filename": "single-beat-0.3.0.tar.gz", "has_sig": false, "md5_digest": "6eb5c59180b39ca3c4a81f9cf9dc5776", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 3768, "upload_time": "2018-10-08T10:31:08", "url": "https://files.pythonhosted.org/packages/36/88/2724451a5919395022f88527bffd390396049ea51bb5cee43fce5d86cd3f/single-beat-0.3.0.tar.gz" } ], "0.3.1": [ { "comment_text": "", "digests": { "md5": "8be89a941d3fdd5f0c46d2f3c0220666", "sha256": "be57fcdf79ba32aa13d032e304be1148d4da31cd2547ecc514cdaf15b0372d3c" }, "downloads": -1, "filename": "single_beat-0.3.1-py3-none-any.whl", "has_sig": false, "md5_digest": "8be89a941d3fdd5f0c46d2f3c0220666", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 7187, "upload_time": "2018-10-10T08:08:27", "url": "https://files.pythonhosted.org/packages/64/70/3591027b556e4faf38ebaec45267b3806b76816b86d7330f555fd8fc1596/single_beat-0.3.1-py3-none-any.whl" } ], "0.3.2": [ { "comment_text": "", "digests": { "md5": "3f10d4c0a01558593a6dd9a85c0b3444", "sha256": "a1a83d7242b8f5cdec0044e77bf85d0f87849aa8e5dbbcbeea9d83cb0f0d9e70" }, "downloads": -1, "filename": "single_beat-0.3.2-py2-none-any.whl", "has_sig": false, "md5_digest": "3f10d4c0a01558593a6dd9a85c0b3444", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 9435, "upload_time": "2019-01-21T16:16:02", "url": "https://files.pythonhosted.org/packages/93/0f/ef48ca52c9f9d2d55b04736f3c7492dc23bb9033fd1726f8b3b1d1c7de47/single_beat-0.3.2-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "49b98d604c7e820ead8aa9259333be4a", "sha256": "2757e8c153d6be76ad764c5cd85b1b1598b0ba31df4ee9b3b9f64d33cff5407f" }, "downloads": -1, "filename": "single-beat-0.3.2.tar.gz", "has_sig": false, "md5_digest": "49b98d604c7e820ead8aa9259333be4a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6494, "upload_time": "2019-01-21T16:16:04", "url": "https://files.pythonhosted.org/packages/01/fe/4d4ab63342a112101a58a705ba29d67eb977ae3d1e692a442f8c3bbfcf59/single-beat-0.3.2.tar.gz" } ], "0.4.1": [ { "comment_text": "", "digests": { "md5": "48e13802223fbc114b8eeaa55703ee87", "sha256": "c96e4067517f9296ef22542bda93ddacf69f4329c45f5b0a0379577c4a11bd71" }, "downloads": -1, "filename": "single_beat-0.4.1-py3-none-any.whl", "has_sig": false, "md5_digest": "48e13802223fbc114b8eeaa55703ee87", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 10881, "upload_time": "2019-02-21T11:03:26", "url": "https://files.pythonhosted.org/packages/ee/fc/957d7394d6c79636256ea9e4fc47f06f24d6986c09f1e7bda20359281391/single_beat-0.4.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "fe4745c614f2470f8b32152d1e1ea973", "sha256": "d8a8e117e50b5a72448ad9f66638930b6ef83184a62f6d1451520636b67667aa" }, "downloads": -1, "filename": "single-beat-0.4.1.tar.gz", "has_sig": false, "md5_digest": "fe4745c614f2470f8b32152d1e1ea973", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9885, "upload_time": "2019-02-21T11:03:27", "url": "https://files.pythonhosted.org/packages/09/0e/a549bba032c7fcaf832a18349865dcc224425549aafd1f1b9d297995cf9c/single-beat-0.4.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "48e13802223fbc114b8eeaa55703ee87", "sha256": "c96e4067517f9296ef22542bda93ddacf69f4329c45f5b0a0379577c4a11bd71" }, "downloads": -1, "filename": "single_beat-0.4.1-py3-none-any.whl", "has_sig": false, "md5_digest": "48e13802223fbc114b8eeaa55703ee87", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 10881, "upload_time": "2019-02-21T11:03:26", "url": "https://files.pythonhosted.org/packages/ee/fc/957d7394d6c79636256ea9e4fc47f06f24d6986c09f1e7bda20359281391/single_beat-0.4.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "fe4745c614f2470f8b32152d1e1ea973", "sha256": "d8a8e117e50b5a72448ad9f66638930b6ef83184a62f6d1451520636b67667aa" }, "downloads": -1, "filename": "single-beat-0.4.1.tar.gz", "has_sig": false, "md5_digest": "fe4745c614f2470f8b32152d1e1ea973", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9885, "upload_time": "2019-02-21T11:03:27", "url": "https://files.pythonhosted.org/packages/09/0e/a549bba032c7fcaf832a18349865dcc224425549aafd1f1b9d297995cf9c/single-beat-0.4.1.tar.gz" } ] }