{ "info": { "author": "Mattias Ugelvik", "author_email": "uglemat@gmail.com", "bugtrack_url": null, "classifiers": [ "Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: End Users/Desktop", "Intended Audience :: System Administrators", "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", "Programming Language :: Python :: 3", "Topic :: System :: Systems Administration", "Topic :: Utilities" ], "description": "Aka - Rename files in complicated ways easily\n=============================================\nAbstract\n--------\n\nThis package provides a command line utility called ``aka`` for swiftly renaming (or copying) files using Python code.\nThis makes it easy to rename files even when the changes you are making are quite complicated. It always\nrenames files in two passes to avoid collisions; it tries to detect miscellaneous errors in advance; and\nif errors occur underways it will put you in an emergency mode to resolve the problem or roll back changes.\nIt also provides the functions ``aka.rename`` and ``aka.copy``, which is the underlying interface.\n\nThe problem being solved\n------------------------\n\nLets say you have a directory with the files ``File0``, ``File1``, and ``File2``. Then some people comes along and complains\n(rightly or wrongly) that the numbering starts at zero. So you decide to write a program to rename all those files, but a\nproblem arises. You cannot do it in any order you like, you have to start with ``File2 -> File3`` in order to avoid conflicts.\nIt'd be nice to just write a function that knows how to change the names of individual files and let another program sort out the rest.\nThis is what ``aka.rename`` is about:\n\n.. code-block:: pycon\n\n >>> import aka\n >>> from contex import rules\n >>> def machine(fn):\n return rules(r'File(\\d+)', {1: lambda digit: int(digit) + 1}).apply(fn)\n >>> machine('File42')\n 'File43'\n >>> aka.rename(machine)\n Actions to be taken (simplified; doesn't show the temporary stage):\n /home/uglemat/Documents/File0 -> /home/uglemat/Documents/File1\n /home/uglemat/Documents/File1 -> /home/uglemat/Documents/File2\n /home/uglemat/Documents/File2 -> /home/uglemat/Documents/File3\n Target directories:\n /home/uglemat/Documents\n \n The files will be renamed as shown above (in two passes though, in order to avoid\n collisions). This program searched for name conflicts in all target directories\n and did not find any. If errors do pop up, you'll be taken to an emergency mode\n where you can roll back changes. Continue? [N/y]: y\n Renaming /home/uglemat/Documents/File0 -> /tmp/aka_maok91r8/File0\n Renaming /home/uglemat/Documents/File1 -> /tmp/aka_maok91r8/File1\n Renaming /home/uglemat/Documents/File2 -> /tmp/aka_maok91r8/File2\n Renaming /tmp/aka_maok91r8/File0 -> /home/uglemat/Documents/File1\n Renaming /tmp/aka_maok91r8/File1 -> /home/uglemat/Documents/File2\n Renaming /tmp/aka_maok91r8/File2 -> /home/uglemat/Documents/File3\n True\n\nI used `contex.rules `_ to manipulate the string, but you can do whatever you like inside ``machine``, you\njust need to return the new name of the file.\n\nBy default it renames files in the current working directory, but that can be changed with the ``location`` argument to ``aka.rename``. ``aka.copy``\nis basically the same, it just copies files instead. Read the docstrings of those functions to learn the details.\n\nCommand line utility\n--------------------\n\nThat's all fine and dandy, but when you just have some files and you need to rename them, you want to do it with a command line utility. This is the basics:\n\n.. code-block:: bash\n \n $ aka --help\n Useful information ...\n $ aka -p 'fn+\".jpg\"'\n\nThat will add a \".jpg\" suffix to all files in the working directory. But lets do what we did above with ``aka.rename``:\n\n.. code-block:: bash\n \n $ aka -p 'rules(r\"File(\\d+)\", {1: lambda digit: int(digit) + 1})'\n\nThe expression after ``-p`` doesn't need to be a new filename, it can also be a unary callable (like ``machine`` above) that returns the new filename.\nThat is why the example above works; ``contex.rules`` returns a callable. If you want to copy instead of rename, just add in the ``-c`` option:\n\n.. code-block:: bash\n \n $ aka -c -p 'rules(r\"File(\\d+)\", {1: lambda digit: int(digit) + 1})'\n \n -- COPYING FILES IN . --\n \n ERROR: /home/uglemat/Documents/File1 -> /home/uglemat/Documents/File2 is a conflict!\n ERROR: /home/uglemat/Documents/File2 -> /home/uglemat/Documents/File3 is a conflict!\n Aborting...\n\nErr, yes, that won't work, of course. Good thing ``aka`` detects naming conflicts in advance!\n\nMore complicated renaming schemes\n---------------------------------\n\nThat's great, but what if it's not a simple one-liner? Then you need to create a new file,\nwrite some python code, launch the python interpreter, import the stuff you need... It's cumbersome, which is why ``aka`` can help with that:\n\n.. code-block:: bash\n \n $ aka -e emacs\n\nThis will launch emacs and take you to a temporary file which looks kind of like this:\n\n.. code-block:: python\n \n import re\n from os.path import join\n from contex import rules\n \n # Directories in which to perform changes:\n # /home/uglemat/Documents\n \n def rename(fn, dirname):\n return fn\n\n\nYour job is to complete ``rename``, and when you exit the editor it will do the job (after asking you if you want to continue).\n \nLets do something more advanced, say you have lots of files in ``~/Documents/files`` of the format ``File`` and you want to split\nthem into the folders ``odd`` and ``even``, like this:\n\n.. code-block:: bash\n \n ~/Documents/files $ for i in {0..20}; do touch \"File$i\"; done\n ~/Documents/files $ ls\n File0 File1 File10 File11 File12 File13 File14 File15 File16 File17 File18 File19 File2 File20 File3 File4 File5 File6 File7 File8 File9\n ~/Documents/files $ mkdir odd even\n \nThere is a slight problem in that you can't rename ``odd`` and ``even``, but they are in the same directory. You just\ngot to make sure that the rename function returns a falsy value for those filenames (btw, aka treats directories like files and\nwill rename them too). Lets go to the editor with ``aka -e 'emacs -nw'`` and write this:\n\n.. code-block:: python\n\n import re\n from os.path import join\n from contex import rules\n\n # Directories in which to perform changes:\n # /home/uglemat/Documents/files\n\n def rename(fn, dirname):\n match = re.search(r'\\d+', fn)\n if match:\n digit = int(match.group(0))\n return join('even' if even(digit) else 'odd', fn)\n \n\n def even(d):\n return (d % 2) == 0\n\nThe directories ``odd`` and ``even`` doesn't match, so ``rename`` returns ``None`` for those names and thus they are ignored, and\nthe code above works as expected:\n\n.. code-block:: shell-session\n \n ~/Documents/files $ aka -e 'emacs -nw'\n running $ emacs -nw +9:14 /tmp/aka_3uvuyn8c.py\n Aka: Proceed? [Y/n]: y\n \n -- RENAMING FILES IN . --\n \n Actions to be taken (simplified; doesn't show the temporary stage):\n /home/uglemat/Documents/files/File3 -> /home/uglemat/Documents/files/odd/File3\n /home/uglemat/Documents/files/File18 -> /home/uglemat/Documents/files/even/File18\n /home/uglemat/Documents/files/File13 -> /home/uglemat/Documents/files/odd/File13\n ...\n Target directories:\n /home/uglemat/Documents/files/odd\n /home/uglemat/Documents/files/even\n \n The files will be renamed as shown above (in two passes though, in order to avoid\n collisions). This program searched for name conflicts in all target directories\n and did not find any. If errors do pop up, you'll be taken to an emergency mode\n where you can roll back changes. Continue? [N/y]: y\n Renaming /home/uglemat/Documents/files/File3 -> /tmp/aka_st72r5jp/File3\n Renaming /home/uglemat/Documents/files/File18 -> /tmp/aka_st72r5jp/File18\n Renaming /home/uglemat/Documents/files/File13 -> /tmp/aka_st72r5jp/File13\n ...\n Renaming /tmp/aka_st72r5jp/File3 -> /home/uglemat/Documents/files/odd/File3\n Renaming /tmp/aka_st72r5jp/File18 -> /home/uglemat/Documents/files/even/File18\n Renaming /tmp/aka_st72r5jp/File13 -> /home/uglemat/Documents/files/odd/File13\n ~/Documents/files $ ls *\n even:\n File0 File10 File12 File14 File16 File18 File2 File20 File4 File6 File8\n \n odd:\n File1 File11 File13 File15 File17 File19 File3 File5 File7 File9\n\n\nRollbacks\n---------\n\nTo test the rollback feature of ``aka``, lets first launch this command:\n\n.. code-block:: shell-session\n\n $ aka -p 'rules(r\"File(\\d+)\", {1: lambda digit: int(digit) + 1})'\n \n -- RENAMING FILES IN . --\n \n Actions to be taken (simplified; doesn't show the temporary stage):\n /home/uglemat/Documents/File3 -> /home/uglemat/Documents/File4\n /home/uglemat/Documents/File1 -> /home/uglemat/Documents/File2\n /home/uglemat/Documents/File2 -> /home/uglemat/Documents/File3\n Target directories:\n /home/uglemat/Documents\n \n The files will be renamed as shown above (in two passes though, in order to avoid\n collisions). This program searched for name conflicts in all target directories\n and did not find any. If errors do pop up, you'll be taken to an emergency mode\n where you can roll back changes. Continue? [N/y]:\n \nNow it's waiting for confirmation from the user. So we have time to do some sabotage in another shell:\n \n.. code-block:: bash\n \n $ touch File4\n $ ls\n File1 File2 File3 File4\n\nIn the first shell, lets enter ``y`` to see how it fails:\n \n.. code-block:: shell-session\n \n Renaming /home/uglemat/Documents/File3 -> /tmp/aka_1ozr4w4b/File3\n Renaming /home/uglemat/Documents/File1 -> /tmp/aka_1ozr4w4b/File1\n Renaming /home/uglemat/Documents/File2 -> /tmp/aka_1ozr4w4b/File2\n Renaming /tmp/aka_1ozr4w4b/File3 -> /home/uglemat/Documents/File4\n \n \n EMERGENCY MODE: File /home/uglemat/Documents/File4 already exists!\n ERROR: Error happened when trying to rename /tmp/aka_1ozr4w4b/File3 -> /home/uglemat/Documents/File4\n \n What should the program do?\n retry : try again (presumably you've fixed something in the meantime)\n rollback : attempt to undo changes (except for the ones previously continue'd)\n showroll : show which actions will be taken if you choose `rollback`\n exit : exit the program\n continue : ignore the error and move on\n > \n\nOh my, looks like things didn't go as planned. You're now in the emergency prompt of ``aka``. You can easily fix the problem\nby deleting ``File4`` and entering ``retry``, but that's boring. Let's first see what happens when you select ``continue``:\n \n.. code-block:: shell-session\n \n > continue\n Renaming /tmp/aka_1ozr4w4b/File1 -> /home/uglemat/Documents/File2\n Renaming /tmp/aka_1ozr4w4b/File2 -> /home/uglemat/Documents/File3\n LOST FILES IN TEMP DIR: '/tmp/aka_1ozr4w4b'\n $ ls /tmp/aka_1ozr4w4b\n File3\n\nIt's not very nice that it just left the file in the temp dir. ``continue`` is probably rarely a good option. Lets be more sophisticated\nand choose ``rollback``:\n\n.. code-block:: shell-session\n \n > showroll\n Rollback actions:\n /tmp/aka_1ozr4w4b/File2 -> /home/uglemat/Documents/File2\n /tmp/aka_1ozr4w4b/File1 -> /home/uglemat/Documents/File1\n /tmp/aka_1ozr4w4b/File3 -> /home/uglemat/Documents/File3\n What should the program do?\n retry : try again (presumably you've fixed something in the meantime)\n rollback : attempt to undo changes (except for the ones previously continue'd)\n showroll : show which actions will be taken if you choose `rollback`\n exit : exit the program\n continue : ignore the error and move on\n > rollback\n Rollback renaming /tmp/aka_1ozr4w4b/File2 -> /home/uglemat/Documents/File2\n Rollback renaming /tmp/aka_1ozr4w4b/File1 -> /home/uglemat/Documents/File1\n Rollback renaming /tmp/aka_1ozr4w4b/File3 -> /home/uglemat/Documents/File3\n $ ls\n File1 File2 File3 File4\n\n\nRollback will \"undo\" all previous actions, in the reverse order that they were \"done'd\". If you use the ``--copy`` option then the undoing\nconsists of deleting files already copied. If any of the rollback actions fails, then ``aka`` will ignore it and try to undo as much as possible.\n\nInstalling\n----------\n\n``aka`` works only in Python 3. \n\nInstall with ``$ pip3 install aka``. You might want to replace ``pip3`` with ``pip``, depending on how your system is configured.\n\nWindows Compatability\n---------------------\n\nI developed this program on GNU/Linux, but it should be working on Windows as well. It understands that filenames are\ncase insensitive on Windows when checking for naming conflicts, yet the case sensitivity is preserved when the actual renames are done.\n\nDeveloping\n----------\n\nAka has some tests. Run ``$ nosetests`` or\n``$ python3 setup.py test`` to run the tests. The code is hosted at https://notabug.org/Uglemat/aka\n\nYou can install in development mode with ``$ python3 setup.py develop``, then your changes to aka will take effect immediately. Launch the same command with the ``--uninstall`` option to (kind of) remove.\n\nLicense\n-------\n\nThe code is licensed under the GNU General Public License 3 or later.\nThis README file is public domain.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://notabug.org/Uglemat/aka", "keywords": null, "license": "GPL3+", "maintainer": null, "maintainer_email": null, "name": "aka", "package_url": "https://pypi.org/project/aka/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/aka/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://notabug.org/Uglemat/aka" }, "release_url": "https://pypi.org/project/aka/1.0.3/", "requires_dist": null, "requires_python": null, "summary": "Rename/copy files using Python code", "version": "1.0.3" }, "last_serial": 1920678, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "5859d5cc76a530d477eec104ac2f2da5", "sha256": "f937f0b99f257324de2e6cbac2d4492f5b4b2de191af409752441ede9c53d5e0" }, "downloads": -1, "filename": "aka-1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "5859d5cc76a530d477eec104ac2f2da5", "packagetype": "bdist_wheel", "python_version": "3.4", "requires_python": null, "size": 47621, "upload_time": "2015-09-09T16:59:13", "url": "https://files.pythonhosted.org/packages/93/26/388800c648972bebe8dd1a21c241202cbc5a5851409b0633f6767572596b/aka-1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "1a1ab2df3d9b8ca653716018e34a17ea", "sha256": "7b16c11fc921eb7db12d11d1f598d97f375b36a8475a8a52fb5b617da76914b3" }, "downloads": -1, "filename": "aka-1.0.tar.gz", "has_sig": false, "md5_digest": "1a1ab2df3d9b8ca653716018e34a17ea", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12802, "upload_time": "2015-09-09T16:59:07", "url": "https://files.pythonhosted.org/packages/ee/4d/02d81a3ca65ace902563189b353ebc9f443d6499260a6d0d8d3aba79d231/aka-1.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "6898e50eeadaa243f36fab9cd9d47b2b", "sha256": "01d43f253d36a4d9a992579ce292ecb71369173c299d82634df33d42e0daf846" }, "downloads": -1, "filename": "aka-1.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "6898e50eeadaa243f36fab9cd9d47b2b", "packagetype": "bdist_wheel", "python_version": "3.4", "requires_python": null, "size": 47677, "upload_time": "2015-09-09T17:05:17", "url": "https://files.pythonhosted.org/packages/35/b6/2c979b5502b83d3d120323d859323a909b5d22dd114dd556f3ab1fec4492/aka-1.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "891e094fc57a976bd4a282409369c90b", "sha256": "6a995259799659ca4e8c9257d86526514091016cd27d3719cb2b4f239e9d3b43" }, "downloads": -1, "filename": "aka-1.0.1.tar.gz", "has_sig": false, "md5_digest": "891e094fc57a976bd4a282409369c90b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12799, "upload_time": "2015-09-09T17:05:00", "url": "https://files.pythonhosted.org/packages/71/cd/27c29170d6f430f2974db693528700a94e4a6b8df2ac4820b0891b6f551b/aka-1.0.1.tar.gz" } ], "1.0.2": [ { "comment_text": "", "digests": { "md5": "702e6bce5fef7ac3b01f347f3f5a74fa", "sha256": "b6d48d281b746244793df5b35fea109d04bdc94506ec835cda521d96f4e2efad" }, "downloads": -1, "filename": "aka-1.0.2-py3-none-any.whl", "has_sig": false, "md5_digest": "702e6bce5fef7ac3b01f347f3f5a74fa", "packagetype": "bdist_wheel", "python_version": "3.4", "requires_python": null, "size": 57221, "upload_time": "2015-09-10T16:44:44", "url": "https://files.pythonhosted.org/packages/17/97/b70a5dab87317663f251a53184b784fad224dd129c82f6567f70b4f6ac86/aka-1.0.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "1ba0a3a55bb976cc2ca421e35a961bbe", "sha256": "582a18fa0a6d2c70ba6ffb4c00bd218191835bf57bd5bc7c27d5d98128e9c25c" }, "downloads": -1, "filename": "aka-1.0.2.tar.gz", "has_sig": false, "md5_digest": "1ba0a3a55bb976cc2ca421e35a961bbe", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16513, "upload_time": "2015-09-10T16:44:39", "url": "https://files.pythonhosted.org/packages/43/bb/3dd29d0113e479eaac75db408296c26e381d791f9d0b1c9406d71aab212e/aka-1.0.2.tar.gz" } ], "1.0.3": [ { "comment_text": "", "digests": { "md5": "ce063a7fdea65c9ed868503dfc39c4fe", "sha256": "44db2414d3abdc07abbd4679e2dc42664dbe957faf3a5216df37c04bc80bc571" }, "downloads": -1, "filename": "aka-1.0.3.tar.gz", "has_sig": false, "md5_digest": "ce063a7fdea65c9ed868503dfc39c4fe", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16520, "upload_time": "2016-01-24T22:05:02", "url": "https://files.pythonhosted.org/packages/4a/04/8f71ebe4e7cddeffe92d4a3a4aac6438845f811300c46710bab2151cdbdf/aka-1.0.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "ce063a7fdea65c9ed868503dfc39c4fe", "sha256": "44db2414d3abdc07abbd4679e2dc42664dbe957faf3a5216df37c04bc80bc571" }, "downloads": -1, "filename": "aka-1.0.3.tar.gz", "has_sig": false, "md5_digest": "ce063a7fdea65c9ed868503dfc39c4fe", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16520, "upload_time": "2016-01-24T22:05:02", "url": "https://files.pythonhosted.org/packages/4a/04/8f71ebe4e7cddeffe92d4a3a4aac6438845f811300c46710bab2151cdbdf/aka-1.0.3.tar.gz" } ] }