# Path: src/gwf/backends/base.py
# class Status(Enum):
#     """Status of a target.
# 
#     A target is unknown to the backend if it has not been submitted or the
#     target has completed and thus isn't being tracked anymore by the backend.
# 
#     A target is submitted if it has been successfully submitted to the backend
#     and is pending execution.
# 
#     A target is running if it is currently being executed by the backend.
#     """
# 
#     UNKNOWN = 0
#     SUBMITTED = 1
#     RUNNING = 2
# 
# Path: src/gwf/utils.py
# def cache(obj):
#     _cache = obj._cache = {}
# 
#     @functools.wraps(obj)
#     def memoizer(*args, **kwargs):
#         if args not in _cache:
#             _cache[args] = obj(*args, **kwargs)
#         return _cache[args]
# 
#     return memoizer
# 
# def is_valid_name(candidate):
#     """Check whether `candidate` is a valid name for a target or workflow."""
#     return re.match(r"^[a-zA-Z_][a-zA-Z0-9._]*$", candidate) is not None
# 
# class timer(ContextDecorator):
#     def __init__(self, msg, logger=None):
#         self.msg = msg
#         self.logger = logger or logging.getLogger(__name__)
# 
#     def __enter__(self):
#         self.start = time.perf_counter()
#         return self
# 
#     def __exit__(self, *args):
#         self.end = time.perf_counter()
#         self.duration = self.end - self.start
#         self.logger.debug(self.msg, self.duration)


import collections
import logging
import os
import os.path
import unicodedata
from collections import defaultdict
from enum import Enum
from .backends import Status
from .compat import fspath
from .exceptions import NameError, WorkflowError
from .utils import cache, is_valid_name, timer

        self._check_for_circular_dependencies()

    @classmethod
    def from_targets(cls, targets):
        """Construct a dependency graph from a set of targets.

        When a graph is initialized it computes all dependency relations
        between targets, ensuring that the graph is semantically sane.
        Therefore, construction of the graph is an expensive operation which
        may raise a number of exceptions:

        :raises gwf.exceptions.FileProvidedByMultipleTargetsError:
            Raised if the same file is provided by multiple targets.

        Since this method initializes the graph, it may also raise:

        :raises gwf.exceptions.WorkflowError:
            Raised if the workflow contains a circular dependency.
        """
        provides = {}
        unresolved = set()
        dependencies = defaultdict(set)
        dependents = defaultdict(set)

        logger.debug("Building dependency graph from %d targets", len(targets))

        if isinstance(targets, dict):
            targets = targets.values()


# Path: src/gwf/backends/base.py
# class Status(Enum):
#     """Status of a target.
# 
#     A target is unknown to the backend if it has not been submitted or the
#     target has completed and thus isn't being tracked anymore by the backend.
# 
#     A target is submitted if it has been successfully submitted to the backend
#     and is pending execution.
# 
#     A target is running if it is currently being executed by the backend.
#     """
# 
#     UNKNOWN = 0
#     SUBMITTED = 1
#     RUNNING = 2
# 
# Path: src/gwf/utils.py
# def cache(obj):
#     _cache = obj._cache = {}
# 
#     @functools.wraps(obj)
#     def memoizer(*args, **kwargs):
#         if args not in _cache:
#             _cache[args] = obj(*args, **kwargs)
#         return _cache[args]
# 
#     return memoizer
# 
# def is_valid_name(candidate):
#     """Check whether `candidate` is a valid name for a target or workflow."""
#     return re.match(r"^[a-zA-Z_][a-zA-Z0-9._]*$", candidate) is not None
# 
# class timer(ContextDecorator):
#     def __init__(self, msg, logger=None):
#         self.msg = msg
#         self.logger = logger or logging.getLogger(__name__)
# 
#     def __enter__(self):
#         self.start = time.perf_counter()
#         return self
# 
#     def __exit__(self, *args):
#         self.end = time.perf_counter()
#         self.duration = self.end - self.start
#         self.logger.debug(self.msg, self.duration)


# Path: src/gwf/core.py
import collections
import logging
import os
import os.path
import unicodedata
from collections import defaultdict
from enum import Enum
from .backends import Status
from .compat import fspath
from .exceptions import NameError, WorkflowError
from .utils import cache, is_valid_name, timer


        self._check_for_circular_dependencies()

    @classmethod
    def from_targets(cls, targets):
        """Construct a dependency graph from a set of targets.

        When a graph is initialized it computes all dependency relations
        between targets, ensuring that the graph is semantically sane.
        Therefore, construction of the graph is an expensive operation which
        may raise a number of exceptions:

        :raises gwf.exceptions.FileProvidedByMultipleTargetsError:
            Raised if the same file is provided by multiple targets.

        Since this method initializes the graph, it may also raise:

        :raises gwf.exceptions.WorkflowError:
            Raised if the workflow contains a circular dependency.
        """
        provides = {}
        unresolved = set()
        dependencies = defaultdict(set)
        dependents = defaultdict(set)

        logger.debug("Building dependency graph from %d targets", len(targets))

        if isinstance(targets, dict):
            targets = targets.values()


        with timer("Built dependency graph in %.3fms", logger=logger):