{ "info": { "author": "Hayden Flinner", "author_email": "hayden@flinner.me", "bugtrack_url": null, "classifiers": [ "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7" ], "description": "Welcome to cachepath's documentation!\n======================================\n\n.. image:: https://img.shields.io/pypi/v/cachepath.svg\n :target: https://pypi.python.org/pypi/cachepath\n\n.. image:: https://img.shields.io/travis/haydenflinner/cachepath.svg\n :target: https://travis-ci.org/haydenflinner/cachepath\n\n.. image:: https://readthedocs.org/projects/cachepath/badge/?version=latest\n :target: https://cachepath.readthedocs.io/en/latest/?badge=latest\n :alt: Documentation Status\n\nA small package for pythonic parameterized cache paths.\n\nGetting Started\n----------------\n\n**Install:** ``pip install cachepath``\n\n**Import:** ``from cachepath import CachePath, TempPath, Path``\n\n**Docs:** `ReadTheDocs`_ | `API doc is here`_\n\n**Why?**\n 1. Integrates ``pathlib`` with ``tempfile.gettempdir`` and ``shutil.rmtree`` by providing ``TempPath`` and ``Path.rm()``::\n\n path = TempPath()\n path.rm()\n # or would you rather..\n path = None\n with tempfile.NamedTemporaryFile(delete=False) as f:\n path = Path(f.name)\n # Only now can we use Path. If we tried using it within the With\n # (for example for path.read_text()), we'd break on Windows\n path.unlink() # only if file, doesn't work on folders\n\n 2. Wraps ``pathlib`` import for Py2/3 compat. (not in ``six``!)::\n\n from cachepath import Path\n # or\n try: from pathlib import Path; except ImportError: from pathlib2 import Path\n\n 3. Provides `CachePath`_, which lets you quickly get a parameterized temp filename, with all folders automatically created::\n\n r = CachePath(date, userid, 'expensive_results.txt')\n assert (r == Path('/tmp/', date, userid, 'expensive_results.txt')\n and r.parent.exists())\n r.rm() # File remove\n r.parent.rm() # Symmetric with folder remove!\n\n # Without cachepath\n p = Path(tempfile.gettempdir(), date, userid, 'expensive_results.txt').\n # Don't update timestamp if it already exists so that we don't cause\n # Make-like tools to always think something's changed\n if not p.parent.exists():\n p.parent.mkdir(parents=True, exist_ok=True)\n\n p.unlink() # Why is it .unlink() instead of .remove()?\n # Why .remove and .unlink, but mkdir instead of createdir?\n p.parent.remove()\n # .remove() might throw because there was another file in the folder,\n # but we didn't care, they're tempfiles!\n import shutil\n shutil.rmtree(p.parent)\n\n**Why, but longer:**\n\nDo you need a temp path to pass to some random tool for its logfile?\nBehold, a gaping hole in ``pathlib``::\n\n import tempfile\n import os\n try: from pathlib import Path; except ImportError: from pathlib2 import Path\n def get_tempfile():\n fd, loc = tempfile.mkstemp()\n os.close(fd) # If we forgot do this, it would stay open until process exit\n return Path(loc)\n\n # Easier way\n from cachepath import TempPath\n def get_tempfile():\n return TempPath() # Path('/tmp/213kjdsrandom')\n\n\nBut this module is called cachepath, not temppath, what gives?\n\nSuppose I'm running that same imaginary tool pretty often, but I'd like to skip running\nit if I already have results for a certain day. Just sticking some identifying info into a filename\nshould be good enough.\nSomething like ``Path('/tmp/20181204_toolresults.txt')`` ::\n\n # try: from pathlib import Path; except ImportError: from pathlib2 import Path\n # We'll cheat a little to get py2/3 compat without so much ugliness\n from cachepath import Path\n import tempfile\n def get_tempfile(date):\n filename = '{}_toolresults.txt'.format(date)\n return Path(tempfile.gettempdir(), filename)\n\n # Easier to do this...\n from cachepath import CachePath\n def get_tempfile(date):\n return CachePath(date, suffix='.txt')\n\nNot bad, but not great. But our requirements changed, let's go a step further.\n\nNow I'm running this tool *a lot*, over a tree of data that looks\nlike this::\n\n 2018-12-23\n person1\n person2\n 2018-12-24\n person1\n 2018-12-25\n person1\n\nI want my logs to be structured the same way. How hard can it be? ::\n\n 2018-12-23/\n person1_output.txt\n person2_output.txt\n 2018-12-24/\n person1_output.txt\n 2018-12-25/\n person1_output.txt\n\nLet's find out::\n\n # Let's get the easy way out of the way first :)\n def get_path(date, person):\n return CachePath(date, person, suffix='_output.txt')\n # Automatically ensures /tmp/date/ exists when we create the CachePath!\n\n # Now the hard way\n def get_path(date, person):\n personfilename = '{p}_output.txt'.format(p=person)\n returning = Path(tempfile.gettempdir())/date/personfilename\n # Does this mkdir update the modified timestamp of the folders we're in?\n # Might matter if we're part of a larger toolset...\n returning.parent.mkdir(exist_ok=True, parents=True)\n return returning\n\nSuppose we hadn't remembered to make the ``$date/`` folders. When we passed the\nPath out to another tool, or tried to .open it,\nwe may have gotten a Permission Denied\nerror on Unix systems rather than the \"File/Folder not found\" you might expect.\nWith CachePath, this can't happen. Creating a CachePath implicitly creates all\nof the preceding directories necessary for your file to exist.\n\nNow, suppose we found a bug in this external tool we were using and we're going\nto re-run it for a day.\nHow do we clear out that day's results so that we can be sure we're looking\nat fresh output from the tool? Well, with CachePath, it's just::\n\n def easy_clear_date(date):\n CachePath(date).clear() # rm -r /tmp/date/*\n\nBut if you don't have cachepath, you'll find that most Python libs play it\npretty safe when it comes to files. Path.remove() requires the folder to be empty,\nand doesn't provide a way to empty the folder. Not to mention, what if our results\nfolders had special permissions, or was actually a symlink, and we had write access\nbut not delete? Oh well,\nlet's see what we can do::\n\n def hard_clear_date(date):\n # We happen to know that date is a folder and not a file (at least in our\n # current design), so we know we need some form of .remove() rather than\n # .unlink(). Unfortunately, pathlib doesn't offer one for folders with\n # files still in them. If you google how to do it, you will find plenty of\n # answers, one of which is a pure pathlib recursive solution! But we're lazy,\n # so lets bring in yet another module:\n p = Path(tempfile.gettempdir(), date)\n import shutil\n if p.exists():\n shutil.rmtree(p)\n p.mkdir(exist_ok=True, parents=True)\n # This still isn't exactly equivalent to CachePath.clear(), because we've\n # lost whatever permissions were set on the date folder, and if it were\n # actually a symlink to somewhere else, that's gone now.\n\nConvinced yet? ``pip install cachepath`` or copy `the source`_ into your local\n``utils.py`` (you know you have one.)\n\n`API doc is here`_.\n\n\nBy the way, as a side effect of importing ``cachepath``, all Paths get the ability\nto do ``rm()`` and ``clear()``.\n\n\nShameless Promo\n----------------\nFind yourself working with paths a lot in cmd-line tools? You might like\n`invoke`_ and/or `magicinvoke`_!\n\n\n\n.. [*] The source for CachePath can be downloaded from the `Github repo`_.\n\n.. _Github repo: https://github.com/haydenflinner/cachepath\n.. [*] This package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypackage`_ project template.\n\n.. _`the source`: https://github.com/haydenflinner/cachepath/blob/master/cachepath/__init__.py\n.. _Cookiecutter: https://github.com/audreyr/cookiecutter\n.. _`audreyr/cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage\n.. _`invoke`: https://www.pyinvoke.org\n.. _`magicinvoke`: https://magicinvoke.readthedocs.io/en/latest/\n.. _`ReadTheDocs`: https://cachepath.readthedocs.io/en/latest/\n.. _`API doc is here`: https://cachepath.readthedocs.io/en/latest/cachepath.html\n.. _`CachePath`: https://cachepath.readthedocs.io/en/latest/cachepath.html#cachepath.CachePath\n\n\n=======\nHistory\n=======\n\n1.0.0 (2018-12-08)\n------------------\n\n* Big doc updates. 1.0.0 to symbolize SemVer adherence.\n\n0.1.0 (2018-12-08)\n------------------\n\n* First release on PyPI. Adds CachePath, TempPath, Path.\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/haydenflinner/cachepath", "keywords": "cachepath,paths,pathlib,cache,temp", "license": "MIT license", "maintainer": "", "maintainer_email": "", "name": "cachepath", "package_url": "https://pypi.org/project/cachepath/", "platform": "", "project_url": "https://pypi.org/project/cachepath/", "project_urls": { "Homepage": "https://github.com/haydenflinner/cachepath" }, "release_url": "https://pypi.org/project/cachepath/1.1.1/", "requires_dist": null, "requires_python": "", "summary": "Pythonic parameterized cache paths.", "version": "1.1.1" }, "last_serial": 4577535, "releases": { "0.1.1": [ { "comment_text": "", "digests": { "md5": "d798d226e86e67977174ae53d89c7a7a", "sha256": "b40d75a705ac533d85454c2c41a373e4bff23cbdbba0df4fb7c10cca9e4fb13f" }, "downloads": -1, "filename": "cachepath-0.1.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "d798d226e86e67977174ae53d89c7a7a", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 6948, "upload_time": "2018-12-08T20:51:15", "url": "https://files.pythonhosted.org/packages/0b/eb/3c85b64ce629bd8542153199c7a4b2484e93a085e236de13cf4a4a5b505f/cachepath-0.1.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "92ce22ee1c4690b9b0b15a43f833c280", "sha256": "6e312e55b799e3348b9b10829697be568d138d1a4880fe3373d5360535050965" }, "downloads": -1, "filename": "cachepath-0.1.1.tar.gz", "has_sig": false, "md5_digest": "92ce22ee1c4690b9b0b15a43f833c280", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11894, "upload_time": "2018-12-08T20:51:17", "url": "https://files.pythonhosted.org/packages/c8/10/5bc0e19d589e5a2847ed0591d97811ad05a1008c39d449e203850ca1c648/cachepath-0.1.1.tar.gz" } ], "1.0.0": [ { "comment_text": "", "digests": { "md5": "8075bc3ec2134756653e5f72e3645e17", "sha256": "1176c7923dfdb0d6c5244528875d3e5aada779611f1d4b2a6d8f388da5d11b2d" }, "downloads": -1, "filename": "cachepath-1.0.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "8075bc3ec2134756653e5f72e3645e17", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 6909, "upload_time": "2018-12-08T22:29:52", "url": "https://files.pythonhosted.org/packages/85/db/7617f836d6b93a7051907cfd281bd6a55bc4e23afb77201179e52ffbeb1d/cachepath-1.0.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "7b45d08076e379f5e609294fa9bfeee7", "sha256": "300c333732451daf65c832799259628b8f14e8441e53c5e442739f316b912892" }, "downloads": -1, "filename": "cachepath-1.0.0.tar.gz", "has_sig": false, "md5_digest": "7b45d08076e379f5e609294fa9bfeee7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12117, "upload_time": "2018-12-08T22:29:53", "url": "https://files.pythonhosted.org/packages/7c/50/ce7b8003de738d6b2aecffcecfc467beb9e52e48f27dccc15bf51e42985d/cachepath-1.0.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "d1c5c225bb40c5fa9b5f3bc58dc0ee99", "sha256": "aac9d60fc27b62226eda1ffe212d6c1cac01847299327446018a61e742f06950" }, "downloads": -1, "filename": "cachepath-1.0.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "d1c5c225bb40c5fa9b5f3bc58dc0ee99", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 6907, "upload_time": "2018-12-08T22:28:42", "url": "https://files.pythonhosted.org/packages/aa/58/959ecefba0335e3769721ffdc490ced3f1a1bee3e76fb0e53e3e7244ad05/cachepath-1.0.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a8243c47cc05fabca1ac3295cbd94b4b", "sha256": "411a0ba053be144f16b7f5985e7ae17e3da3dbbec515132bf7125d4708713da5" }, "downloads": -1, "filename": "cachepath-1.0.1.tar.gz", "has_sig": false, "md5_digest": "a8243c47cc05fabca1ac3295cbd94b4b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12133, "upload_time": "2018-12-08T22:28:43", "url": "https://files.pythonhosted.org/packages/70/73/f82fa27187005c4a77fbdab11f569835235b5514474682ad7b17b83654e3/cachepath-1.0.1.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "79e571db62652259e75fd5a85cc30b8e", "sha256": "9b7541ae9e2a4ef285c3b37ee010dd54283542df72f4df9dbc2f0d297bd7f12c" }, "downloads": -1, "filename": "cachepath-1.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "79e571db62652259e75fd5a85cc30b8e", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 6911, "upload_time": "2018-12-08T22:37:12", "url": "https://files.pythonhosted.org/packages/59/46/5a4f116e284348e4bb836f33695cd160a750e17c7435820f9be95095dc28/cachepath-1.1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a187948874530e68f5befce92b0c025a", "sha256": "0a904873ddfbb28cd7ea212f98ed782486b8d3b220791003cdf6b703db478740" }, "downloads": -1, "filename": "cachepath-1.1.0.tar.gz", "has_sig": false, "md5_digest": "a187948874530e68f5befce92b0c025a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12123, "upload_time": "2018-12-08T22:37:13", "url": "https://files.pythonhosted.org/packages/2a/cd/4968309a462865f4e3bccce04f8972e7d30bb0567ab613c20124179ef0ea/cachepath-1.1.0.tar.gz" } ], "1.1.1": [ { "comment_text": "", "digests": { "md5": "6b3a26605814ffbeaa4a7aaf1a581b73", "sha256": "8a92ec61cc2bc40401bda656b47bdb3b16234e500357ff66edd8f8a2c7524b7c" }, "downloads": -1, "filename": "cachepath-1.1.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "6b3a26605814ffbeaa4a7aaf1a581b73", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 8048, "upload_time": "2018-12-09T15:09:46", "url": "https://files.pythonhosted.org/packages/fb/fd/24affbd7937f761c2b7e8afb587651907a191998d3ba1a7c28b6b1d03e02/cachepath-1.1.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ea7ba79bbccd06842a46d9717fe32375", "sha256": "a9a511a5c2e12f1b0ed53111446851e8843bd895a1055356a179a679a9f21bcc" }, "downloads": -1, "filename": "cachepath-1.1.1.tar.gz", "has_sig": false, "md5_digest": "ea7ba79bbccd06842a46d9717fe32375", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13470, "upload_time": "2018-12-09T15:09:47", "url": "https://files.pythonhosted.org/packages/47/da/e1cf0731f9a5086e4278096093bab00d7754242394cb18e1bbedf31bf7aa/cachepath-1.1.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "6b3a26605814ffbeaa4a7aaf1a581b73", "sha256": "8a92ec61cc2bc40401bda656b47bdb3b16234e500357ff66edd8f8a2c7524b7c" }, "downloads": -1, "filename": "cachepath-1.1.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "6b3a26605814ffbeaa4a7aaf1a581b73", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 8048, "upload_time": "2018-12-09T15:09:46", "url": "https://files.pythonhosted.org/packages/fb/fd/24affbd7937f761c2b7e8afb587651907a191998d3ba1a7c28b6b1d03e02/cachepath-1.1.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ea7ba79bbccd06842a46d9717fe32375", "sha256": "a9a511a5c2e12f1b0ed53111446851e8843bd895a1055356a179a679a9f21bcc" }, "downloads": -1, "filename": "cachepath-1.1.1.tar.gz", "has_sig": false, "md5_digest": "ea7ba79bbccd06842a46d9717fe32375", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13470, "upload_time": "2018-12-09T15:09:47", "url": "https://files.pythonhosted.org/packages/47/da/e1cf0731f9a5086e4278096093bab00d7754242394cb18e1bbedf31bf7aa/cachepath-1.1.1.tar.gz" } ] }