{ "info": { "author": "Illya Gerasymchuk", "author_email": "illya@iluxonchik.me", "bugtrack_url": null, "classifiers": [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Software Development :: Testing", "Topic :: Software Development :: Testing :: Unit" ], "description": "# File Guard: Preserve The Content Of Files and Directories\n\nProtect the content of your files and directories in a certain scope.\nIn that scope, you can change the contents of the file/directory as you wish.\nAfter the scope has ended, the original contents of the file(s) and/or\ndirectory(ies) will be restored. The \"scope\" can either be a `with` block or a\nfunction/method.\n\nBelow is an example of guarding the contents of the `allure.txt` file.\nInside the `with guard('allure.txt')` block, the content of the file is\nchanged. However, after its scope ends, the previous content of the file is\nrestored:\n\n```python\n>>> from fileguard import guard\n>>> with open('allure.txt', 'r') as f:\n print(f.read())\n\nThe allure of breaking the law\nWas always too much for me to ever ignore\n>>> with guard('allure.txt'):\n with open('allure.txt', 'w') as f:\n f.write('Still Dre Day')\n\n with open('allure.txt', 'r') as f:\n print(f.read())\n\nStill Dre Day\n>>> with open('allure.txt', 'r') as f:\n print(f.read())\n\nThe allure of breaking the law\nWas always too much for me to ever ignore\n```\n\n# Installation\n\nTo install `fileguard`, simply use pip:\n\n```\npip install fileguard\n```\n\n# Requirements\n\n* Python `3.6+`. It should work on other Python `3.X` versions too.\n\nThis library has no external dependencies.\n\n# Where and When Is FileGuard Useful?\n\nThis library is useful in various scenarios, specially in testing, as it\nallows you to save a lot of boilerplate and error-prone code.\n\nFor example,\nlet's say that the program that you are developing reads an writes to\na configuration file. In your tests, you manually create a configuration file\nand then want to test that your program interacts with it in the expected\nmanner.\n\nIn one of your test functions you want to write tests for when the\na function that updates the configuration file with invalid data and then\nyour program reads it in. In another test function you want to test that\nthe program does not crash if the configuration file is deleted. Another test\nfunction tests the case where a deleted configuration file is updated. In yet another\ntest function you want to test that changing the contents of the configuration\nfile works as expected. You are only interested in testing how your program\nreacts to changes of the configuration file in the scope of each one of those\ntest functions. After those, you want the contents of the original\nconfiguration file to be restored.\n\nIf done manually you have two options:\n\n* write boilerplate code to back up and restore the contents of the file\nbefore and after each test function, respectively\n* have a lot of copies of configuration files (one for invalid data, one with\n empty data, one with non-existent data, etc). At the end, you would still\n be left with the task of restoring the contents of the edited/deleted files\n\nKeep in mind, that the example described above is rather simple. What if:\n\n* you want to preserve the contents of multiple files\n* you want to preserve the contents, not just of a file, but of the whole\ndirectory\n* you're not dealing with `UTF-8` encoded text files, but rather with\nbinaries, music files, images, etc\n* you want to preserve the contents of the file in a scope inside a function\n(e.g. within an `with` block) and not the of the whole function\n\nAll of those add complexity to the error-prone boilerplate code and make\nit dirty, complex, hard to maintain and confusing.\n\nThe `fileguard` library allows you to tackle those problems in a clean,\nPythonic way. All you have to do is decorate the test function with `@guard`\n\n# API Documentation and Examples\n\nIn the text that follows, **\"fileguarded\"** means that the contents of the\nfile(s) and/or directory(ies) will be preserved. In other words, their\ncontent after the end of the scope will be the same as it was right\nbefore the beginning of the scope.\n\nIn order to use `fileguard`, all you have to do is import `guard`:\n\n```python\nfrom fileguard import guard\n```\n\n`guard` can be used as a:\n\n* function/method decorator\n * the scope of the function will be file-guarded\n\n ```python\n @guard('my_file.txt')\n def change_my_file():\n # Within this function, change the contents of the file as you wish.\n # You can even delete it.\n # After the function has executed, 'my_file.txt's contents will be\n # the same as they were right before the execution of this function.\n\n ```\n\n* class decorator\n * all of the **user-defined** methods will be file-guarded.\n The following two code snippets are equivalent:\n\n ```python\n @guard('my_file.txt')\n class MyClass(object):\n\n def __init__(self, my_arg_1, my_arg_2):\n self._my_arg_1 = my_arg_1\n self._my_arg_2 = my_arg_2\n\n def my_method_1(self):\n # code here\n\n def my_method_2(self):\n # code here\n\n ```\n\n ```python\n class MyClass(object):\n\n def __init__(self, my_arg_1, my_arg_2):\n self._my_arg_1 = my_arg_1\n self._my_arg_2 = my_arg_2\n\n @guard('my_file.txt')\n def my_method_1(self):\n # code here\n\n @guard('my_file.txt')\n def my_method_2(self):\n # code here\n ```\n\n* context manager\n * all of the code within the `with` block is file-guarded, including\n calls to functions that change the contents of the fileguarded\n files and directories.\n\n ```python\n with guard('my_file.txt'):\n # Within \"with\" block, change the contents of the file as you wish.\n # You can even delete it.\n # After the \"with\" scope has ended, 'my_file.txt's contents will be\n # the same as they were right before the execution of this with block.\n ```\n\n\n## What If The File/Directory Is Deleted?\n\n* The files will be restored, even if you **delete** them.\n\n* The complete contents of the directory will be restored, even if you\n**delete** the whole directory or some files from within it.\n\n## Arguments\n\n`guard()` accepts the list of files and directories to fileguard. It can take\na single argument:\n\n```python\n@guard('file.txt')\ndef my_function(arg1, arg2):\n # code here\n```\n\nor multiple ones:\n\n```python\n@guard('file_1.txt', 'file_2.txt')\ndef my_function(arg1, arg2):\n # code here\n```\n\nthe snippet above is equivalent to:\n\n```python\n@guard('file_2.txt')\n@guard('file_1.txt')\ndef my_function(arg1, arg2):\n # code here\n```\n\nIn that case, the contents of all of the files passed as argument will be\nfileguarded.\n\nYou can pass a file, a directory or a mixture of them as an arguments:\n\n```python\n@guard('file_1.txt', 'directory_1', '/home/iluxonchik/directory_2', '/home/iluxonchik/file_2.txt')\ndef my_function(arg1, arg2):\n # code here\n```\n\nIn that case, the contents of the file `./file_1.txt`, directory `./directory_1`,\ndirectory `/home/iluxonchik/directory_2` and file `/home/iluxonchik/file_2.txt`\nwill be fileguarded.\n\nThe fileguarded files and directories **do not need to exist at the moment of decoration**.\nYou can fileguard a file or directory in function, method and or a class (by decorating it with `guard()`),\nwithout that file or directory existing yet. You have to make sure that the file\nexists **when the decorated function or method is executed**.\nIf, however, you are using `guard()` as a context manager, you have to make sure that\nthe protected file or directory **does exist** at the moment when you use\n`with guard():`\n\n## Supported File Types\n\nAny file type is supported. You can guard a text file, a binary, an music\nfile, an image file, a video file, etc. Under the hood, the original file\nis backed up by its copy.\n\n## Directories\n\nJust like files, directories can contain arbitrary files. The original\ndirectory and its contents will be restored. Under the hood, the original\ndirectory is backed up by a copy of all of its contents.\n\n## File-Guarded Functions Calling File-Guarded Functions (Nested Calls)\n\nThe backup order is preserved. Internally, a stack is used. The best\nway to illustrate this is with an example.\n\nLet's consider that you have a file `lets_ride.txt` with the following\ncontent:\n\n```\n It\u2019s a new day, and if you ever knew Dre\n\n```\n\nAlso, `fileguard_demo.py` contents are as follows:\n\n```python\n\"\"\" Contents of fileguard_demo.py\"\"\"\n\nfrom fileguard import guard\n\ndef print_file_content():\n with open('lets_ride.txt', 'r') as f:\n print(f.read())\n\ndef append_text_to_file(text):\n with open('lets_ride.txt', 'a') as f:\n f.write(text)\n\n@guard('lets_ride.txt')\ndef change_file_1():\n print('Content before change_file_1:')\n print_file_content()\n\n append_text_to_file('\\tSame Chronic, just a different smoke\\n')\n\n print('Content after change_file_1:')\n print_file_content()\n\n@guard('lets_ride.txt')\ndef change_file_2():\n print('Content before change_file_2:')\n print_file_content()\n\n append_text_to_file('\\tSame Impala, different spokes\\n')\n\n change_file_1()\n\n print('Content after change_file_2:')\n print_file_content()\n\n@guard('lets_ride.txt')\ndef change_file_3():\n print('Content before change_file_3:')\n print_file_content()\n\n append_text_to_file('\\tYou would say I was The New Dre\\n')\n\n change_file_2()\n\n print('Content after change_file_3:')\n print_file_content()\n\nprint('Intial file content:')\nprint_file_content()\n\nwith guard('lets_ride.txt'):\n print('Content before with:')\n print_file_content()\n\n change_file_3()\n\n print('Content after with:')\n print_file_content()\n\nprint('Final file content:')\nprint_file_content()\n```\n\nThe output of running `python fileguard_demo.py` is the following:\n\n```\nIntial file content:\n It\u2019s a new day, and if you ever knew Dre\n\nContent before with:\n It\u2019s a new day, and if you ever knew Dre\n\nContent before change_file_3:\n It\u2019s a new day, and if you ever knew Dre\n\nContent before change_file_2:\n It\u2019s a new day, and if you ever knew Dre\n You would say I was The New Dre\n\nContent before change_file_1:\n It\u2019s a new day, and if you ever knew Dre\n You would say I was The New Dre\n Same Impala, different spokes\n\nContent after change_file_1:\n It\u2019s a new day, and if you ever knew Dre\n You would say I was The New Dre\n Same Impala, different spokes\n Same Chronic, just a different smoke\n\nContent after change_file_2:\n It\u2019s a new day, and if you ever knew Dre\n You would say I was The New Dre\n Same Impala, different spokes\n\nContent after change_file_3:\n It\u2019s a new day, and if you ever knew Dre\n You would say I was The New Dre\n\nContent after with:\n It\u2019s a new day, and if you ever knew Dre\n\nFinal file content:\n It\u2019s a new day, and if you ever knew Dre\n\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/iluxonchik/fileguard", "keywords": "testing,testing tools,test,file,directory,preserve content,backup", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "fileguard", "package_url": "https://pypi.org/project/fileguard/", "platform": "", "project_url": "https://pypi.org/project/fileguard/", "project_urls": { "Homepage": "https://github.com/iluxonchik/fileguard" }, "release_url": "https://pypi.org/project/fileguard/0.0.1/", "requires_dist": null, "requires_python": "", "summary": "Preserve the content of your files and directories during testing.", "version": "0.0.1" }, "last_serial": 4060210, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "de892ce091a19c212afd4b836c659c0b", "sha256": "ab6b4b46c864b4a5232bdc4066a92e326e300a9ad5c9678f6c1ae26d9cfb4f25" }, "downloads": -1, "filename": "fileguard-0.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "de892ce091a19c212afd4b836c659c0b", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 6348, "upload_time": "2018-07-14T00:26:23", "url": "https://files.pythonhosted.org/packages/4f/93/92f0cb347bf6f82f71dbdeeca1a32d22e5f6adb46c47d7bfead5a2b41989/fileguard-0.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "900f344f2f044ba07461568b3bfcf1f3", "sha256": "18436009e3df1c27631c2f4f367e16e541cfc035342e6292b239504fdc8bfb58" }, "downloads": -1, "filename": "fileguard-0.0.1.tar.gz", "has_sig": false, "md5_digest": "900f344f2f044ba07461568b3bfcf1f3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7865, "upload_time": "2018-07-14T00:26:25", "url": "https://files.pythonhosted.org/packages/cd/35/6a7da961d69e9169016f99eb11de09b89d1079d5edef0925f341d1c34440/fileguard-0.0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "de892ce091a19c212afd4b836c659c0b", "sha256": "ab6b4b46c864b4a5232bdc4066a92e326e300a9ad5c9678f6c1ae26d9cfb4f25" }, "downloads": -1, "filename": "fileguard-0.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "de892ce091a19c212afd4b836c659c0b", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 6348, "upload_time": "2018-07-14T00:26:23", "url": "https://files.pythonhosted.org/packages/4f/93/92f0cb347bf6f82f71dbdeeca1a32d22e5f6adb46c47d7bfead5a2b41989/fileguard-0.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "900f344f2f044ba07461568b3bfcf1f3", "sha256": "18436009e3df1c27631c2f4f367e16e541cfc035342e6292b239504fdc8bfb58" }, "downloads": -1, "filename": "fileguard-0.0.1.tar.gz", "has_sig": false, "md5_digest": "900f344f2f044ba07461568b3bfcf1f3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7865, "upload_time": "2018-07-14T00:26:25", "url": "https://files.pythonhosted.org/packages/cd/35/6a7da961d69e9169016f99eb11de09b89d1079d5edef0925f341d1c34440/fileguard-0.0.1.tar.gz" } ] }