# 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

        bar = Target(
            name='bar',
            inputs=foo.outputs['C'],
            outputs='result',
        )

    The target can also specify an *options* dictionary specifying the
    resources needed to run the target. The options are consumed by the backend
    and may be ignored if the backend doesn't support a given option. For
    example, we can set the *cores* option to set the number of cores that the
    target uses::

        Target('Foo', inputs=[], outputs=[], options={'cores': 16}, working_dir='/tmp')

    To see which options are supported by your backend of choice, see the
    documentation for the backend.

    :ivar str name:
        Name of the target.

    .. versionchanged:: 1.6.0
        Named inputs and outputs were added. Prior versions require *inputs*
        and *outputs* to be lists.
    """

    def __init__(
        self, name, inputs, outputs, options, working_dir=None, spec="", protect=None
    ):
        self.name = name

# 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


        bar = Target(
            name='bar',
            inputs=foo.outputs['C'],
            outputs='result',
        )

    The target can also specify an *options* dictionary specifying the
    resources needed to run the target. The options are consumed by the backend
    and may be ignored if the backend doesn't support a given option. For
    example, we can set the *cores* option to set the number of cores that the
    target uses::

        Target('Foo', inputs=[], outputs=[], options={'cores': 16}, working_dir='/tmp')

    To see which options are supported by your backend of choice, see the
    documentation for the backend.

    :ivar str name:
        Name of the target.

    .. versionchanged:: 1.6.0
        Named inputs and outputs were added. Prior versions require *inputs*
        and *outputs* to be lists.
    """

    def __init__(
        self, name, inputs, outputs, options, working_dir=None, spec="", protect=None
    ):
        self.name = name

        if not is_valid_name(self.name):