{ "info": { "author": "Michael Dacre", "author_email": "mike.dacre@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: Science/Research", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: System :: Clustering", "Topic :: System :: Monitoring" ], "description": "####\nFyrd\n####\n\nOne liner script and function submission to torque or slurm clusters with\ndependency tracking using python. Uses the same syntax irrespective of cluster\nenvironment!\n\nLearn more at https://fyrd.science, https://fyrd.rtfd.com, and\nhttps://github.com/MikeDacre/fyrd\n\n.. image:: http://i.imgur.com/NNbprZH.png\n :alt: fyrd cluster logo \u2014 a Saxon shield remeniscent of those used in fyrds\n :target: https://fyrd.readthedocs.org\n :height: 200\n :width: 200\n\n+---------+----------------------------------------------------+\n| Author | Michael D Dacre |\n+---------+----------------------------------------------------+\n| License | MIT License, property of Stanford, use as you wish |\n+---------+----------------------------------------------------+\n| Version | 0.6.2b1 |\n+---------+----------------------------------------------------+\n\n\n.. image:: https://badge.buildkite.com/b6659b460caf5205919916c4e9d212c4e04d4301fa55a51180.svg?branch=master\n :target: https://buildkite.com/mikedacre/fyrd-cluster-tests\n.. image:: https://travis-ci.org/MikeDacre/fyrd.svg?branch=master\n :target: https://travis-ci.org/MikeDacre/fyrd\n.. image:: https://api.codacy.com/project/badge/Grade/c163cff81a1941a18b2c5455901695a3\n :target: https://www.codacy.com/app/mike-dacre/fyrd?utm_source=github.com&utm_medium=referral&utm_content=MikeDacre/fyrd&utm_campaign=Badge_Grade\n\n.. image:: https://readthedocs.org/projects/fyrd/badge/?version=latest\n :target: https://fyrd.readthedocs.io/\n\n.. image:: https://badge.fury.io/py/fyrd.svg\n :target: https://badge.fury.io/py/fyrd\n :alt: PyPI Version\n.. image:: https://img.shields.io/badge/python%20versions-2.7%203.4%203.5%203.6%203.7-brightgreen.svg\n :target: https://fyrd.science\n.. image:: https://requires.io/github/MikeDacre/fyrd/requirements.svg?branch=master\n :target: https://requires.io/github/MikeDacre/fyrd/requirements/?branch=master\n :alt: Requirements Status\n\n\nAllows simple job submission with *dependency tracking and queue waiting* on\neither torque, slurm, or locally with the multiprocessing module. It uses simple\ntechniques to avoid overwhelming the queue and to catch bugs on the fly.\n\nIt is routinely tested on Mac OS and Linux with slurm and torque clusters, or\nin the absence of a cluster, on Python versions ``2.7.10``, ``2.7.11``, ``2.7.12``,\n``3.3.0``, ``3.4.0``, ``3.5.2``, ``3.6.2``, and ``3.7-dev``. The full test suite is\navailable in the ``tests`` folder.\n\nFyrd is pronounced 'feared' (sort of), it is an Anglo-Saxon term for an army,\nparticularly an army of freemen (in this case an army of compute nodes). The\nlogo is based on a Saxon shield commonly used by these groups. This software\nwas formerly known as 'Python Cluster'.\n\nFor usage instructions and complete documentation see `the documentation site\n`_ and the `fyrd_manual.pdf\n`_ document\nin this repository.\n\n.. contents:: **Contents**\n\nOverview\n========\n\nThis library was created to make working with torque or slurm clusters as easy\nas working with the multiprocessing library. It aims to provide:\n\n- Easy submission of either python functions or shell scripts to torque or slurm\n from within python.\n- Simple dependency tracking for jobs.\n- The ability to submit jobs with any of the torque or slurm keyword arguments.\n- Easy customization.\n- Very simple usage that scales to complex applications.\n- A simple queue monitoring API that functions identically with torque and slurm\n queues.\n- A fallback local mode that allows code to run locally using the multiprocessing\n module without needing any changes to syntax.\n\nTo do this, all major torque and slurm keyword arguments are encoded in\ndictionaries in the ``fyrd/options.py`` file using synonyms so that all arguments\nare standardized on the fly. Job management is handled by the ``Job`` class in\n``fyrd/job.py``, which accepts any of the keyword arguments in the options file.\nTo make submission as simple as possible, the code makes used of profiles\ndefined in the ``~/.fyrd/profiles.txt`` config file. These allow simple grouping\nof keyword arguments into named profiles to make submission even easier.\nDependency tracking is handled by the ``depends=`` argument to ``Job``, which\naccepts job numbers or ``Job`` objects, either singularly or as lists.\n\nTo allow simple queue management and job waiting, a ``Queue`` class is\nimplemented in ``fyrd/queue.py``. It uses iterators, also defined in that file,\nto parse torque or slurm queues transparently and allow access to their\nattributes through the ``Queue`` class and the ``Queue.jobs`` dictionary. The ``Job``\nclass uses this system to block until the job completes when either the\n``wait()`` or ``get()`` methods are called.\n\nNote, waiting can email you when it is done, but you need to enable it in the\nconfig file (``~/.fyrd/config.txt``)::\n\n [notify]\n mode = linux # Can be linux or smtp, linux uses the mail command\n notify_address = your.address@gmail.com \n # The following are only needed for smtp mode\n smtp_host = smtp.gmail.com\n smtp_port = 587\n smtp_tls = True\n smtp_from = your.server@gmail.com\n smtp_user = None # Defaults to smtp_from\n # This is insecure, so use an application specific password. This should\n # be a read-only file with the SMTP password. After making it run:\n # chmod 400 ~/.fyrd/smtp_pass\n smtp_passfile = ~/.fyrd/smtp_pass\n\nTo allow similar functionality on a system not connected to a torque or slurm\nqueue, a local queue that behaves similarly, including allowing dependency\ntracking, is implemented in the ``fyrd/jobqueue.py`` file. It is based on\nmultiprocessing but behaves like torque. It is not a good idea to use this\nmodule in place of multiprocessing due to the dependency tracking overhead, it\nis primarily intended as a fallback, but it does work well enough to use\nindependently. **Note: the local mode currently is quite slow, as the overhead\nfor job management means that 100% of each available CPU is not used, only\naround 80% is. The local mode still works fine as a fallback or for testing\ncode, but it is important to remember that fyrd is meant primarily for large\ncluster use.**\n\nAs all clusters are different, common alterable parameters are defined in a\nconfig file located at ``~/.fyrd/config.txt``. This includes an option for max\nqueue size, which makes job submission block until the queue has opened up,\npreventing job submission failure on systems with queue limits (most clusters).\n\nTo make life easier, a bunch of simple wrapper functions are defined in\n``fyrd/basic.py`` that allow submission without having to worry about using the\nclass system, or to submit existing job files. Several helper function are also\ncreated in ``fyrd/helpers.py`` that allow the automation of more complex tasks,\nlike running ``apply`` on a pandas dataframe in parallel on the cluster\n(``fyrd.helpers.parapply()``).\n\nBasic Usage\n-----------\n\nThe end result is that submitting 10 thousand very small jobs to a small cluster\ncan be done like this:\n\n.. code:: python\n\n jobs = []\n for i in huge_list:\n jobs.append(fyrd.Job(my_function, (i,), profile='small').submit())\n results = fyrd.get(jobs)\n\nThe results list in this example will contain the function outputs, even if\nthose outputs are integers, objects, or other Python types. Similarly, shell\nscripts can be run like this:\n\n.. code:: python\n\n script = r\"\"\"zcat {} | grep \"#config\" | awk '{{split($1,a,\".\"); print a[2]\"\\t\"$2}}'\"\"\"\n jobs = []\n for i in [i for i in os.listdir('.') if i.endswith('.gz')]:\n jobs.append(fyrd.Job(script.format(i), profile='long').submit())\n results = fyrd.get(jobs)\n for i in results:\n print(i.stdout)\n\nResults will contain the contents of STDOUT for the submitted script\n\nHere is the same code with dependency tracking:\n\n.. code:: python\n\n script = r\"\"\"zcat {} | grep \"#config\" | awk '{{split($1,a,\".\"); print a[2]\"\\t\"$2}}'\"\"\"\n jobs = []\n jobs2 = []\n for i in [i for i in os.listdir('.') if i.endswith('.gz')]:\n j = fyrd.Job(script.format(i), profile='long').submit()\n jobs.append(j)\n jobs2.append(fyrd.Job(my_function, depends=j).submit())\n results = []\n for i in jobs2:\n i.wait()\n results.append(i.out)\n\nAs you can see, the ``profile`` keyword is not required, if not supplied the\ndefault profile is used. It is also important to note that ``.out`` will contain\nthe same contents as ``.stdout`` for all script submissions, but for function\nsubmissions, ``.out`` contains the function output, not STDOUT.\n\nNote, to submit simple functions, I recommend that you use the ``jobify``\ndecorator instead:\n\n.. code:: python\n\n >>> import fyrd\n >>> @fyrd.jobify(name='test_job', mem='1GB')\n ... def test(string, iterations=4):\n ... \"\"\"This does basically nothing!\"\"\"\n ... outstring = \"\"\n ... for i in range(iterations):\n ... outstring += \"Version {0}: {1}\".format(i, string)\n ... return outstring\n ... \n >>> test?\n Signature: test(*args, **kwargs)\n Docstring:\n This is a fyrd.job.Job decorated function.\n\n When you call it it will return a Job object from which you can get\n the results with the ``.get()`` method.\n\n Original Docstring:\n\n This does basically nothing!\n File: ~/code/fyrd/fyrd/helpers.py\n Type: function\n >>> j = test('hi')\n >>> j.get()\n 'Version 0: hiVersion 1: hiVersion 2: hiVersion 3: hi'\n\n\nCommand Line Tools\n------------------\n\nFyrd provides a few command line tools to make little jobs easier. The main\ntool is ``fyrd``. Running ``fyrd --help`` will give instructions on use, something\nlike this::\n\n usage: fyrd [-h] [-v] [-V]\n {run,submit,wait,queue,conf,prof,keywords,clean,local} ...\n\n Manage fyrd config, profiles, and queue.\n\n ============ ======================================\n Author Michael D Dacre \n Organization Stanford University\n License MIT License, use as you wish\n Version 0.6.2-beta1\n ============ ======================================\n\n positional arguments:\n {run,submit,wait,queue,conf,prof,keywords,clean,local}\n run (r) Run simple shell scripts\n submit (sub, s) Submit existing job files\n wait (w) Wait for jobs\n queue (q) Search the queue\n conf (config) View and manage the config\n prof (profile) Manage profiles\n keywords (keys, options)\n Print available keyword arguments.\n clean Clean up a job directory\n local (server) Manage the local queue server\n\n optional arguments:\n -h, --help show this help message and exit\n -v, --verbose Show debug outputs\n -V, --version Print version string\n\nThe keywords each have their own help menus and are fairly self-explanatory.\nThe ``conf`` and ``profile`` arguments allow you to edit the fyrd config and\ncluster profiles without having to directly edit the config files in the\n``~/.fyrd/`` directory.\n\nThe ``keywords`` argument is a help function only, it prints all possible keyword\narguments that can be used in cluster submissions.\n\n``queue`` allows you to query the queue in the same way that ``squeue`` or ``qstat``\nwould, with a few extra functions to make it easy to see only your jobs, or\nonly your running jobs.\n\nThere is another command line tool provided ``myqueue`` or ``myq`` (both are the\nsame), these tools are just wrappers for ``fyrd queue`` and they make it really\nfast to query a torque or slurm queue on any machine. e.g. ``myq -r`` will show\nyou all your currently running jobs, ``myq -r -c`` will display a count of all\ncurrently running jobs, and ``myq -r -l`` will dump a list of job numbers only to\nthe console, really useful when combined with ``xargs``, e.g. ``myq -r -l | xargs\nqdel``.\n\nThe ``wait`` command just blocks until the provided job numbers complete, and\ncan send you an email when it completes, see the config info above.\n\nAnd the ``clean`` command provides options to clean out a job directory that\ncontains leftover files from a fyrd session.\n\nInstallation\n-------------\n\nThis module will work with Python 2.7+ on Linux and Mac OS systems.\n\nThe betas are on PyPI, and can be installed directly from there:\n\n.. code:: shell\n\n pip install fyrd\n fyrd conf init\n\nTo install a specific tag from github, do the following:\n\n.. code:: shell\n\n pip install https://github.com/MikeDacre/fyrd/archive/v0.6.1b9.tar.gz\n fyrd conf init\n\nTo get the latest version:\n\n.. code:: shell\n\n pip install https://github.com/MikeDacre/fyrd/tarball/master\n fyrd conf init\n\nTo get the development version (still pretty stable):\n\n.. code:: shell\n\n pip install https://github.com/MikeDacre/fyrd/tarball/dev\n fyrd conf init\n \nThe ``fyrd conf init`` command initializes your environment interactively by\nasking questions about the local cluster system.\n\nI recommend installing using anaconda or pyenv, this will make your life much\nsimpler, but is not required.\n\nIn general you want either `pyenv `_ or user\nlevel install (``pip install --user``) even if you have ``sudo`` access, as most\ncluster environments share /home/ across the cluster, making this module\navailable everywhere. Anaconda will work if it is installed in a cross-cluster\ncapacity, usually as a module (with lmod, e.g. ``module load anaconda``). An\ninstall to the system python will usually fail as cluster nodes need to have\naccess to the module also.\n\nImporting is simple:\n\n.. code:: python\n\n import fyrd\n\nRequirements\n............\n\nThis software requires the following external modules:\n\n- `dill `_ \u2014 which makes function submission more stable\n- `tabulate `_ \u2014 allows readable printing of help\n- `six `_ \u2014 makes python2/3 cross-compatibility easier\n- `tblib `_ \u2014 allows me to pass Tracebacks between nodes\n- `tqdm `_ \u2014 pretty progress bars for multi-job get and wait\n- `sqlalchemy `_ \u2014 used in local mode\n to track jobs\n- `Pyro4 `_ \u2014 used in local mode to make a\n daemon\n\nCluster Dependencies\n....................\n\nIn order to submit functions to the cluster, this module must import them on the\ncompute node. This means that all of your python modules must be available on\nevery compute node.\n\nBy default, the same python executable used for submission is used on the\ncluster to run functions, however, this can be overridden by the\n'generic_python' option on the cluster. If using this option, you must install\nall of your local modules on the cluster also.\n\nTo avoid pain and debugging, you can do this manually by running this on your\nlogin node:\n\n.. code:: shell\n\n freeze --local | grep -v '^\\-e' | cut -d = -f 1 > module_list.txt\n\nAnd then on the compute nodes:\n\n.. code:: shell\n\n cat module_list.txt | xargs pip install --user\n\nAlternately, if your pyenv is available on the cluster nodes, then all of\nyour modules are already available, so you don't need to worry about this!\n\n\nTesting\n=======\n\nTo fully test this software, I use ``py.test`` tests written in the tests folder.\nUnfortunately, local queue tests do not work with ``py.test``, so I have separated\nthem out into the ``local_queue.py`` script. To run all tests, run ``python\ntests/run_tests.py``.\n\nTo ensure sensible testing always, I use `buildkite `_,\nwhich is an amazing piece of software. It integrates into this repository and\nruns tests on all python versions I support on my two clusters (a slurm cluster\nand a torque cluster) every day and on every push or pull request. I also use\n`travis ci `_ to run local queue tests, and\n`codacy `_ to monitor code style.\n\nAll code in the master branch must pass the travis-ci and buildkite tests, code\nin dev also *usually* passes those test, but it is not guaranteed. All other\nbranches are unstable and will often fail the tests.\n\nReleases\n========\n\nI use the following work-flow to release versions of fyrd:\n\n1. Develop new features and fix new bugs in a feature branch\n2. Write tests for the new feature\n3. When all tests are passing, merge into dev\n4. Do more extensive manual testing in dev, possibly add additional\n commits.\n5. Repeat the above for other related features and bugs\n6. When a related set of fixes and features are done and well tested,\n merge into master with a pull request through github, all travis and\n buildkite tests must pass for the merge to work.\n7. At some point after the new features are in master, add a new tagged\n beta release.\n8. After the beta is determined to be stable and all issues attached to\n that version milestone are resolved, create a non-beta tag\n\nNew releases are added when enough features and fixes have accumulated to\njustify it, new minor version are added only when there are very large changes\nin the code and are always tracked by milestones.\n\nWhile this project is still in its infancy, the API cannot be considered stable\nand the major version will remain 0. once version 1.0 is reached, any API\nchanges will result in a major version change.\n\nAs such, and non-beta release can be considered stable, beta releases and the\nmaster branch are very likely to be stable, dev is usually but not always\nstable, all other branches are very unstable.\n\nIssues and Contributing\n=======================\n\nIf you have any trouble with this software add an issue in\nhttps://github.com/MikeDacre/fyrd/issues\n\nFor peculiar technical questions or help getting the code installed, email\nme at `mike.dacre@gmail.com `_.\n\nI am always looking for help with this software, and I will gladly accept\npull requests. In particular, I am looking for help with:\n\n- Testing the code in different cluster environments\n- Expanding the list of keyword options\n- Adding new clusters other than torque and slurm\n- Implementing new features in the issues section\n\nIf you are interested in helping out with any of those things, or if you would\nbe willing to give me access to your cluster to allow me to run tests and port\nfyrd to your environment, please contact me.\n\nIf you are planning on contributing and submitting a pull request, please\nfollow these rules:\n\n- Follow the code style as closely as possible, I am a little obsessive about\n that\n- If you add new functions or features:\n - Add some tests to the test suite that fully test your new feature\n - Add notes to the documentation on what your feature does and how it works\n- Make sure your code passes the full test suite, which means you need to run\n ``python tests/run_tests.py`` from the root of the repository at a bare\n minimum. Ideally, you will install pyenv and run ``bash tests/pyenv_tests.py``\n- Squash all of your commits into a single commit with a well written and\n informative commit message.\n- Send me a pull request to either the ``dev`` or ``master`` branches.\n\nIt may take a few days for me to fully review your pull request, as I will test\nit extensively. If it is a big new feature implementation I may request that\nyou send the pull request to the ``dev`` branch instead of to ``master``.\n\nWhy the Name?\n=============\n\nI gave this project the name 'Fyrd' in honour of my grandmother, H\u00e9l\u00e8ne\nSandolphen, who was a scholar of old English. It is the old Anglo-Saxon word\nfor 'army', and this code gives you an army of workers on any machine so it\nseemed appropriate.\n\nThe project used to be called \"Python Cluster\", which is more descriptive but\nfrankly boring. Also, about half a dozen other projects have almost the same\nname, so it made no sense to keep that name and put the project onto PyPI.\n\n\nDocumentation\n=============\n\nThis software is much more powerful that this document gives it credit for,\nto get the most out of it, read the docs at https://fyrd.readthedocs.org\nor get the PDF version from the file in\n`docs/fyrd_manual.pdf `_.\n", "description_content_type": null, "docs_url": null, "download_url": "https://github.com/MikeDacre/fyrd/archive/v0.6.2b1.tar.gz", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://fyrd.science", "keywords": "slurm torque multiprocessing cluster job_management", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "fyrd", "package_url": "https://pypi.org/project/fyrd/", "platform": "", "project_url": "https://pypi.org/project/fyrd/", "project_urls": { "Download": "https://github.com/MikeDacre/fyrd/archive/v0.6.2b1.tar.gz", "Homepage": "https://fyrd.science" }, "release_url": "https://pypi.org/project/fyrd/0.6.2b1/", "requires_dist": null, "requires_python": "", "summary": "Submit functions and shell scripts to torque, slurm, or local machines", "version": "0.6.2b1" }, "last_serial": 3646030, "releases": { "0.6.1b8": [ { "comment_text": "", "digests": { "md5": "4dee3b268f41d5ee365e310048793547", "sha256": "ecc10edc7c25b8ecdbd5b0febee6e761af7e1c6015759c2800dc776f8ec8e2c4" }, "downloads": -1, "filename": "fyrd-0.6.1b8-py2.py3-none-any.whl", "has_sig": true, "md5_digest": "4dee3b268f41d5ee365e310048793547", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 90440, "upload_time": "2017-08-03T06:46:43", "url": "https://files.pythonhosted.org/packages/14/d2/8fb7cb1139b92e690d3e6c29756eb900d111682213d9c9992634ec9b7fe3/fyrd-0.6.1b8-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "06544890c1bf63b7aa10247786fc3840", "sha256": "13215ad1be1542ee6ae5caa5c3bb6c907d4b053b61f9b54651a69b6a03a7eadb" }, "downloads": -1, "filename": "fyrd-0.6.1b8.tar.gz", "has_sig": true, "md5_digest": "06544890c1bf63b7aa10247786fc3840", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 76757, "upload_time": "2017-08-03T15:32:20", "url": "https://files.pythonhosted.org/packages/05/e7/a5161147aa06a8bf4cb012bc2a915e3dcf822fd3fe8a9d267b8105fe5702/fyrd-0.6.1b8.tar.gz" } ], "0.6.1b9": [ { "comment_text": "", "digests": { "md5": "e6f1f7a83b3fe3cad6fa4fca9f7c0215", "sha256": "25428f863781fad25e496f1321b84412b3310f83c0f37a7c633f4978f1f7decb" }, "downloads": -1, "filename": "fyrd-0.6.1b9-py2.py3-none-any.whl", "has_sig": true, "md5_digest": "e6f1f7a83b3fe3cad6fa4fca9f7c0215", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 92912, "upload_time": "2017-08-03T22:36:40", "url": "https://files.pythonhosted.org/packages/3d/7b/458d213778acc2db4f3cb0e7822ef2987c0031ed2970d8e131288dad7d17/fyrd-0.6.1b9-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "bc70d8cdc8480690e0908a02350f4000", "sha256": "4ab267efd453e542d0a20bc868bbc8519afef5cdf5c0182b7f0ebc2298bf6cdc" }, "downloads": -1, "filename": "fyrd-0.6.1b9.tar.gz", "has_sig": true, "md5_digest": "bc70d8cdc8480690e0908a02350f4000", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 85916, "upload_time": "2017-08-03T22:36:41", "url": "https://files.pythonhosted.org/packages/72/80/4710525565637852602cccb508eb943ede45df01cf14fb2b81150a07903e/fyrd-0.6.1b9.tar.gz" } ], "0.6.2b1": [ { "comment_text": "", "digests": { "md5": "b9b4175d84605b930d704400770ff6aa", "sha256": "165722cc04a53a4a256d7cdec46b357a49d279469265b1c3419c0585b627cc1b" }, "downloads": -1, "filename": "fyrd-0.6.2b1.tar.gz", "has_sig": true, "md5_digest": "b9b4175d84605b930d704400770ff6aa", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 127660, "upload_time": "2018-03-07T00:20:54", "url": "https://files.pythonhosted.org/packages/e4/9e/d2432b12b236339e5395a4a8eecec1dd3a94dc849aa46b6cb8ed95420cf4/fyrd-0.6.2b1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b9b4175d84605b930d704400770ff6aa", "sha256": "165722cc04a53a4a256d7cdec46b357a49d279469265b1c3419c0585b627cc1b" }, "downloads": -1, "filename": "fyrd-0.6.2b1.tar.gz", "has_sig": true, "md5_digest": "b9b4175d84605b930d704400770ff6aa", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 127660, "upload_time": "2018-03-07T00:20:54", "url": "https://files.pythonhosted.org/packages/e4/9e/d2432b12b236339e5395a4a8eecec1dd3a94dc849aa46b6cb8ed95420cf4/fyrd-0.6.2b1.tar.gz" } ] }