{ "info": { "author": "Branko Vukelic", "author_email": "branko@brankovukelic.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 2 - Pre-Alpha", "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License" ], "description": "====\nzoro\n====\n\nBefore we introduce you to zoro, please do not confuse it with Zorro_ which is\na networking library that has nothing to do with this one.\n\nThe name 'zoro' comes from a Japanese onomatopoeic phrase 'zoro zoro' which\nsignifies a sound a horde makes as it goes somewhere.\n\nHaving to deal with a build process that involves both backend and frontend\ncode, transpiled languages like CoffeScript, LiveScript, LESS or Compass,\nexotic configuration files or settings that need to be swapped before going\ninto production... The horde of tasks that need to be completed before a\nproject can be served to the masses can be hard to deal with without software\nto help us. On the other hand, learning a complex build system may seem like a\nchore.\n\nZoro tries to fill the gap between writing small (shell) scripts, and mastering\na full build system like Buildout. I chose Python for this task, not only\nbecause I develop Python software, but also because of its vast standard\nlibrary that can simplify many tasks without pulling in dozens of third-party\nlibraries. Zoro is also a simple and pure Python module, so you do not need\nanything other than the Python interpreter installed on your system. This makes\nit not only easy to install, but also portable across platforms.\n\nIn fact, the ``zoro`` module itself does not hide any of the modules and\nfunctions it imports from the standard library, so you can ``from zoro import\n*`` to access them witout having to add many lines of imports. Further more,\nthough its API, zoro tries to stay as close to bare Python as possible. After\nall, why invent a new language if there is already a good one (or ten).\n\n.. contents::\n\nLicense\n=======\n\nZoro is released under MIT license. See the source code for copyright and\nlicense.\n\nInstallation\n============\n\nYou can install zoro frim PyPI as usual::\n\n easy_install zoro\n\nor::\n\n pip install zoro\n\nBasic concept\n=============\n\nSomewhat similar to GNU Make, zoro allows you to easily define build targets,\nand run various commands within them. This is achieved through the use of\n``zoro.Make`` class. Let's take a look at a real-life example of such a class\nand discuss its usage. ::\n\n #!/usr/bin/env python\n\n from zoro import *\n\n\n class Targets(Make):\n \"\"\"\n Build my project.\n \"\"\"\n\n def __init__(self):\n super(Targets, self).__init__()\n self._parser.add_options('-k', '--key', help='API key',\n dest='api_key')\n\n def dev(self):\n \"\"\" start test runners, servers and compilers \"\"\"\n wait_for_interrupt(\n run(node_local('lsc -cwbo static/js src/ls')),\n run('compass watch -c tools/compass.rb'),\n run(python('app/main'), env={'APPENV': 'development'}),\n )\n\n def test(self, args, options):\n \"\"\" run unit tests \"\"\"\n wait_for_interrupt(\n watch(python('tools/run_tests'), '.', '*.py'),\n )\n\n def build(self):\n \"\"\" prepares the project for deployment \"\"\"\n self.clean()\n copytree('app', 'build/app')\n copytree('static', 'build/static')\n patch('build/app/conf.py', lambda s: self._patch_key(s))\n cleanup('build', '*.pyc')\n run(python('tools/cachebust build/app/templates'), wait=True)\n\n def _patch_key(self, s):\n key = self._options.k.api_key\n if key is None:\n err('No API key specified', 1)\n return re.sub(r\"API_KEY='[^']+'\", \"API_KEY='%s'\" % key, s)\n\n\n if __name__ == '__main__':\n Targets()()\n\n\nThis file is usually saved as 'zorofile' in the project's root directory. The\nshebang line at the top of the file allows us to run this file without\nexplicitly naming the interpreter (on Linux and UNIX systems at least). On\nWindows we also include a 'zorofile.cmd' file to go with it. The contents of\nthe file may look like this::\n\n @echo off\n python zorofile %*\n\nNow we can start calling the zorofile directly.\n\nImporting zoro functions\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nNormally, when using zoro, we import everything from ``zoro`` module with::\n\n from zoro import *\n\nThis pulls in not only the functions and classes defined by zoro itself, but\nalso anything and evertyhing zoro itself imports. This includes (among other\nthings, the ``os`` module, ``sys``, ``time``, ``platform``, ``shlex``,\n``datetime``, etc). For a full listing of what's imported, you should look at\nthe source code.\n\nTargets class\n~~~~~~~~~~~~~\n\nThe next thing we notice is the ``Targets`` class. It's a subclass of the\n``zoro.Make`` class, and we use it to house all our build targets, as well as\nany utility methods we might need.\n\nThe constructor\n~~~~~~~~~~~~~~~\n\nThe constructor of the ``zoro.Make`` class builds a parser object (created by\n``optparse`` module). The parser is used to define and parse command line\narguments passed to our zorofile. In our subclass, we augment the default\nparser with a new option '-k', which we will use to pass a production API key\nduring the build process.\n\nParsed positional arguments and options are stored as ``_args`` and\n``_options`` instance attributes respectively and can be access by all instance\nmethods.\n\nTargets and utilities\n~~~~~~~~~~~~~~~~~~~~~\n\nLet's cover the utility methods first. In our example, we have one utility\nmethod which replaces the API key in our configuration module. The reason we\nmade it an instance method instead of a function defined outside the class is\nthat this way we have access to all properties on the class, including the\n``_options`` attribute mentioned in the previous section.\n\nThe reason utility methods are prefixed with an underscore is that methods\nwithout a leading underscore will be treated as build targets.\n\nYou will also note that we are using the ``re`` module without explicitly\nimporting it. We can do that because it is already imported in the ``zoro``\nmodule.\n\nApart from the constructor and the utility method, there are also three build\ntargets: 'dev', 'test', and 'build'. All three targets are normal Python\nmethods. They have docstrings of which the first lines are used in help message\nwhen the zorofile is run with the '-l' switch.\n\nThe 'dev' target is used when we want to develop the application. It\nfacilitates live compilation of LiveScript_ and Compass_ code and runs our\napplication's built-in development server. This is achieved by using the\n``zoro.run()`` function.\n\nThe ``zoro.run()`` function executes commands asyncrhonously by default. This\nmeans that the function itself returns before the command exits. This is\nconvenient because the commands in the 'dev' target will run indefinitely until\nthey receive a keyboard interrupt.\n\nThe first command is passed to ``zoro.node_local()`` function. This function\nconstructs the correct path for the locally installed NodeJS_ dependencies. The\nactual command to run is dependent on the platform we are on, and this function\nalso takes care of ironing out the differences.\n\nThird command is a python script, so we are passing it to ``zoro.python()``\nfunction, which prepends 'python' and appends the '.py' extension. You will\nalso notice that the third command uses an ``env`` keyword argument to the\n``zoro.run()`` function. This allows us to override or add envionrment \nvariables specifically for that command.\n\nAll three commands in the 'dev' target are wrapped in\n``zoro.wait_for_interrupt()`` call. This function takes child process objects\nor watchdog_ observers as positional arguments, and terminates them all when\nthe zorofile receives a keyboard interrupt. Because ``zoro.run()`` returns a\nchild process object for the command it executes, we can pass its return value\ndirectly to ``zoro.wait_for_interrupt()``.\n\nThe second target, 'test', looks very similar to the 'dev' target, but it runs\nits command using ``zoro.watch()`` instead of ``zoro.run()``. The\n``zoro.watch()`` function takes three arguments. The first one is the same as\n``zoro.run()``. The second argument is a path that should be monitored for\nchanges and the last argument is a glob pattern to use as a filter. Whenever a\nfile or directory under the monitored path, matching the specified glob\npattern, is modified, the command is executed. This allows us to rerun our\ntests whenever we modify a Python module.\n\nFinally, the 'build' target creates a 'build' directory and prepares the code\nfor deployment. It uses the ``shutil.copytree()`` function to copy the\ndirectories into the target directory, calls ``zoro.patch()`` to patch the\nconfiguration file with the help from the utility method, and uses\n``zoro.cleanup()`` to remove unneeded files.\n\nRunning the targets\n~~~~~~~~~~~~~~~~~~~\n\nTo run the targets, we need to call the instance of our ``Targets`` class. This\nis done in an ``if`` block so that it is only run when the zorofile is called\ndirectly.\n\nAPI documentation\n=================\n\nThere is no separate API documentation, but you will find the source code to be\nwell-documented. The code is less than 700 lines *with* inline documentation,\nso you should just dig in. You will find examples for each function in the\ndocstrings.\n\nReporting bugs\n==============\n\nPlease report any bugs to the project's `BitBucket bugtracker`_.\n\n.. _Zorro: https://pypi.python.org/pypi/Zorro\n.. _LiveScript: http://livescript.net/\n.. _Compass: http://compass-style.org/\n.. _watchdog: http://pythonhosted.org//watchdog/\n.. _BitBucket bugtracker: https://bitbucket.org/brankovukelic/zoro/issues", "description_content_type": null, "docs_url": null, "download_url": "https://bitbucket.org/brankovukelic/zoro/downloads", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://bitbucket.org/brankovukelic/zoro/", "keywords": null, "license": "MIT", "maintainer": null, "maintainer_email": null, "name": "zoro", "package_url": "https://pypi.org/project/zoro/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/zoro/", "project_urls": { "Download": "https://bitbucket.org/brankovukelic/zoro/downloads", "Homepage": "https://bitbucket.org/brankovukelic/zoro/" }, "release_url": "https://pypi.org/project/zoro/1.14/", "requires_dist": null, "requires_python": null, "summary": "Build tool written in Python, works with anything", "version": "1.14" }, "last_serial": 1081616, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "7e55b5e53336023e5f655a8633eb1ce8", "sha256": "09c2798a1a6af09108db116def497fdcc3c25c1e60296639d6e247906c9d4082" }, "downloads": -1, "filename": "zoro-1.0.tar.gz", "has_sig": false, "md5_digest": "7e55b5e53336023e5f655a8633eb1ce8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11556, "upload_time": "2014-02-26T13:19:55", "url": "https://files.pythonhosted.org/packages/bc/05/711197e4f2bd3258c275d6c857bce8f10aee2998149bd4165ec95046a575/zoro-1.0.tar.gz" }, { "comment_text": "", "digests": { "md5": "4bfcbbb23d31598c989c492f5966c471", "sha256": "066bc1635e73e138bee6816d4ada7d613a28876f25f9b93dbe841438d42159d2" }, "downloads": -1, "filename": "zoro-1.0.zip", "has_sig": false, "md5_digest": "4bfcbbb23d31598c989c492f5966c471", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12350, "upload_time": "2014-02-26T13:19:34", "url": "https://files.pythonhosted.org/packages/63/22/ee455009bc112d44e1a2167c46666716015951fbf6418e499cda8b3e9d92/zoro-1.0.zip" } ], "1.1": [ { "comment_text": "", "digests": { "md5": "6999767c1f9118aeb6ef1138fdaeb5d9", "sha256": "592a8ef78f9517345763eacf10c1f5e3422670d1c7dd4493f86aa02e900a2fac" }, "downloads": -1, "filename": "zoro-1.1.tar.gz", "has_sig": false, "md5_digest": "6999767c1f9118aeb6ef1138fdaeb5d9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12359, "upload_time": "2014-02-26T14:46:37", "url": "https://files.pythonhosted.org/packages/ad/6f/066a0721e9b1c9a62d022c396f896c0cbc51fccc8f1f853ef96d3104acde/zoro-1.1.tar.gz" }, { "comment_text": "", "digests": { "md5": "554b84db699cc1c3dfa0792144d07e08", "sha256": "00756f338e5e452536adbda4e80dd015f152d8c434a4e7b03b1b487210f87538" }, "downloads": -1, "filename": "zoro-1.1.zip", "has_sig": false, "md5_digest": "554b84db699cc1c3dfa0792144d07e08", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16486, "upload_time": "2014-02-26T14:46:34", "url": "https://files.pythonhosted.org/packages/a7/e2/4a10ce4ea1856ce40fbe6f0c1d85fc65d3471cd9916a42ad1540c031c794/zoro-1.1.zip" } ], "1.11": [ { "comment_text": "", "digests": { "md5": "5715825b5da1e75872ef4a2013e8d707", "sha256": "84e11db52dd67e5e7150470aca8df92ead949cc2b816a28d62f31be4b100133c" }, "downloads": -1, "filename": "zoro-1.11.tar.gz", "has_sig": false, "md5_digest": "5715825b5da1e75872ef4a2013e8d707", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12384, "upload_time": "2014-02-26T14:50:10", "url": "https://files.pythonhosted.org/packages/a8/65/0c12f9a7b3d85e0da0c39cc0a38cf860a8afda3b4474da7beb34725bc71a/zoro-1.11.tar.gz" }, { "comment_text": "", "digests": { "md5": "67a372852f6eb36f1a80864c617ae72d", "sha256": "145f600fd9ec99f764151ba71195cbde7ebb2d6c5f77906f7a87fa5ed8ed7ec4" }, "downloads": -1, "filename": "zoro-1.11.zip", "has_sig": false, "md5_digest": "67a372852f6eb36f1a80864c617ae72d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16529, "upload_time": "2014-02-26T14:50:07", "url": "https://files.pythonhosted.org/packages/a4/85/77f259486a5a23a30d8592edbb3e0c0ec543660306be64e0afdbf1f7438a/zoro-1.11.zip" } ], "1.12": [ { "comment_text": "", "digests": { "md5": "d44e9db142c2ab40f98b915be5ed0647", "sha256": "fc109e6bc5b8a090a4de0299a53c8aac4b7f6dd8e6f9fd7158883b2dec25888c" }, "downloads": -1, "filename": "zoro-1.12.tar.gz", "has_sig": false, "md5_digest": "d44e9db142c2ab40f98b915be5ed0647", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12538, "upload_time": "2014-03-07T09:26:26", "url": "https://files.pythonhosted.org/packages/d9/cc/028bc6049553564e6c91b3d8a1b6888e7ece5e67af8b5575427e38fa457a/zoro-1.12.tar.gz" }, { "comment_text": "", "digests": { "md5": "4f59f251436ff04ae5105750b719eab1", "sha256": "a7f2a1ac7b023fe0623cfea9fbe9b3a836dd538708179e83935df963bd7fa868" }, "downloads": -1, "filename": "zoro-1.12.zip", "has_sig": false, "md5_digest": "4f59f251436ff04ae5105750b719eab1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16688, "upload_time": "2014-03-07T09:26:20", "url": "https://files.pythonhosted.org/packages/8c/b9/62243fbffe1d69bba7bcb74951866062b2b441bd2eca5c81f8cce064fc2d/zoro-1.12.zip" } ], "1.13": [ { "comment_text": "", "digests": { "md5": "54debcdbac6ab0f5a7c07a37cc43b616", "sha256": "9b222b9241bb3e79adc30c395bbbb1ff071afff39a41b5f68c75f5128779be44" }, "downloads": -1, "filename": "zoro-1.13.tar.gz", "has_sig": false, "md5_digest": "54debcdbac6ab0f5a7c07a37cc43b616", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12554, "upload_time": "2014-03-07T09:37:00", "url": "https://files.pythonhosted.org/packages/0a/9e/525870653b9e7321e4841496cac66b8422f016b1df72344cdb68d034dae3/zoro-1.13.tar.gz" }, { "comment_text": "", "digests": { "md5": "6a782a9cb6286da235448f5a119f34d0", "sha256": "aed13ddab36ee7004dad69f7d375cfe4c2f791fa0d91bcd806c1c400172df615" }, "downloads": -1, "filename": "zoro-1.13.zip", "has_sig": false, "md5_digest": "6a782a9cb6286da235448f5a119f34d0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16707, "upload_time": "2014-03-07T09:36:53", "url": "https://files.pythonhosted.org/packages/8f/e3/8e7f7a8c82d06021116f2e7f29cd7cbec6bfd2a151f9eb15a5927f5b9622/zoro-1.13.zip" } ], "1.14": [ { "comment_text": "", "digests": { "md5": "fc80e2d2a6f372d6d1865af05771a206", "sha256": "52483148e7ae269d74496a34d1075037185ea28f4ae82adaeb5bf72f10d37494" }, "downloads": -1, "filename": "zoro-1.14.tar.gz", "has_sig": false, "md5_digest": "fc80e2d2a6f372d6d1865af05771a206", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12711, "upload_time": "2014-05-05T17:17:53", "url": "https://files.pythonhosted.org/packages/ea/f9/0a097d8a48ac7ab90a6d6a4ab7045d66c182b3a5ca60468224c83098f123/zoro-1.14.tar.gz" }, { "comment_text": "", "digests": { "md5": "164542373026e6cee912364c90b5b6de", "sha256": "7888ff2953d1f01f610f9dd371e661c64f77f2818af62113e63c74b4183778ec" }, "downloads": -1, "filename": "zoro-1.14.zip", "has_sig": false, "md5_digest": "164542373026e6cee912364c90b5b6de", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16814, "upload_time": "2014-05-05T17:17:49", "url": "https://files.pythonhosted.org/packages/25/36/028f7c0096be378bed9e64ea5ef7956dfa462fdb51339fb43a1015aecab9/zoro-1.14.zip" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "fc80e2d2a6f372d6d1865af05771a206", "sha256": "52483148e7ae269d74496a34d1075037185ea28f4ae82adaeb5bf72f10d37494" }, "downloads": -1, "filename": "zoro-1.14.tar.gz", "has_sig": false, "md5_digest": "fc80e2d2a6f372d6d1865af05771a206", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12711, "upload_time": "2014-05-05T17:17:53", "url": "https://files.pythonhosted.org/packages/ea/f9/0a097d8a48ac7ab90a6d6a4ab7045d66c182b3a5ca60468224c83098f123/zoro-1.14.tar.gz" }, { "comment_text": "", "digests": { "md5": "164542373026e6cee912364c90b5b6de", "sha256": "7888ff2953d1f01f610f9dd371e661c64f77f2818af62113e63c74b4183778ec" }, "downloads": -1, "filename": "zoro-1.14.zip", "has_sig": false, "md5_digest": "164542373026e6cee912364c90b5b6de", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16814, "upload_time": "2014-05-05T17:17:49", "url": "https://files.pythonhosted.org/packages/25/36/028f7c0096be378bed9e64ea5ef7956dfa462fdb51339fb43a1015aecab9/zoro-1.14.zip" } ] }