Coverage for flogin/errors.py: 84%

31 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-03 22:51 +0000

1from __future__ import annotations 

2 

3TYPE_CHECKING = False 

4if TYPE_CHECKING: 

5 from subprocess import ( 

6 CalledProcessError, # https://github.com/astral-sh/ruff/issues/15681 

7 ) 

8 

9 import requests # https://github.com/astral-sh/ruff/issues/15681 

10 

11__all__ = ( 

12 "EnvNotSet", 

13 "PipException", 

14 "PipExecutionError", 

15 "PluginException", 

16 "PluginNotInitialized", 

17 "UnableToDownloadPip", 

18) 

19 

20 

21class PluginException(Exception): 

22 r"""A class that represents exceptions with your plugin""" 

23 

24 

25class PluginNotInitialized(PluginException): 

26 r"""This is raised when you try to access something that needs data from the initialize method, and it hasn't been called yet.""" 

27 

28 def __init__(self) -> None: 

29 super().__init__("The plugin hasn't been initialized yet") 

30 

31 

32class EnvNotSet(PluginException): 

33 """This is raised when an environment variable that flow automatically sets is not set and can not be retrieved. This should only get raised when your plugin gets run, but not by flow. 

34 

35 .. versionadded: 1.1.0 

36 

37 Attributes 

38 ----------- 

39 name: :class:`str` 

40 The name of the environment variable that was not found 

41 alternative: Optional[:class:`str`] 

42 Optionally, the name of the keyword argument in the :class:`~flogin.testing.plugin_tester.PluginTester` constructor that will set the variable for you. 

43 """ 

44 

45 def __init__(self, name: str, alternative: str | None = None) -> None: 

46 self.name = name 

47 self.alternative = alternative 

48 alt = ( 

49 f"If you ran your plugin via the plugin tester, you can use the {alternative!r} keyword argument to quickly set this." 

50 if alternative 

51 else "" 

52 ) 

53 super().__init__( 

54 f"The {name!r} environment variable is not set. These should be set by flow when it runs your plugin. {alt}" 

55 ) 

56 

57 

58class PipException(Exception): 

59 r"""This is a base class to represent errors derived from the :class:`~flogin.pip.Pip` object. 

60 

61 .. versionadded:: 2.0.0 

62 """ 

63 

64 

65class UnableToDownloadPip(PipException): 

66 r"""This is an exception which is used to indicate that an error occurred while attempting to download pip. 

67 

68 .. versionadded:: 2.0.0 

69 

70 Attributes 

71 ---------- 

72 error: :class:`requests.exceptions.HTTPError` | :class:`requests.Timeout` | :class:`requests.ConnectionError` 

73 The error that was raised by the :doc:`req:index` module. 

74 """ 

75 

76 def __init__(self, err: requests.RequestException) -> None: 

77 super().__init__(err) 

78 self.error = err 

79 

80 

81class PipExecutionError(PipException): 

82 r"""This is an exception which is raised whenever :meth:`flogin.pip.Pip.run` gets a return code that isn't ``0``. 

83 

84 .. versionadded:: 2.0.0 

85 

86 Attributes 

87 ---------- 

88 error: :class:`subprocess.CalledProcessError` 

89 The original error that was raised by subprocess 

90 """ 

91 

92 def __init__(self, err: CalledProcessError) -> None: 

93 super().__init__( 

94 f"An error occurred while attempting to use pip: {err.stderr.decode()}" 

95 ) 

96 self.error = err 

97 

98 @property 

99 def output(self) -> str: 

100 """:class:`str` The output from :attr:`subprocess.CalledProcessError.output`""" 

101 return self.error.output.decode() 

102 

103 @property 

104 def returncode(self) -> int: 

105 """:class:`int` The returncode from :attr:`subprocess.CalledProcessError.returncode`""" 

106 return self.error.returncode 

107 

108 @property 

109 def stderr(self) -> str: 

110 """:class:`str` The stderr from :attr:`subprocess.CalledProcessError.stderr`""" 

111 return self.error.stderr.decode()