{"id":17046335,"url":"https://github.com/sylvainde/didyoumean-python","last_synced_at":"2025-04-04T08:06:31.314Z","repository":{"id":45745101,"uuid":"25763670","full_name":"SylvainDe/DidYouMean-Python","owner":"SylvainDe","description":"Module to have suggestions in case of errors (NameError, AttributeError, etc).","archived":false,"fork":false,"pushed_at":"2025-01-28T07:37:07.000Z","size":1084,"stargazers_count":120,"open_issues_count":22,"forks_count":15,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-28T07:08:12.481Z","etag":null,"topics":["error","exception","exceptions","importerror","ioerror","python","suggestion","syntaxerror","valueerror"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SylvainDe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-10-26T08:29:48.000Z","updated_at":"2025-01-28T07:37:11.000Z","dependencies_parsed_at":"2024-05-02T23:03:45.745Z","dependency_job_id":"858ac846-971d-4121-86d3-c5e3a19b5261","html_url":"https://github.com/SylvainDe/DidYouMean-Python","commit_stats":{"total_commits":716,"total_committers":5,"mean_commits":143.2,"dds":0.006983240223463638,"last_synced_commit":"9ef827b94160c0e842487d072919063698704d43"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SylvainDe%2FDidYouMean-Python","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SylvainDe%2FDidYouMean-Python/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SylvainDe%2FDidYouMean-Python/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SylvainDe%2FDidYouMean-Python/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SylvainDe","download_url":"https://codeload.github.com/SylvainDe/DidYouMean-Python/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247142063,"owners_count":20890652,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["error","exception","exceptions","importerror","ioerror","python","suggestion","syntaxerror","valueerror"],"created_at":"2024-10-14T09:45:58.306Z","updated_at":"2025-04-04T08:06:31.295Z","avatar_url":"https://github.com/SylvainDe.png","language":"Python","readme":"DidYouMean-Python (aka BetterErrorMessages)\n===========================================\n\n[![BetterErrorMessages on PyPI](https://badge.fury.io/py/BetterErrorMessages.svg)](https://badge.fury.io/py/BetterErrorMessages)\n\n[![Coverage Status](https://coveralls.io/repos/SylvainDe/DidYouMean-Python/badge.svg?branch=master)](https://coveralls.io/r/SylvainDe/DidYouMean-Python?branch=master)\n\n[![Code Climate](https://codeclimate.com/github/SylvainDe/DidYouMean-Python/badges/gpa.svg)](https://codeclimate.com/github/SylvainDe/DidYouMean-Python)\n[![Scrutinizer](https://scrutinizer-ci.com/g/SylvainDe/DidYouMean-Python/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/SylvainDe/DidYouMean-Python/?branch=master)\n[![Codacy Badge](https://www.codacy.com/project/badge/Grade/54bed0a6466b48ea973325cff2594376)](https://www.codacy.com/app/sylvain-desodt-github/DidYouMean-Python)\n\nLogic to have various kind of suggestions in case of errors (NameError, AttributeError, ImportError, TypeError, ValueError, SyntaxError, MemoryError, OverflowError, IOError, OSError).\n\nInspired by \"Did you mean\" for Ruby ([Explanation](http://www.yukinishijima.net/2014/10/21/did-you-mean-experience-in-ruby.html), [Github Page](https://github.com/yuki24/did_you_mean)), this is a simple implementation for/in Python. I wanted to see if I could mess around and create something similar in Python and it seems to be possible.\n\nThis project is not maintained anymore\n--------------------------------------\n\nAs of October 2021, this project is not maintained anymore. Indeed, improvements to the exception messages have been implemented in Python 3.8, 3.9, 3.10 and 3.11 making this project useless (which is a good thing).\n\nIf you are interested in projects aiming to make exception messages better, please refer to the [list of projects below](https://github.com/SylvainDe/DidYouMean-Python#see-also-similar-projectsideas). In particular [friendly-traceback/friendly-traceback](https://github.com/friendly-traceback/friendly-traceback).\n\nI'll try to ensure that the test suite runs fine on all Python versions as it was useful in the past to catch issues in the Python project because things got released.\n\nUsage\n-----\n\nOnce the package is installed (see below), the logic adding suggestions can be invoked in different ways:\n\n * hook on `sys.excepthook` : just call `didyoumean_enablehook()` and you'll have the suggestions for any uncaught exception:\n\n```\n\u003e\u003e\u003e abc = 3\n\u003e\u003e\u003e abcd\nTraceback (most recent call last):\n  File \"\u003cstdin\u003e\", line 1, in \u003cmodule\u003e\nNameError: name 'abcd' is not defined\n\u003e\u003e\u003e didyoumean.didyoumean_api.didyoumean_enablehook()\n\u003e\u003e\u003e abcd\nTraceback (most recent call last):\n  File \"\u003cstdin\u003e\", line 1, in \u003cmodule\u003e\nNameError: name 'abcd' is not defined. Did you mean 'abc' (local)?\n```\n\n * post-mortem function `didyoumean_postmortem()` on the last uncaught exception during interactive sessions:\n\n```\n\u003e\u003e\u003e abc = 3\n\u003e\u003e\u003e abcd\nTraceback (most recent call last):\n  File \"\u003cstdin\u003e\", line 1, in \u003cmodule\u003e\nNameError: name 'abcd' is not defined\n\u003e\u003e\u003e didyoumean.didyoumean_api.didyoumean_postmortem()\nNameError(\"name 'abcd' is not defined. Did you mean 'abc' (local)?\",)\n```\n\n * context manager `didyoumean_contextmanager()`:\n\n```\n\u003e\u003e\u003e with didyoumean.didyoumean_api.didyoumean_contextmanager():\n...     abcd\n...\nTraceback (most recent call last):\n  File \"\u003cstdin\u003e\", line 2, in \u003cmodule\u003e\nNameError: name 'abcd' is not defined. Did you mean 'abc' (local)\n```\n\n * decorator : just add the `@didyoumean` decorator before any function (the `main()` could be a good choice) and you'll have the suggestions for any exception happening through a call to that method.\n\n\n```\n\u003e\u003e\u003e @didyoumean.didyoumean_api.didyoumean_decorator\n... def foo(): return abcd\n...\n\u003e\u003e\u003e foo()\nTraceback (most recent call last):\n  File \"\u003cstdin\u003e\", line 2, in foo\nNameError: global name 'abcd' is not defined. Did you mean 'abc' (global)?\n```\n\n_The API does not look great and may be updated in the near future._\n\n\nExample\n-------\n\n_More examples can be found from the test file `didyoumean/didyoumean_sugg_tests.py`._\n\n\n### NameError\n\n##### Fuzzy matches on existing names (local, builtin, keywords, modules, etc)\n\n```python\ndef my_func(foo, bar):\n\treturn foob\nmy_func(1, 2)\n#\u003e\u003e\u003e Before: NameError(\"global name 'foob' is not defined\",)\n#\u003e\u003e\u003e After: NameError(\"global name 'foob' is not defined. Did you mean 'foo' (local)?\",)\n```\n```python\nleng([0])\n#\u003e\u003e\u003e Before: NameError(\"name 'leng' is not defined\",)\n#\u003e\u003e\u003e After: NameError(\"name 'leng' is not defined. Did you mean 'len' (builtin)?\",)\n```\n```python\nimport math\nmaths.pi\n#\u003e\u003e\u003e Before: NameError(\"name 'maths' is not defined\",)\n#\u003e\u003e\u003e After: NameError(\"name 'maths' is not defined. Did you mean 'math' (local)?\",)\n```\n```python\npasss\n#\u003e\u003e\u003e Before: NameError(\"name 'passs' is not defined\",)\n#\u003e\u003e\u003e After: NameError(\"name 'passs' is not defined. Did you mean 'pass' (keyword)?\",)\n```\n```python\ndef my_func():\n\tfoo = 1\n\tfoob +=1\nmy_func()\n#\u003e\u003e\u003e Before: UnboundLocalError(\"local variable 'foob' referenced before assignment\",)\n#\u003e\u003e\u003e After: UnboundLocalError(\"local variable 'foob' referenced before assignment. Did you mean 'foo' (local)?\",)\n```\n##### Checking if name is the attribute of a defined object\n\n```python\nclass Duck():\n\tdef __init__(self):\n\t\tquack()\n\tdef quack(self):\n\t\tpass\nd = Duck()\n#\u003e\u003e\u003e Before: NameError(\"global name 'quack' is not defined\",)\n#\u003e\u003e\u003e After: NameError(\"global name 'quack' is not defined. Did you mean 'self.quack'?\",)\n```\n```python\nimport math\npi\n#\u003e\u003e\u003e Before: NameError(\"name 'pi' is not defined\",)\n#\u003e\u003e\u003e After: NameError(\"name 'pi' is not defined. Did you mean 'math.pi'?\",)\n```\n##### Looking for missing imports\n\n```python\nstring.ascii_lowercase\n#\u003e\u003e\u003e Before: NameError(\"name 'string' is not defined\",)\n#\u003e\u003e\u003e After: NameError(\"name 'string' is not defined. Did you mean to import string first?\",)\n```\n##### Looking in missing imports\n\n```python\nchoice\n#\u003e\u003e\u003e Before: NameError(\"name 'choice' is not defined\",)\n#\u003e\u003e\u003e After: NameError(\"name 'choice' is not defined. Did you mean 'choice' from random (not imported)?\",)\n```\n##### Special cases\n\n```python\nassert j ** 2 == -1\n#\u003e\u003e\u003e Before: NameError(\"name 'j' is not defined\",)\n#\u003e\u003e\u003e After: NameError(\"name 'j' is not defined. Did you mean '1j' (imaginary unit)?\",)\n```\n### AttributeError\n\n##### Fuzzy matches on existing attributes\n\n```python\nlst = [1, 2, 3]\nlst.appendh(4)\n#\u003e\u003e\u003e Before: AttributeError(\"'list' object has no attribute 'appendh'\",)\n#\u003e\u003e\u003e After: AttributeError(\"'list' object has no attribute 'appendh'. Did you mean 'append'?\",)\n```\n```python\nimport math\nmath.pie\n#\u003e\u003e\u003e Before: AttributeError(\"'module' object has no attribute 'pie'\",)\n#\u003e\u003e\u003e After: AttributeError(\"'module' object has no attribute 'pie'. Did you mean 'pi'?\",)\n```\n##### Trying to find method with similar meaning (hardcoded)\n\n```python\nlst = [1, 2, 3]\nlst.add(4)\n#\u003e\u003e\u003e Before: AttributeError(\"'list' object has no attribute 'add'\",)\n#\u003e\u003e\u003e After: AttributeError(\"'list' object has no attribute 'add'. Did you mean 'append'?\",)\n```\n```python\nlst = [1, 2, 3]\nlst.get(5, None)\n#\u003e\u003e\u003e Before: AttributeError(\"'list' object has no attribute 'get'\",)\n#\u003e\u003e\u003e After: AttributeError(\"'list' object has no attribute 'get'. Did you mean 'obj[key]' with a len() check or try: except: KeyError or IndexError?\",)\n```\n##### Detection of mis-used builtins\n\n```python\nlst = [1, 2, 3]\nlst.max()\n#\u003e\u003e\u003e Before: AttributeError(\"'list' object has no attribute 'max'\")\n#\u003e\u003e\u003e After: AttributeError(\"'list' object has no attribute 'max'. Did you mean 'max(list)'?\")\n```\n##### Period used instead of comma\n\n```python\na, b = 1, 2\nmax(a. b)\n#\u003e\u003e\u003e Before: AttributeError(\"'int' object has no attribute 'b'\")\n#\u003e\u003e\u003e After: AttributeError(\"'int' object has no attribute 'b'. Did you mean to use a comma instead of a period?\")\n```\n### ImportError\n\n##### Fuzzy matches on existing modules\n\n```python\nfrom maths import pi\n#\u003e\u003e\u003e Before: ImportError('No module named maths',)\n#\u003e\u003e\u003e After: ImportError(\"No module named maths. Did you mean 'math'?\",)\n```\n##### Fuzzy matches on elements of the module\n\n```python\nfrom math import pie\n#\u003e\u003e\u003e Before: ImportError('cannot import name pie',)\n#\u003e\u003e\u003e After: ImportError(\"cannot import name pie. Did you mean 'pi'?\",)\n```\n##### Looking for import from wrong module\n\n```python\nfrom itertools import pi\n#\u003e\u003e\u003e Before: ImportError('cannot import name pi',)\n#\u003e\u003e\u003e After: ImportError(\"cannot import name pi. Did you mean 'from math import pi'?\",)\n```\n### TypeError\n\n##### Fuzzy matches on keyword arguments\n\n```python\ndef my_func(abcde):\n\tpass\nmy_func(abcdf=1)\n#\u003e\u003e\u003e Before: TypeError(\"my_func() got an unexpected keyword argument 'abcdf'\",)\n#\u003e\u003e\u003e After: TypeError(\"my_func() got an unexpected keyword argument 'abcdf'. Did you mean 'abcde'?\",)\n```\n##### Confusion between brackets and parenthesis\n\n```python\nlst = [1, 2, 3]\nlst(0)\n#\u003e\u003e\u003e Before: TypeError(\"'list' object is not callable\",)\n#\u003e\u003e\u003e After: TypeError(\"'list' object is not callable. Did you mean 'list[value]'?\",)\n```\n```python\ndef my_func(a):\n    pass\nmy_func[1]\n#\u003e\u003e\u003e Before: TypeError(\"'function' object has no attribute '__getitem__'\",)\n#\u003e\u003e\u003e After: TypeError(\"'function' object has no attribute '__getitem__'. Did you mean 'function(value)'?\",)\n```\n### ValueError\n\n##### Special cases\n\n```python\n'Foo{}'.format('bar')\n#\u003e\u003e\u003e Before: ValueError('zero length field name in format',)\n#\u003e\u003e\u003e After: ValueError('zero length field name in format. Did you mean {0}?',)\n```\n```python\nimport datetime\ndatetime.datetime.strptime(\"%d %b %y\", \"30 Nov 00\")\n#\u003e Before: ValueError(\"time data '%d %b %y' does not match format '30 Nov 00'\",)\n#\u003e After: ValueError(\"time data '%d %b %y' does not match format '30 Nov 00'. Did you mean to swap value and format parameters?\",)\n```\n\n### SyntaxError\n\n##### Fuzzy matches when importing from __future__\n\n```python\nfrom __future__ import divisio\n#\u003e\u003e\u003e Before: SyntaxError('future feature divisio is not defined',)\n#\u003e\u003e\u003e After: SyntaxError(\"future feature divisio is not defined. Did you mean 'division'?\",)\n```\n##### Various\n\n```python\nreturn\n#\u003e\u003e\u003e Before: SyntaxError(\"'return' outside function\", ('\u003cstring\u003e', 1, 0, None))\n#\u003e\u003e\u003e After: SyntaxError(\"'return' outside function. Did you mean to indent it, 'sys.exit([arg])'?\", ('\u003cstring\u003e', 1, 0, None))\n```\n### MemoryError\n\n##### Search for a memory-efficient equivalent\n\n```python\nrange(99999999999)\n#\u003e\u003e\u003e Before: MemoryError()\n#\u003e\u003e\u003e After: MemoryError(\". Did you mean 'xrange'?\",)\n```\n### OverflowError\n\n##### Search for a memory-efficient equivalent\n\n```python\nrange(999999999999999)\n#\u003e\u003e\u003e Before: OverflowError('range() result has too many items',)\n#\u003e\u003e\u003e After: OverflowError(\"range() result has too many items. Did you mean 'xrange'?\",)\n```\n### OSError/IOError\n\n##### Suggestion for tilde/variable expansions\n\n```python\nos.listdir('~')\n#\u003e\u003e\u003e Before: OSError(2, 'No such file or directory')\n#\u003e\u003e\u003e After: OSError(2, \"No such file or directory. Did you mean '/home/user' (calling os.path.expanduser)?\")\n```\n### RuntimeError\n\n##### Suggestion to avoid reaching maximum recursion depth\n\n```python\nglobal rec\ndef rec(n): return rec(n-1)\nrec(0)\n#\u003e\u003e\u003e Before: RuntimeError('maximum recursion depth exceeded',)\n#\u003e\u003e\u003e After: RuntimeError('maximum recursion depth exceeded. Did you mean to avoid recursion (cf http://neopythonic.blogspot.fr/2009/04/tail-recursion-elimination.html), increase the limit with `sys.setrecursionlimit(limit)` (current value is 1000)?',)\n```\n\nInstallation\n------------\n\nThe package is available on [Pypi](https://pypi.python.org/pypi) as [BetterErrorMessages](https://pypi.python.org/pypi/BetterErrorMessages/).\n\nInstallation can be done from the package index with `pip install BetterErrorMessages`.\n\nInstallation from sources can be done just as easily:\n\n```\ngit clone https://github.com/SylvainDe/DidYouMean-Python.git\ncd DidYouMean-Python\ngit install .\n```\n\n\n\nMaking things automatic in your interactive sessions\n----------------------------------------------------\n\nYou can have the suggestions automatically in your interactive sessions by adding the following code in your [${PYTHONSTARTUP} file](https://docs.python.org/3.6/using/cmdline.html#envvar-PYTHONSTARTUP):\n\n```\ntry:\n    import didyoumean\nexcept ImportError:\n    print(\"Did you mean to install BetterErrorMessages first (`pip install BetterErrorMessages`)\")\nelse:\n    didyoumean.didyoumean_api.didyoumean_enablehook()\n```\n\nImplementation\n--------------\n\nAll external APIs (decorator, hook, etc) use the same logic behind the scene. It works in a pretty simple way : when an exception happens, we try to get the relevant information out of the error message and of the backtrace to find the most relevant suggestions. To filter the best suggestions out of everything in case of fuzzy match, I am currently using ```difflib```.\n\n\nSee also (similar projects/ideas)\n---------------------------------\n\nProjects:\n\n - [\"Did you mean\" for Ruby (yuki24/did_you_mean)](https://github.com/yuki24/did_you_mean): source for the idea.\n\n - [dutc/didyoumean](https://github.com/dutc/didyoumean) : a quite similar project developed in pretty much the same time. A few differences though : written in C, works only for AttributeError, etc.\n\n - [nvbn/TheF*ck](https://github.com/nvbn/thefuck) : Correct and execute your previous shell command.\n\n - [asweigart/PyDidYouMean](https://github.com/asweigart/pydidyoumean) : Improve \"file/command not found\" errors with suggestions.\n\n - [Qix-/better-exceptions](https://github.com/Qix-/better-exceptions) : Pretty and useful exceptions in Python\n\n - [danrobinson/tracestack](https://github.com/danrobinson/tracestack) : Search your Python error messages on the web.\n\n - [cfbolz/syntaxerrors](https://github.com/cfbolz/syntaxerrors) : Python parser that can recover from errors (also, cfbolz added many suggestions for errors to PyPy).\n\n - [friendly-traceback/friendly-traceback](https://github.com/friendly-traceback/friendly-traceback) : Replace standard traceback by something easier to understand.\n\n - [Did You Mean in Perl](http://perltricks.com/article/122/2014/10/31/Implementing-Did-You-Mean-in-Perl)\n\n - [Commit in iPython](https://github.com/ipython/ipython/pull/9073/files) to add suggestions in case of errors\n\n\nDiscussions:\n\n - [PEP 473 :  Adding structured data to built-in exceptions](http://legacy.python.org/dev/peps/pep-0473/).\n\n - Ideas from the Python Ideas mailing list : [\"Improve error message when missing 'self' in method definition\"](https://mail.python.org/pipermail/python-ideas/2016-October/042672.html), \"Better error messages\" [part 1](https://mail.python.org/pipermail/python-ideas/2016-November/043848.html) and [part 2](https://mail.python.org/pipermail/python-ideas/2016-December/043910.html)\n\n - In [Raymond Hettinger's PyconCA keynote](https://www.youtube.com/watch?v=-TdrFjDJn5E), the part about the `hint` builtin (at 14 minutes) looks a lot like `didyoumean_postmortem`.\n\n\nContributing\n------------\n\nFeedback is welcome, feel free to :\n * send me an email for any question/advice/comment/criticism\n * open issues if something goes wrong (please provide at least the version of Python you are using).\n\nAlso, pull-requests are welcome to :\n * fix issues\n * enhance the documentation\n * improve the code\n * bring awesomeness\n\nAs for the technical details :\n\n * this is under MIT License : you can do anything you want as long as you provide attribution back to this project.\n * I try to follow [PEP 8](http://legacy.python.org/dev/peps/pep-0008/) and [PEP 257](https://www.python.org/dev/peps/pep-0257/) as much as possible. Compliancy is checked during continuous integration using the [pycodestyle](https://pypi.python.org/pypi/pycodestyle) and [pep257](https://pypi.python.org/pypi/pep257) checkers.\n * I try to have most of the code covered by unit tests.\n * I try to write the code in such a way that it works on all Python versions from 2.6 (included).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsylvainde%2Fdidyoumean-python","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsylvainde%2Fdidyoumean-python","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsylvainde%2Fdidyoumean-python/lists"}