{ "info": { "author": "Joe Cross", "author_email": "joe.mcross@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Topic :: Software Development :: Libraries" ], "description": ".. image:: https://img.shields.io/travis/numberoverzero/roughly/master.svg?style=flat-square\n :target: https://travis-ci.org/numberoverzero/roughly\n.. image:: https://img.shields.io/coveralls/numberoverzero/roughly/master.svg?style=flat-square\n :target: https://coveralls.io/github/numberoverzero/roughly\n.. image:: https://img.shields.io/pypi/v/roughly.svg?style=flat-square\n :target: https://pypi.python.org/pypi/roughly\n.. image:: https://img.shields.io/pypi/status/roughly.svg?style=flat-square\n :target: https://pypi.python.org/pypi/roughly\n.. image:: https://img.shields.io/github/issues-raw/numberoverzero/roughly.svg?style=flat-square\n :target: https://github.com/numberoverzero/roughly/issues\n.. image:: https://img.shields.io/pypi/l/roughly.svg?style=flat-square\n :target: https://github.com/numberoverzero/roughly/blob/master/LICENSE\n\n``__eq__`` overloading for simpler approximate equality testing\n\nInstallation\n------------\n::\n\n pip install roughly\n\nUsage\n-----\n\nTesting a function that returns ``arrow.now()`` usually looks like this::\n\n import arrow\n\n\n def function_under_test():\n return arrow.now()\n\n\n def test_function_returns_now():\n now = arrow.now()\n actual = function_under_test()\n assert now.replace(seconds=-2) <= actual <= now.replace(seconds=2)\n\nIt gets worse when you want to check two objects that have a datetime field\nthat should be approximately equal, but you still want the class's\n``__eq__`` to check exact equality. There's a better way::\n\n import arrow\n import roughly\n\n\n def test_function_returns_now():\n now = roughly.near(arrow.now(), seconds=2)\n assert function_under_test() == now\n\n\nMotivation\n----------\n\nApproximate equality should be handled carefully. You can introduce subtle\nerrors when two dates are close by one part of the system, but not close in\nanother.\n\nMost frequently in testing, however, we'd really like to use the existing\nequality-based tests for objects that have datetime attributes, without\npatching the system that vends datetimes.\n\nConsider a test that stores an object in a mock database, updating an item\nwith the current time. We'd really like to use the helpers provided by\n``unittest.mock`` like ``assert_called_once_with`` but that requires us to use\nexact values. We need something that **looks** like equality to another\nsystem.\n\nWithout roughly, here's the workaround to ensure a date is nearby. Note that\nall of the other arguments could have been checked with mock's assert calls::\n\n assert engine.save.call_count == 1\n (item, *_), kwargs = engine.save.call_args\n assert item is key\n assert kwargs[\"atomic\"] is True\n\n # :( One of the the fields in this object is a datetime, so we can't\n # do an exact match. We could mock arrow.now() but that's really an\n # implementation detail that we shouldn't need to know.\n condition = Key.until >= arrow.now().replace(seconds=-10)\n assert kwargs[\"condition\"].column is condition.column\n assert kwargs[\"condition\"].value >= condition.value\n\nHere's exactly the same check, but with a ``roughly.near`` datetime for the\ncondition (the condition being tested is a bloop ConditionalExpression)::\n\n condition = Key.until >= near(arrow.now(), seconds=5)\n engine.save.assert_called_once_with(key, atomic=True, condition=condition)\n\nThe approximate parts of the object are injected into the arguments we expect,\nand when the ``unittest.mock`` machinery performs the comparison\n``self._call_matcher((args, kwargs)) == self._call_matcher(self.call_args)``\nand iterates through the ``(args, kwargs)`` tuples, it will compare the\ncondition object with our approximate datetime to the actual condition with a\nreal datetime. Inside that class's ``__eq__``, it will check\n``self.value == other.value`` which holds for our ``ApproximateArrow`` compared\nto the actual ``Arrow``.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/numberoverzero/roughly", "keywords": "fuzz fuzzy approx approximate equality", "license": "MIT", "maintainer": null, "maintainer_email": null, "name": "roughly", "package_url": "https://pypi.org/project/roughly/", "platform": "any", "project_url": "https://pypi.org/project/roughly/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/numberoverzero/roughly" }, "release_url": "https://pypi.org/project/roughly/0.0.6/", "requires_dist": null, "requires_python": null, "summary": "approximations for equality testing", "version": "0.0.6" }, "last_serial": 2157929, "releases": { "0.0.0": [], "0.0.1": [ { "comment_text": "", "digests": { "md5": "289ae7b030005f5522253a35dfaf1a86", "sha256": "0f73a07ab947499ef6e90f70a767d5ede7d458b73daa02b8f4848afd80860a5a" }, "downloads": -1, "filename": "roughly-0.0.1.tar.gz", "has_sig": false, "md5_digest": "289ae7b030005f5522253a35dfaf1a86", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 3263, "upload_time": "2016-06-04T22:52:49", "url": "https://files.pythonhosted.org/packages/6d/b9/aff10bd7e89083ae1594c656978b9078dc29ba49c6e195bde65786391d4e/roughly-0.0.1.tar.gz" } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "ca9e14d4c0e4f90e8f8e2aac8cbf2495", "sha256": "9eb2197a885e000a49ba743bcc76a6a19fd17c0df0988b914f45b67ee4af0bb9" }, "downloads": -1, "filename": "roughly-0.0.2.tar.gz", "has_sig": false, "md5_digest": "ca9e14d4c0e4f90e8f8e2aac8cbf2495", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 3412, "upload_time": "2016-06-05T00:06:16", "url": "https://files.pythonhosted.org/packages/23/c6/e2ead50d42384e5b049507df0c01a4a5184e053f0e57220fb76cf1cd5a08/roughly-0.0.2.tar.gz" } ], "0.0.3": [ { "comment_text": "", "digests": { "md5": "9ece9556b9832cdffd118097f064c988", "sha256": "eb01840457bb217a147f15991c57b81c554947cd7a7442722c5b7cf6d5d39b3e" }, "downloads": -1, "filename": "roughly-0.0.3.tar.gz", "has_sig": false, "md5_digest": "9ece9556b9832cdffd118097f064c988", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 3540, "upload_time": "2016-06-05T01:43:17", "url": "https://files.pythonhosted.org/packages/90/34/9038293a1021894a4cf5fa745a9669e069781801471aff194ee56111035c/roughly-0.0.3.tar.gz" } ], "0.0.4": [ { "comment_text": "", "digests": { "md5": "d7428c5f2c7690a514f6b2656f5431bf", "sha256": "1ccc9effeabd309b2d48b3319dcf4ee6608f6da78c74287d16da6c6c6a14fd76" }, "downloads": -1, "filename": "roughly-0.0.4.tar.gz", "has_sig": false, "md5_digest": "d7428c5f2c7690a514f6b2656f5431bf", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4721, "upload_time": "2016-06-05T02:07:00", "url": "https://files.pythonhosted.org/packages/63/e8/f62fd82a9058c2a7c361212909a81df5854e05300f55990011f78b9d0819/roughly-0.0.4.tar.gz" } ], "0.0.5": [ { "comment_text": "", "digests": { "md5": "dfa79fdb3d8fbc238bbdca97ab7fb966", "sha256": "f724b521179433e297c166658d099c8f79b3de6aa07d361a10a61360950f0fa8" }, "downloads": -1, "filename": "roughly-0.0.5.tar.gz", "has_sig": false, "md5_digest": "dfa79fdb3d8fbc238bbdca97ab7fb966", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4393, "upload_time": "2016-06-05T05:25:31", "url": "https://files.pythonhosted.org/packages/8a/92/77bb6fff05576f07d11753b8dab6986f239f014240167007c32ea5136ce0/roughly-0.0.5.tar.gz" } ], "0.0.6": [ { "comment_text": "", "digests": { "md5": "04d22d2c8925dd27c14cff11ab9289be", "sha256": "e49d72c2b2f90fdb7d4963c254874dbc667de25b443c48d26419b3d85b73dd8b" }, "downloads": -1, "filename": "roughly-0.0.6.tar.gz", "has_sig": false, "md5_digest": "04d22d2c8925dd27c14cff11ab9289be", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4448, "upload_time": "2016-06-09T01:03:25", "url": "https://files.pythonhosted.org/packages/df/9b/514842a670ec6719c895ab47da5be1701aee15da10464567a48263cbb21f/roughly-0.0.6.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "04d22d2c8925dd27c14cff11ab9289be", "sha256": "e49d72c2b2f90fdb7d4963c254874dbc667de25b443c48d26419b3d85b73dd8b" }, "downloads": -1, "filename": "roughly-0.0.6.tar.gz", "has_sig": false, "md5_digest": "04d22d2c8925dd27c14cff11ab9289be", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4448, "upload_time": "2016-06-09T01:03:25", "url": "https://files.pythonhosted.org/packages/df/9b/514842a670ec6719c895ab47da5be1701aee15da10464567a48263cbb21f/roughly-0.0.6.tar.gz" } ] }