{ "info": { "author": "Eric V. Smith", "author_email": "eric@trueblade.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "===========\nexpiration\n===========\n\nOverview\n========\n\nexpiration is designed to manage complex expiration rules. My specific\nuse case is for backups, where I want to keep yearly backups forever,\nmonthly backups for 2 years, weekly backups for 3 months, daily\nbackups for 2 weeks, and hourly backups for 1 week. with expiration,\nyou'd implement the rules describing this as::\n\n >>> from __future__ import print_function\n >>> import expiration\n >>> from datetime import timedelta\n >>> expiration_rules = expiration.RetentionRules(timedelta(weeks=1), # default value\n ... [expiration.RetentionCriterion(expiration.date.year_of,\n ... None),\n ... expiration.RetentionCriterion(expiration.date.month_of,\n ... timedelta(days=2*365)),\n ... expiration.RetentionCriterion(expiration.date.week_of,\n ... timedelta(days=30*3)),\n ... expiration.RetentionCriterion(expiration.date.day_of,\n ... timedelta(weeks=2)),\n ... ])\n\nNext, you need some items that you want to check for expiration. These\nobjects can be any type at all: expiration does not inspect them,\nexcept to extract a timestamp from them. And you provide that\ntimestamp access function, called `time_key`.\n\nFor demonstration, I'll create a bunch of objects with expiration\ndates::\n\n >>> from collections import namedtuple\n >>> from datetime import datetime\n >>> Item = namedtuple('Item', 'name expiration')\n >>> items = [Item(1, datetime(2012, 1, 1)),\n ... Item(2, datetime(2014, 1, 3)),\n ... Item(3, datetime(2014, 2, 1)),\n ... Item(4, datetime(2014, 2, 2)),\n ... Item(4, datetime(2014, 7, 1)),\n ... Item(5, datetime(2014, 7, 25, 8, 0)),\n ... Item(6, datetime(2014, 7, 25, 9, 0)),\n ... Item(7, datetime(2014, 7, 25, 10, 0)),\n ... ]\n\nAnd now, we find which ones have expired::\n\n >>> from operator import attrgetter\n >>> for item, criterion in expiration.find_expired_items(items, attrgetter('expiration'),\n ... expiration_rules,\n ... asof_timestamp=datetime(2014, 8, 1)):\n ... print(item)\n Item(name=4, expiration=datetime.datetime(2014, 2, 2, 0, 0))\n\nThis shows that under these rules, item 4 is the only one that would\nbe deleted. It's not the first one in a month and it's more than 3\nmonths old, so it's due to be expired.\n\nIf, instead, we ask what items have expired as of January 1, 2020::\n\n >>> for item, criterion in expiration.find_expired_items(items, attrgetter('expiration'),\n ... expiration_rules,\n ... asof_timestamp=datetime(2020, 1, 1)):\n ... print(item)\n Item(name=3, expiration=datetime.datetime(2014, 2, 1, 0, 0))\n Item(name=4, expiration=datetime.datetime(2014, 2, 2, 0, 0))\n Item(name=4, expiration=datetime.datetime(2014, 7, 1, 0, 0))\n Item(name=5, expiration=datetime.datetime(2014, 7, 25, 8, 0))\n Item(name=6, expiration=datetime.datetime(2014, 7, 25, 9, 0))\n Item(name=7, expiration=datetime.datetime(2014, 7, 25, 10, 0))\n\nThe only items that would be kept are 1 and 2, since they're the first\nitems at the start of each year, which has an infinite expiration\ntime.\n\nLimitations\n===========\n\nBecause `find_expired_items` only compares one item at a time to the\nrules, it can effectively only implement \"first of\" rules. That is,\napply a criterion to the first backup of the year, or the first one of\nthe month, etc. It cannot currently implement rules like \"keep the\nsecond backup of the month\". If you did delete the first backup and\nkept the second one, then the next time `find_expired_items` ran, it\nwould see what used to be the second item as now being the first one.\n\nChange log\n==========\n\n1.0 2016-10-27 Eric V. Smith\n----------------------------\n\n* Always require setuptools (issue #1).\n\n* Remove hack to modify RPM name (issue #2).\n\n* No code changes.\n\n0.1 2014-12-15 Eric V. Smith\n----------------------------\n\n* Initial release.\n\n\n", "description_content_type": null, "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://bitbucket.org/ericvsmith/expiration", "keywords": "", "license": "Apache License Version 2.0", "maintainer": "", "maintainer_email": "", "name": "expiration", "package_url": "https://pypi.org/project/expiration/", "platform": "", "project_url": "https://pypi.org/project/expiration/", "project_urls": { "Homepage": "https://bitbucket.org/ericvsmith/expiration" }, "release_url": "https://pypi.org/project/expiration/1.0/", "requires_dist": null, "requires_python": "", "summary": "Manages complex expiration rules.", "version": "1.0" }, "last_serial": 2426732, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "9df8291bf9cbc258a4149686f4cbc2b2", "sha256": "248dd5c2d11013edb016402e5f1bd583494837a74eea6de2b1dc322d8783780d" }, "downloads": -1, "filename": "expiration-0.1.tar.gz", "has_sig": false, "md5_digest": "9df8291bf9cbc258a4149686f4cbc2b2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8983, "upload_time": "2014-12-15T23:31:59", "url": "https://files.pythonhosted.org/packages/89/01/f434070a822d73db5f001074100512e94278c167fa720eb6502f77b7103b/expiration-0.1.tar.gz" } ], "1.0": [ { "comment_text": "", "digests": { "md5": "ac30c75f7ddb93d0251726d73364d194", "sha256": "d9e704b28439f27f3a5acd358d950fedb39e514f49689a12a50919dfaf3acae0" }, "downloads": -1, "filename": "expiration-1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "ac30c75f7ddb93d0251726d73364d194", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 6572, "upload_time": "2016-10-27T17:39:52", "url": "https://files.pythonhosted.org/packages/c6/c3/5640545e8268dbba39f6680518c1c8b13e185800245615a1a5917cd248b9/expiration-1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "77acd8c2d4b46ed3476d8bd19ce6f9df", "sha256": "bbdac29eba142bf0833c1fd8e94e244bb8203dcfc20a10becc3ff016279ac948" }, "downloads": -1, "filename": "expiration-1.0.tar.gz", "has_sig": false, "md5_digest": "77acd8c2d4b46ed3476d8bd19ce6f9df", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9136, "upload_time": "2016-10-27T17:39:54", "url": "https://files.pythonhosted.org/packages/42/1b/5e622186e547f3c9b393629dc64ba2f08f496faaf883cda01803e5b5e540/expiration-1.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "ac30c75f7ddb93d0251726d73364d194", "sha256": "d9e704b28439f27f3a5acd358d950fedb39e514f49689a12a50919dfaf3acae0" }, "downloads": -1, "filename": "expiration-1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "ac30c75f7ddb93d0251726d73364d194", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 6572, "upload_time": "2016-10-27T17:39:52", "url": "https://files.pythonhosted.org/packages/c6/c3/5640545e8268dbba39f6680518c1c8b13e185800245615a1a5917cd248b9/expiration-1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "77acd8c2d4b46ed3476d8bd19ce6f9df", "sha256": "bbdac29eba142bf0833c1fd8e94e244bb8203dcfc20a10becc3ff016279ac948" }, "downloads": -1, "filename": "expiration-1.0.tar.gz", "has_sig": false, "md5_digest": "77acd8c2d4b46ed3476d8bd19ce6f9df", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9136, "upload_time": "2016-10-27T17:39:54", "url": "https://files.pythonhosted.org/packages/42/1b/5e622186e547f3c9b393629dc64ba2f08f496faaf883cda01803e5b5e540/expiration-1.0.tar.gz" } ] }