{
"info": {
"author": "Joe Alcorn",
"author_email": "joealcorn123@gmail.com",
"bugtrack_url": null,
"classifiers": [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.1",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Topic :: Software Development :: Testing",
"Topic :: Utilities"
],
"description": "Laboratory!\n===========\n\n.. image:: https://img.shields.io/travis/joealcorn/laboratory.svg?\n :target: https://travis-ci.org/joealcorn/laboratory\n\n.. image:: https://readthedocs.org/projects/laboratory-python/badge/?version=latest\n :target: https://laboratory-python.readthedocs.io/en/latest/\n\n.. image:: https://img.shields.io/github/license/joealcorn/laboratory.svg?\n :target: https://opensource.org/licenses/mit-license.php\n\n.. image:: https://img.shields.io/pypi/v/laboratory.svg?\n :target: https://pypi.python.org/pypi/laboratory\n\n.. image:: https://pypi-badges.global.ssl.fastly.net/svg?package=laboratory&timeframe=monthly\n :target: https://pypi.joealcorn.co.uk\n\n\nA Python library for carefully refactoring critical paths by testing in\nproduction (inspired by `GitHub's Scientist`_) with support for Python 2.7, 3.3+\n\n.. _GitHub's Scientist: https://github.com/github/scientist\n\n\n#. `Why?`_\n#. `Installation`_\n#. `Getting started`_\n#. `Adding context`_\n#. `Ramping up`_\n#. `Controlling comparison`_\n#. `Raise on mismatch`_\n#. `Publishing results`_\n#. `Caveats`_\n#. `Links`_\n\n.. _Why?:\n\nWhy?\n----\n\nSome blocks of code are more critical than the rest. Laboratory helps us refactor important\ncode paths by running experiments in production and verifying the results.\n\nThe value lies in its ability to give us a sense of confidence where there was none before.\nThrough experimentation we immediately see if candidate code is misbehaving, and at the same\ntime we establish a feedback loop that we can use to converge on correctness more quickly.\n\nI've written a blog post if you'd like to know more: `Sure-footed refactoring`_.\nThe original blog post that inspired this project is worth a read too: `Scientist`_.\n\n.. _Sure-footed refactoring: https://joealcorn.co.uk/blog/2018/sure-footed-refactoring\n.. _Scientist: https://githubengineering.com/scientist/\n\n\nInstallation\n------------\n\nInstalling from pypi is recommended\n\n.. code::\n\n $ pip install laboratory\n\nYou can also install a `tagged version`_ from Github\n\n.. code::\n\n $ pip install https://github.com/joealcorn/laboratory/archive/v1.0.tar.gz\n\nOr the latest development version\n\n.. code::\n\n $ pip install git+https://github.com/joealcorn/laboratory.git\n\n\n.. _tagged version: https://github.com/joealcorn/laboratory/releases\n\n.. _Getting started:\n\nGetting started\n---------------\n\nSee: `Installation`_ or ``pip install laboratory``\n\nWith Laboratory you conduct an experiment with your known-good code as the\ncontrol block and a new code branch as a candidate. Laboratory will:\n\n- Execute the new and the old code in a randomised order\n- Compare the return values\n- Record timing information about old & new code\n- Catch (but record!) exceptions in the new code\n- Publish all of this information\n\nLet's imagine you're refactoring some authorisation code. Your existing code\nis working, but it's a fragile pile of spaghetti that is becoming hard to\nmaintain. You want to refactor, but this is important code and you simply can't\nafford to get this wrong or else you risk exposing user data.\nConsidering the state of the original code, this could be difficult to pull off,\nbut Laboratory is here to help.\n\nLaboratory helps us verify the correctness of our implementation even with the\ncornucopia of factors that make production a unique environment (bad or legacy\ndata, heavy load, etc.)\n\nLet's set up an experiment to run our old (control) and new (candidate) code:\n\n.. code:: python\n\n import laboratory\n\n # set up the experiment and define control and candidate functions\n experiment = laboratory.Experiment()\n experiment.control(authorise_control, args=[user], kwargs={'action': action})\n experiment.candidate(authorise_candidate, args=[user], kwargs={'action': action})\n\n # conduct the experiment and return the control value\n authorised = experiment.conduct()\n\n\nNote that the ``Experiment`` class can also be used as a decorator if the\ncontrol and candidate functions take the same arguments.\n\n.. code:: python\n\n def authorise_candidate(user, action=None):\n return True\n\n @Experiment.decorator(candidate=authorise_candidate)\n def authorise_control(user, action=None):\n return True\n\n\nAn experiment will always return the value of the control block.\n\n\nAdding context\n--------------\n\nA lot of the time there's going to be extra context around an experiment that's\nuseful to use in publishing or when verifying results. There are a couple ways\nto set this.\n\n.. code:: python\n\n # The first is experiment-wide context, which will be set on every Observation an experiment makes\n experiment = laboratory.Experiment(name='Authorisation experiment', context={'action': action})\n\n # Context can also be set on an Observation-specific basis\n experiment.control(control_func, context={'strategy': 1})\n experiment.candidate(cand_func, context={'strategy': 2})\n\nContext can be retrieved using the ``get_context`` method on ``Experiment`` and ``Observation`` instances.\n\n.. code:: python\n\n class Experiment(laboratory.Experiment):\n def publish(self, result):\n self.get_context()\n result.control.get_context()\n result.candidates[0].get_context()\n\n\nRamping up\n----------\n\nBefore running a candidate code block Laboratory will call ``Experiment.enabled``.\nBy overriding this method we can control when the candidate code will be executed.\n\nFor example, if we wanted to enable the experiment for just 10% of calls, we could\ndo something along these lines:\n\n.. code:: python\n\n class MyExperiment(laboratory.Experiment):\n def enabled(self):\n return random.random() < 0.1\n\nThis is useful for slowly ramping up the experiment, but because we have access to\nthe experiment context in the ``enabled`` method, we're also able to do fancier\nthings like enabling only for specific users.\n\n.. code:: python\n\n class MyExperiment(laboratory.Experiment):\n def enabled(self):\n ctx = self.get_context()\n return ctx['user'] in user_segment\n\n\nControlling comparison\n----------------------\n\nNot all data is created equal. By default laboratory compares using ``==``, but\nsometimes you may need to tweak this to suit your needs. It's easy enough |--|\nsubclass ``Experiment`` and implement the ``compare(control, observation)`` method.\n\n.. code:: python\n\n class MyExperiment(Experiment):\n def compare(self, control, observation):\n return control.value['id'] == observation.value['id']\n\n\nRaise on mismatch\n*****************\n\nThe ``Experiment`` class accepts a ``raise_on_mismatch`` argument which you can set\nto ``True`` if you want Laboratory to raise an exception when the comparison returns\nfalse. This may be useful in testing, for example.\n\n\nPublishing results\n------------------\n\nThis data is useless unless we can do something with it. Laboratory makes no\nassumptions about how to do this |--| it's entirely for you to implement to suit\nyour needs. For example, timing data can be sent to graphite, and mismatches\ncan be placed in a capped collection in redis for debugging later.\n\nThe publish method is passed a ``Result`` instance, with control and candidate\ndata is available in ``Result.control`` and ``Result.candidates``\nrespectively.\n\n.. code:: python\n\n class MyExperiment(laboratory.Experiment):\n def publish(self, result):\n statsd.timing('MyExperiment.control', result.control.duration)\n for o in result.candidates:\n statsd.timing('MyExperiment.%s' % o.name, o.duration)\n\n\nCaveats\n-------\n\nBecause of the way Laboratory works, there are some situations in which it should\nnot be used. Namely, any code with side effects, such as disk or database writes,\nor other state changes, are unsuitable as they'll lead to duplicated writes.\nYou could end up with buggy data or a candidate that affects the execution of\nthe control.\n\nYou\u2019ll also take a performance hit by running your new code in addition to the old,\nso be mindful of that. You should ramp an experiment up slowly and keep an eye on\nyour metrics.\n\n\nLinks\n-----\n\n- `Documentation `_\n- `Source code `_\n- `CI server `_\n- `Python Package Index `_\n- `Sure-footed refactoring `_\n\n\nMaintenance\n-----------\n\nLaboratory is actively maintained by Joe Alcorn (`Github `_, `Twitter `_)\n\n\n.. |--| unicode:: U+2014 .. em dash\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/joealcorn/laboratory",
"keywords": "",
"license": "MIT",
"maintainer": "",
"maintainer_email": "",
"name": "laboratory",
"package_url": "https://pypi.org/project/laboratory/",
"platform": "",
"project_url": "https://pypi.org/project/laboratory/",
"project_urls": {
"Homepage": "https://github.com/joealcorn/laboratory"
},
"release_url": "https://pypi.org/project/laboratory/1.0.2/",
"requires_dist": [
"Sphinx (==1.6.6) ; extra == 'dev'",
"alabaster (==0.7.12) ; extra == 'dev'"
],
"requires_python": "",
"summary": "Sure-footed refactoring achieved through experimenting",
"version": "1.0.2"
},
"last_serial": 5228671,
"releases": {
"0": [],
"0.1.0": [
{
"comment_text": "",
"digests": {
"md5": "1d0e346a79bc31d87f8cccae4daad503",
"sha256": "d18d10f8e0f924c8dea20478facd8e2be4d9f8b9f19c9c7a9335d32ee5c68385"
},
"downloads": -1,
"filename": "laboratory-0.1.0-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "1d0e346a79bc31d87f8cccae4daad503",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 6923,
"upload_time": "2016-02-14T21:09:47",
"url": "https://files.pythonhosted.org/packages/3f/25/74a8649f7d7d1bc9ab0aa0591f26fe4e27a2e09e10290b6431e4950de373/laboratory-0.1.0-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "bed76f0cac67f41ffeb805a1052771a1",
"sha256": "40b41848c0447e62432da7166c1e0c59bc907393880acb53145c887c6770890b"
},
"downloads": -1,
"filename": "laboratory-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "bed76f0cac67f41ffeb805a1052771a1",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 3993,
"upload_time": "2016-02-14T21:09:53",
"url": "https://files.pythonhosted.org/packages/a6/0b/388f441b84fa164acf89632472709fbcccbd5c4ad8777fb8815f9d2e7a1f/laboratory-0.1.0.tar.gz"
}
],
"0.2.0": [
{
"comment_text": "",
"digests": {
"md5": "aa70f9c8d2be658a86045ea2294fe23b",
"sha256": "47ac5138764698f2b08681659650019d7e8a2c3165796f66a2287cdedb30d2af"
},
"downloads": -1,
"filename": "laboratory-0.2.0-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "aa70f9c8d2be658a86045ea2294fe23b",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 7579,
"upload_time": "2016-02-18T01:45:23",
"url": "https://files.pythonhosted.org/packages/a5/41/9b2ffe13edd86f3b4e0b4f1d58dc554f0d95365487b1fb39eb11789868c1/laboratory-0.2.0-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "de27d82afafb53647c3dc6d40ddb6e84",
"sha256": "684842b10c99d4f950cb749a7c05f0912a7a08780e4090b66805bba874b2abe2"
},
"downloads": -1,
"filename": "laboratory-0.2.0.tar.gz",
"has_sig": false,
"md5_digest": "de27d82afafb53647c3dc6d40ddb6e84",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 4471,
"upload_time": "2016-02-18T01:45:28",
"url": "https://files.pythonhosted.org/packages/67/c8/433083afc20fb60dced30338dfbc67281390a122ea11d19fa2a570c050c6/laboratory-0.2.0.tar.gz"
}
],
"0.3.0": [
{
"comment_text": "",
"digests": {
"md5": "99b8fbfc43af249a917eff6888108e65",
"sha256": "908b73dbeee516cff2c2d27c6baa3de1a8272307fa4edc378b56e3e29c445ac3"
},
"downloads": -1,
"filename": "laboratory-0.3.0-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "99b8fbfc43af249a917eff6888108e65",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 7875,
"upload_time": "2016-04-18T23:23:35",
"url": "https://files.pythonhosted.org/packages/6c/4c/3f2d4d73018e7d0e47e985ce019626d279768d5839e5afb31b98fd62421e/laboratory-0.3.0-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "e0454545c209adfc314cf7c27afe08d5",
"sha256": "113bf7bcedee698598bfa08c266f97da058fd7d025741db5eb2f1a969e0d97a4"
},
"downloads": -1,
"filename": "laboratory-0.3.0.tar.gz",
"has_sig": false,
"md5_digest": "e0454545c209adfc314cf7c27afe08d5",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 4700,
"upload_time": "2016-04-18T23:23:56",
"url": "https://files.pythonhosted.org/packages/e5/a6/7ff70ec63403fc6e44dacdbeab61e2aec72edebbc7d00511e15fca1ddecf/laboratory-0.3.0.tar.gz"
}
],
"0.4.0": [
{
"comment_text": "",
"digests": {
"md5": "27ad8ce092eed826fe4d004b7288452d",
"sha256": "e8c0ce01dea1cac0878a5f80460dac28fc56b9f4f15cf7ef6232b7206ff2546b"
},
"downloads": -1,
"filename": "laboratory-0.4.0-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "27ad8ce092eed826fe4d004b7288452d",
"packagetype": "bdist_wheel",
"python_version": "2.7",
"requires_python": null,
"size": 8210,
"upload_time": "2016-05-11T10:31:17",
"url": "https://files.pythonhosted.org/packages/45/2c/0d2d6f070d8294b8b82083db65cd3b3d69db355cffd4f546e46bd2ca9b16/laboratory-0.4.0-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "a30b684fb68efac846d183024afdc9cc",
"sha256": "670769270b54019f8c282829ec370df3aaa142e33b7c7379069a1a7670157ca5"
},
"downloads": -1,
"filename": "laboratory-0.4.0.tar.gz",
"has_sig": false,
"md5_digest": "a30b684fb68efac846d183024afdc9cc",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 4978,
"upload_time": "2016-05-11T10:30:59",
"url": "https://files.pythonhosted.org/packages/a3/a1/c2b973d658321c157564f416cad9cf1d011ced77397eed5b3157e33b3c3c/laboratory-0.4.0.tar.gz"
}
],
"0.4.1": [
{
"comment_text": "",
"digests": {
"md5": "f7a4250ea16d6ce64db616e1200582c6",
"sha256": "c20e56c23ef40fb0882f91972fcab9e8f3c4bbdc95f7b54b0e6c176777da6af4"
},
"downloads": -1,
"filename": "laboratory-0.4.1-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "f7a4250ea16d6ce64db616e1200582c6",
"packagetype": "bdist_wheel",
"python_version": "2.7",
"requires_python": null,
"size": 8207,
"upload_time": "2016-05-12T17:29:05",
"url": "https://files.pythonhosted.org/packages/f4/05/511ab1e6f8882f586f8df9f9a6960c7a907a2968b5f73e426e41dda38c9c/laboratory-0.4.1-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "15ae052025592228bcd840394e6cfb94",
"sha256": "080e7f210b81b496842cb3c63bddf5bb7ac6aba883adff14b8c131431185f30d"
},
"downloads": -1,
"filename": "laboratory-0.4.1.tar.gz",
"has_sig": false,
"md5_digest": "15ae052025592228bcd840394e6cfb94",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 4965,
"upload_time": "2016-05-12T17:28:57",
"url": "https://files.pythonhosted.org/packages/da/75/6b192a4f9d60c75e9ca473229ee5e90479f69e2cbae3c78b547de5356516/laboratory-0.4.1.tar.gz"
}
],
"0.4.2": [
{
"comment_text": "",
"digests": {
"md5": "3f1a439b7e5b677ce445f0e0ec6ecdab",
"sha256": "e6f2a85aadbba876af03328b011602d84fea30228ef2536d0cb9ca6d6ed2cc44"
},
"downloads": -1,
"filename": "laboratory-0.4.2-py2.py3-none-any.whl",
"has_sig": true,
"md5_digest": "3f1a439b7e5b677ce445f0e0ec6ecdab",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 8216,
"upload_time": "2017-06-15T18:13:30",
"url": "https://files.pythonhosted.org/packages/25/fd/7855e5198129ad123ff427c95d64fe133e53db099e5b1d897c3281e39f4b/laboratory-0.4.2-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "2bac49dde4934bcf3e4b6641f67e0334",
"sha256": "67e70ea69b480c5da5385cb6006220c120a0d06b8f8d1c641dee3a4f61102704"
},
"downloads": -1,
"filename": "laboratory-0.4.2.tar.gz",
"has_sig": true,
"md5_digest": "2bac49dde4934bcf3e4b6641f67e0334",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 4955,
"upload_time": "2017-06-15T18:13:33",
"url": "https://files.pythonhosted.org/packages/88/cd/2210449f9b9491225af3884a27fd938dc72b93db246ef3d2963226f44ed8/laboratory-0.4.2.tar.gz"
}
],
"0.4.3": [
{
"comment_text": "",
"digests": {
"md5": "8b6477039d9b932a80b00b61f75c4188",
"sha256": "d3a7751edf92e37686b387149ae3ccf0b292adfa23e1e8fc0e605e284f84d91f"
},
"downloads": -1,
"filename": "laboratory-0.4.3-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "8b6477039d9b932a80b00b61f75c4188",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 9852,
"upload_time": "2018-01-17T22:41:02",
"url": "https://files.pythonhosted.org/packages/a5/cb/ac5081b72cb919f159bfc085d438e89884ad6bd82051e95084334a6b9265/laboratory-0.4.3-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "2fd56275270a4c106849ba75c69b976c",
"sha256": "cdc566150470edc5f68a082ee537addca1e701844dcb4eb8ba646845aa09c882"
},
"downloads": -1,
"filename": "laboratory-0.4.3.tar.gz",
"has_sig": false,
"md5_digest": "2fd56275270a4c106849ba75c69b976c",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 5972,
"upload_time": "2018-01-17T22:41:04",
"url": "https://files.pythonhosted.org/packages/0c/61/45ddddde243224bb2e292cb8b76eaa010190109b5d06bc2520257b77e4e0/laboratory-0.4.3.tar.gz"
}
],
"1.0": [
{
"comment_text": "",
"digests": {
"md5": "76f9607ea718b40594f69e46a3ea4ff7",
"sha256": "7f0410f9596a66f7cfefa08a2c75fc7d3bef3064484c28fe0effdea2f8b5200f"
},
"downloads": -1,
"filename": "laboratory-1.0-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "76f9607ea718b40594f69e46a3ea4ff7",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 11533,
"upload_time": "2018-01-29T21:27:15",
"url": "https://files.pythonhosted.org/packages/3e/f1/30f6103b2c9a85d3820429be583e253122b6be6c7ae044d22bbbbae5ca10/laboratory-1.0-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "90fb7b2f2611cf53f39b606e0d073d69",
"sha256": "e127a0ebd5acd60d44eb42637dbd44036368326b293e9627614015b174e406fe"
},
"downloads": -1,
"filename": "laboratory-1.0.tar.gz",
"has_sig": false,
"md5_digest": "90fb7b2f2611cf53f39b606e0d073d69",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 7389,
"upload_time": "2018-01-29T21:27:19",
"url": "https://files.pythonhosted.org/packages/a4/3c/34438f5ad1d41c16f9424b19fe5b619d14f10a636637864fcfebc3d7842c/laboratory-1.0.tar.gz"
}
],
"1.0.1": [
{
"comment_text": "",
"digests": {
"md5": "0e9b628b4f05bdf6d500f22d4cc11a91",
"sha256": "db1dad2eecb929683058ade38a9c04debfbd9f0cf808a72463351d90187e02de"
},
"downloads": -1,
"filename": "laboratory-1.0.1-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "0e9b628b4f05bdf6d500f22d4cc11a91",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 12685,
"upload_time": "2018-08-21T22:46:50",
"url": "https://files.pythonhosted.org/packages/49/c6/cafe1e009f19bcfe3d8ba7b01c5828ad68467a7d273292ab507d17551cd4/laboratory-1.0.1-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "5e6bf4db859ed13125b6c46c888eb6aa",
"sha256": "d6f4d6de98ee3d8365612a12df0b0ff61ae2f41d3f81b5fa1d811496dd703fe8"
},
"downloads": -1,
"filename": "laboratory-1.0.1.tar.gz",
"has_sig": false,
"md5_digest": "5e6bf4db859ed13125b6c46c888eb6aa",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 8173,
"upload_time": "2018-08-21T22:46:53",
"url": "https://files.pythonhosted.org/packages/30/25/35c5f084405f2f9cf921115a2de85af7a406304ddd2132f0caa431dc5cd5/laboratory-1.0.1.tar.gz"
}
],
"1.0.2": [
{
"comment_text": "",
"digests": {
"md5": "e9bbff6d1e4655697e7c90baee0cf775",
"sha256": "9501afa85a4252e2269ae7ed08aeded8d80ed0de5691b06297caab1f30b41277"
},
"downloads": -1,
"filename": "laboratory-1.0.2-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "e9bbff6d1e4655697e7c90baee0cf775",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 9450,
"upload_time": "2019-05-05T15:03:38",
"url": "https://files.pythonhosted.org/packages/be/c4/915d8f1d7dcf6c055c70976e1b44d12fa4691cd2493b1474aca689d459d1/laboratory-1.0.2-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "58f3a8f9276e9c822be5cad05c375b43",
"sha256": "8b92a73561c56896e926cbdca4a131d6a98f7f5b425f40fdfd4b3fec57646a63"
},
"downloads": -1,
"filename": "laboratory-1.0.2.tar.gz",
"has_sig": false,
"md5_digest": "58f3a8f9276e9c822be5cad05c375b43",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 8263,
"upload_time": "2019-05-05T15:03:42",
"url": "https://files.pythonhosted.org/packages/76/7e/8379ffbe24f9e23b9bb033cb86e71855638a07acad7ce4918d618174c415/laboratory-1.0.2.tar.gz"
}
],
"1.0.dev0": [
{
"comment_text": "",
"digests": {
"md5": "95aceb9a92d49e5f2b3307791690a6fd",
"sha256": "4b7ac2808e1156df294a16bf9a398bb064b870e0b2741b5a6c6c551bc3e44490"
},
"downloads": -1,
"filename": "laboratory-1.0.dev0-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "95aceb9a92d49e5f2b3307791690a6fd",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 11566,
"upload_time": "2018-01-23T22:11:31",
"url": "https://files.pythonhosted.org/packages/1c/7e/a247370a703b3789fedfebbaacdd6cb8f478ccf81b8eb2b57ffe50841790/laboratory-1.0.dev0-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "8ec90ffc77792f359b1b23d4e690abbd",
"sha256": "112eaaab14131c3d121fb0b4ae247c6f01881b67a8631f4e991a5f82b8d3b0fc"
},
"downloads": -1,
"filename": "laboratory-1.0.dev0.tar.gz",
"has_sig": false,
"md5_digest": "8ec90ffc77792f359b1b23d4e690abbd",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 7368,
"upload_time": "2018-01-23T22:11:34",
"url": "https://files.pythonhosted.org/packages/1e/e5/c776da48759d03abb110d3fb21a6f228eaa0f328cd47f5773db25420b4fd/laboratory-1.0.dev0.tar.gz"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "e9bbff6d1e4655697e7c90baee0cf775",
"sha256": "9501afa85a4252e2269ae7ed08aeded8d80ed0de5691b06297caab1f30b41277"
},
"downloads": -1,
"filename": "laboratory-1.0.2-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "e9bbff6d1e4655697e7c90baee0cf775",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 9450,
"upload_time": "2019-05-05T15:03:38",
"url": "https://files.pythonhosted.org/packages/be/c4/915d8f1d7dcf6c055c70976e1b44d12fa4691cd2493b1474aca689d459d1/laboratory-1.0.2-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "58f3a8f9276e9c822be5cad05c375b43",
"sha256": "8b92a73561c56896e926cbdca4a131d6a98f7f5b425f40fdfd4b3fec57646a63"
},
"downloads": -1,
"filename": "laboratory-1.0.2.tar.gz",
"has_sig": false,
"md5_digest": "58f3a8f9276e9c822be5cad05c375b43",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 8263,
"upload_time": "2019-05-05T15:03:42",
"url": "https://files.pythonhosted.org/packages/76/7e/8379ffbe24f9e23b9bb033cb86e71855638a07acad7ce4918d618174c415/laboratory-1.0.2.tar.gz"
}
]
}