@@ -168,6 +168,63 @@ def skip_if_no_remote_network() -> Optional[str]:
168168 return None
169169
170170
171+ def skip_on_env (
172+ varname : str ,
173+ present : bool = True ,
174+ eq : Optional [str ] = None ,
175+ ne : Optional [str ] = None ,
176+ reason : Optional [str ] = None ,
177+ ) -> Optional [str ]:
178+ """
179+ Helper function to check for environment variables.
180+
181+ If any of the checks match, return the skip reason.
182+
183+ Args:
184+ varname(str):
185+ The environment variable to check
186+ present(bool):
187+ When ``True``, skip if variable is present in the environment.
188+ When ``False``, skip if variable is not present in the environment.
189+ eq(str):
190+ Skips when the variable is present in the environment and matches this value.
191+ ne(str):
192+ Skips when the variable is present in the environment and does not match this value.
193+ reason(str):
194+ The custom reason message to use.
195+
196+ Returns:
197+ str: The skip reason
198+ None: Should not be skipped.
199+ """
200+ if eq and ne :
201+ raise pytest .UsageError ('Cannot pass both `eq` and `ne`.' )
202+ if present is False and (eq or ne ):
203+ raise pytest .UsageError ('Cannot pass `present=False` and either `eq` or `ne`.' )
204+ if present is False and varname not in os .environ :
205+ if not reason :
206+ reason = "The variable '{0}' is not present in the environ." .format (varname )
207+ return reason
208+ elif present is True and varname in os .environ :
209+ varname_value = os .environ [varname ]
210+ if eq :
211+ if varname_value == eq :
212+ if not reason :
213+ reason = "'{0}' present in environ and '{1}=={2}'" .format (
214+ varname , varname , eq
215+ )
216+ elif ne :
217+ if varname_value != ne :
218+ if not reason :
219+ reason = "'{0}' present in environ and '{1}!={2}'" .format (
220+ varname , varname , eq
221+ )
222+ elif not reason :
223+ reason = "The variable '{0}' is present in the environ." .format (varname )
224+ return reason
225+ return None
226+
227+
171228def evaluate_markers (item : 'Item' ) -> None :
172229 """
173230 Fixtures injection based on markers or test skips based on CLI arguments.
@@ -695,3 +752,31 @@ def evaluate_markers(item: 'Item') -> None:
695752 raise pytest .UsageError (
696753 'Passed an invalid platform to skip_unless_on_platforms: {}' .format (exc )
697754 )
755+ skip_on_env_marker = item .get_closest_marker ('skip_on_env' )
756+ if skip_on_env_marker is not None :
757+ args = list (skip_on_env_marker .args )
758+ if not args :
759+ raise pytest .UsageError (
760+ "The 'skip_on_env' marker needs at least one argument to be passed, the environment variable name."
761+ )
762+ envvar = args .pop (0 )
763+ if args :
764+ raise pytest .UsageError (
765+ "The 'skip_on_env' only accepts one argument, the environment variable name."
766+ )
767+ if not isinstance (envvar , str ):
768+ raise pytest .UsageError (
769+ 'The environment variable argument must be a string.'
770+ )
771+ kwargs = cast (Dict [str , Any ], skip_on_env_marker .kwargs ).copy ()
772+ present = kwargs .pop ('present' , True )
773+ eq = kwargs .pop ('eq' , None )
774+ ne = kwargs .pop ('ne' , None )
775+ reason = kwargs .pop ('reason' , None )
776+ if kwargs :
777+ raise pytest .UsageError (
778+ "The 'skip_on_env' marker only accepts 'present', 'eq', 'ne' and 'reason' as keyword arguments."
779+ )
780+ skip_reason = skip_on_env (envvar , present = present , eq = eq , ne = ne , reason = reason )
781+ if skip_reason :
782+ raise pytest .skip .Exception (skip_reason , ** exc_kwargs )
0 commit comments