1515import time
1616import warnings
1717from abc import ABC
18+ from abc import ABCMeta
1819from abc import abstractmethod
1920from dataclasses import dataclass
2021from dataclasses import field
3435from typing import List
3536from typing import Optional
3637from typing import overload
38+
39+ try :
40+ from typing import Self
41+ except ImportError :
42+ from typing_extensions import Self
3743from typing import Tuple
3844from typing import Type
3945from typing import Union
4450import testinfra
4551from filelock import BaseFileLock
4652from filelock import FileLock
53+ from pytest import Mark
54+ from pytest import MarkDecorator
4755from pytest import param
4856from pytest_container .helpers import get_always_pull_option
4957from pytest_container .inspect import ContainerHealth
@@ -493,6 +501,11 @@ class ContainerBase:
493501 default_factory = list
494502 )
495503
504+ #: optional list of marks applied to this container image under test
505+ _marks : Collection [Union [MarkDecorator , Mark ]] = field (
506+ default_factory = list
507+ )
508+
496509 _is_local : bool = False
497510
498511 def __post_init__ (self ) -> None :
@@ -503,6 +516,9 @@ def __post_init__(self) -> None:
503516 def __str__ (self ) -> str :
504517 return self .url or self .container_id
505518
519+ def __bool__ (self ) -> bool :
520+ return True
521+
506522 @property
507523 def _build_tag (self ) -> str :
508524 """Internal build tag assigned to each immage, either the image url or
@@ -519,6 +535,18 @@ def local_image(self) -> bool:
519535 """
520536 return self ._is_local
521537
538+ @property
539+ def marks (self ) -> Collection [Union [MarkDecorator , Mark ]]:
540+ return self ._marks
541+
542+ @property
543+ def values (self ) -> Tuple [Self , ...]:
544+ return (self ,)
545+
546+ @property
547+ def id (self ) -> str :
548+ return str (self )
549+
522550 def get_launch_cmd (
523551 self ,
524552 container_runtime : OciRuntimeBase ,
@@ -656,8 +684,25 @@ def baseurl(self) -> Optional[str]:
656684 """
657685
658686
687+ class _HackMROMeta (ABCMeta ):
688+ def mro (cls ):
689+ return (
690+ cls ,
691+ ContainerBase ,
692+ ContainerBaseABC ,
693+ tuple ,
694+ _pytest .mark .ParameterSet ,
695+ object ,
696+ )
697+
698+
659699@dataclass (unsafe_hash = True )
660- class Container (ContainerBase , ContainerBaseABC ):
700+ class Container (
701+ ContainerBase ,
702+ ContainerBaseABC ,
703+ _pytest .mark .ParameterSet ,
704+ metaclass = _HackMROMeta ,
705+ ):
661706 """This class stores information about the Container Image under test."""
662707
663708 def pull_container (self , container_runtime : OciRuntimeBase ) -> None :
@@ -696,7 +741,12 @@ def baseurl(self) -> Optional[str]:
696741
697742
698743@dataclass (unsafe_hash = True )
699- class DerivedContainer (ContainerBase , ContainerBaseABC ):
744+ class DerivedContainer (
745+ ContainerBase ,
746+ ContainerBaseABC ,
747+ _pytest .mark .ParameterSet ,
748+ metaclass = _HackMROMeta ,
749+ ):
700750 """Class for storing information about the Container Image under test, that
701751 is build from a :file:`Containerfile`/:file:`Dockerfile` from a different
702752 image (can be any image from a registry or an instance of
@@ -723,6 +773,23 @@ class DerivedContainer(ContainerBase, ContainerBaseABC):
723773 #: has been built
724774 add_build_tags : List [str ] = field (default_factory = list )
725775
776+ @staticmethod
777+ def _get_recursive_marks (
778+ ctr : Union [Container , "DerivedContainer" , str ]
779+ ) -> Collection [Union [MarkDecorator , Mark ]]:
780+ if isinstance (ctr , str ):
781+ return []
782+ if isinstance (ctr , Container ):
783+ return ctr ._marks
784+
785+ return tuple (ctr ._marks ) + tuple (
786+ DerivedContainer ._get_recursive_marks (ctr .base )
787+ )
788+
789+ @property
790+ def marks (self ) -> Collection [Union [MarkDecorator , Mark ]]:
791+ return DerivedContainer ._get_recursive_marks (self )
792+
726793 def __post_init__ (self ) -> None :
727794 super ().__post_init__ ()
728795 if not self .base :
0 commit comments