# 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
    @timer("Checked for circular dependencies in %.3fms", logger=logger)
    def _check_for_circular_dependencies(self):
        """Check for circular dependencies in the graph.

        Raises :class:`WorkflowError` if a circular dependency is found.
        """
        logger.debug("Checking for circular dependencies")

        fresh, started, done = 0, 1, 2

        nodes = self.targets.values()
        state = dict((n, fresh) for n in nodes)

        def visitor(node):
            state[node] = started
            for dep in self.dependencies[node]:
                if state[dep] == started:
                    raise WorkflowError("Target {} depends on itself.".format(node))
                elif state[dep] == fresh:
                    visitor(dep)
            state[node] = done

        for node in nodes:
            if state[node] == fresh:
                visitor(node)

    def endpoints(self):
        """Return a set of all targets that are not depended on by other targets."""
        return set(self.targets.values()) - set(self.dependents.keys())


# 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

    @timer("Checked for circular dependencies in %.3fms", logger=logger)
    def _check_for_circular_dependencies(self):
        """Check for circular dependencies in the graph.

        Raises :class:`WorkflowError` if a circular dependency is found.
        """
        logger.debug("Checking for circular dependencies")

        fresh, started, done = 0, 1, 2

        nodes = self.targets.values()
        state = dict((n, fresh) for n in nodes)

        def visitor(node):
            state[node] = started
            for dep in self.dependencies[node]:
                if state[dep] == started:
                    raise WorkflowError("Target {} depends on itself.".format(node))
                elif state[dep] == fresh:
                    visitor(dep)
            state[node] = done

        for node in nodes:
            if state[node] == fresh:
                visitor(node)

    def endpoints(self):
        """Return a set of all targets that are not depended on by other targets."""
        return set(self.targets.values()) - set(self.dependents.keys())


    @cache