Skip to content

Instantly share code, notes, and snippets.

@pradyunsg
Created October 15, 2021 18:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pradyunsg/2a325d32ef6901e288780c064eab763e to your computer and use it in GitHub Desktop.
Save pradyunsg/2a325d32ef6901e288780c064eab763e to your computer and use it in GitHub Desktop.
A draft of what better error messages in pip _could_ look like

Better Error Messages in pip

Example 1

Today:

ERROR: Could not find a version that satisfies the requirement qwertyuidfudsjhdblfnsfngnsjkfnjsflz (from versions: none)
ERROR: No matching distribution found for qwertyuidfudsjhdblfnsfngnsjkfnjsfl

Redesign:

Error: no-available-distribution-files

× Could not find any distribution files for qwertyuidfudsjhdblfnsfngnsjkfnjsfl
╰─▶ 0 distribution files were found

💡 Hint: Is there a typo in the package name?
Error: no-compatible-distribution-files

× Could not find any compatible distribution files for qwertyuidfudsjhdblfnsfngnsjkfnjsfl
╰─▶ 2 distribution files were found
    0 are compatible with this system

💡 Hint: Is this package compatible with your OS and Python version?

This will need some code refactoring though, since we need to change where the error is raised.

Example 2

Today:

Looking in indexes: https://artlondon.dev.bloomberg.com/artifactory/api/pypi/bloomberg-pypi/simple
Obtaining file:///Users/pgedam/Developer/OSS/pip
  Preparing metadata (setup.py) ... error
  ERROR: Command errored out with exit status 1:
   command: /Users/pgedam/Developer/OSS/pip/.venv/bin/python -c 'import io, os, sys, setuptools, tokenize; sys.argv[0] = '"'"'/Users/pgedam/Developer/OSS/pip/setup.py'"'"'; __file__='"'"'/Users/pgedam/Developer/OSS/pip/setup.py'"'"';f = getattr(tokenize, '"'"'open'"'"', open)(__file__) if os.path.exists(__file__) else io.StringIO('"'"'from setuptools import setup; setup()'"'"');code = f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /private/var/folders/zz/t_4dqqmd0vsfy3ds10d4s9240000gp/T/pip-pip-egg-info-2rzgcvm0
       cwd: /Users/pgedam/Developer/OSS/pip/
  Complete output (5 lines):
  Traceback (most recent call last):
    File "<string>", line 1, in <module>
    File "/Users/pgedam/Developer/OSS/pip/setup.py", line 26, in <module>
      raise Exception("Fail!")
  Exception: Fail!
  ----------------------------------------
WARNING: Discarding file:///Users/pgedam/Developer/OSS/pip. Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

Redesign:

Looking in indexes: https://artlondon.dev.bloomberg.com/artifactory/api/pypi/bloomberg-pypi/simple
Obtaining file:///Users/pgedam/Developer/OSS/pip
  Preparing metadata (setup.py) ... error
  Complete output (5 lines):
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/Users/pgedam/Developer/OSS/pip/setup.py", line 26, in <module>
        raise Exception("Fail!")
    Exception: Fail!
    ----------------------------------------
WARNING: Discarding file:///Users/pgedam/Developer/OSS/pip. metadata-generation-failed

Error: metadata-generation-failed

× Could not generate metadata for pip
╰─▶ The package's build system exited with a non-zero exit code: 1

⚠️ This is likely an issue with how the pip package has to be built/installed.
💡 Hint: The pip package's build system output is available above.

Example 3: SSL issues

Today:

Collecting xdict
  Could not fetch URL https://pypi.python.org/simple/xdict/: There was a problem confirming the ssl certificate: [SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:590) - skipping
  Could not find a version that satisfies the requirement xdict (from versions: )
No matching distribution found for xdict

Redesigned:

Collecting xdict
  Warning: network-connectivity (SSL)

  × Skipped URL: https://pypi.python.org/simple/xdict/
  ╰─▶ There was a problem confirming the SSL certificate!
      [SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:590)

  ⚠️ This is likely an issue with the network, or pip's network configuration.

Error: no-available-distribution-files

× Could not find any distribution files for xdict
╰─▶ 0 distribution files were found

💡 Hint: Is there a typo in the package name?

If we can manage adding some complexity, for passing the information about the "get-page" failure, to the point in the code where we actually raise the error -- this will be possible:

Collecting xdict
  Warning: network-connectivity (SSL)

  × Skipped URL: https://pypi.python.org/simple/xdict/
  ╰─▶ There was a problem confirming the SSL certificate!
      [SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:590)

Error: no-available-distribution-files

× Could not find any distribution files for xdict
╰─▶ 0 distribution files were found

⚠️ This is likely an issue with the network, or pip's network configuration.
💡 Hint: The details of the network issue was printed by pip, when it occurred.

Implementation

ErrorCause = Union[str, List[str]]


class DiagnosticError(Exception):
    def __init__(
        message: str,
        cause: str,
        *,
        style: Literal["error", "warning"] = "error",
        reference: Optional[str] = None,  # this needs to be set on the class, or in __init__.
        hint: Optional[str] = None,
        caution: Optional[str] = None,
    ):
        ...
    
    def __str__(self):
        return self.reference
    
    def __repr__(self):
        return f"<{self.__class__.__name__}({self.message})>"
    
    def __rich__(self):
        # This is where I have to implement the presentation logic.
        pass


class ConcreteDiagnosticError(DiagnosticError):
    """Demonstrates the exception implementation style.
    """
    reference = "concrete"
    
    def __init__(self, ...):
        # figure out the diagnostic messages, to pass into __init__
        super().__init__(...)

Inspiration

miette, graphical

  × wtf?!
  │ it broke :(
  ├─▶ something went wrong
  │\u{20}\u{20}\u{20}
  │   Here's a more detailed explanation of everything that actually went
  │   wrong because it's actually important.
  │\u{20}\u{20}\u{20}
  ╰─▶ very much went wrong
  help: try doing it better next time?

miette, narrative

wtf?!
it broke :(
    Diagnostic severity: error
    Caused by: something went wrong
Here's a more detailed explanation of everything that actually went wrong because it's actually important.
    Caused by: very much went wrong
diagnostic help: try doing it better next time?
diagnostic code: oops::my::bad
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment