# Path: peru/cache.py
# def compute_key(data):
#     # To hash this dictionary of fields, serialize it as a JSON string, and
#     # take the SHA1 of that string. Dictionary key order is unspecified, so
#     # "sort_keys" keeps our hash stable. Specifying separators makes the
#     # JSON slightly more compact, and protects us against changes in the
#     # default.  "ensure_ascii" defaults to true, so specifying it just
#     # protects us from changes in the default.
#     json_representation = json.dumps(
#         data, sort_keys=True, ensure_ascii=True, separators=(',', ':'))
#     sha1 = hashlib.sha1()
#     sha1.update(json_representation.encode("utf8"))
#     return sha1.hexdigest()
# 
# Path: peru/edit_yaml.py
# def set_module_field_in_file(yaml_file_path, module_name, field_name, new_val):
#     with open(yaml_file_path) as f:
#         yaml_text = f.read()
#     new_yaml_text = set_module_field(yaml_text, module_name, field_name,
#                                      new_val)
#     with open(yaml_file_path, "w") as f:
#         f.write(new_yaml_text)
# 
# Path: peru/plugin.py
# async def plugin_fetch(plugin_context, module_type, module_fields, dest,
#                        display_handle):
#     env = {'PERU_SYNC_DEST': dest}
#     await _plugin_job(plugin_context, module_type, module_fields, 'sync', env,
#                       display_handle)
# 
# async def plugin_get_reup_fields(plugin_context, module_type, module_fields,
#                                  display_handle):
#     with tmp_dir(plugin_context) as output_file_dir:
#         output_path = os.path.join(output_file_dir, 'reup_output')
#         env = {'PERU_REUP_OUTPUT': output_path}
#         await _plugin_job(plugin_context, module_type, module_fields, 'reup',
#                           env, display_handle)
#         with open(output_path) as output_file:
#             fields = yaml.safe_load(output_file) or {}
# 
#     for key, value in fields.items():
#         if not isinstance(key, str):
#             raise PluginModuleFieldError(
#                 'reup field name must be a string: {}'.format(key))
#         if not isinstance(value, str):
#             raise PluginModuleFieldError(
#                 'reup field value must be a string: {}'.format(value))
# 
#     return fields


import json
import os
import textwrap
from .cache import compute_key
from .error import PrintableError, error_context
from .edit_yaml import set_module_field_in_file
from . import imports
from .plugin import plugin_fetch, plugin_get_reup_fields
from . import scope
        from . import parser  # avoid circular imports
        self.recursion_specified = recursive is not None

    async def _get_base_tree(self, runtime):
        override_path = runtime.get_override(self.name)
        if override_path is not None:
            # Marking overrides as used lets us print a warning when an
            # override is unused.
            runtime.mark_override_used(self.name)
            override_tree = await self._get_override_tree(
                runtime, override_path)
            return override_tree

        key = compute_key({
            'type': self.type,
            'plugin_fields': self.plugin_fields,
            'peru_file': self.peru_file,
        })
        # Use a lock to prevent the same module from being double fetched. The
        # lock is taken on the cache key, not the module itself, so two
        # different modules with identical fields will take the same lock and
        # avoid double fetching.
        cache_key_lock = runtime.cache_key_locks[key]
        async with cache_key_lock:
            # Skip reading the cache if --no-cache is set. This is the only
            # place in the code we check that flag. Deterministic operations
            # like tree merging still get read from cache, because there's no
            # reason to redo them.
            if key in runtime.cache.keyval and not runtime.no_cache:
                return runtime.cache.keyval[key]
            with runtime.tmp_dir() as tmp_dir:

# Path: peru/cache.py
# def compute_key(data):
#     # To hash this dictionary of fields, serialize it as a JSON string, and
#     # take the SHA1 of that string. Dictionary key order is unspecified, so
#     # "sort_keys" keeps our hash stable. Specifying separators makes the
#     # JSON slightly more compact, and protects us against changes in the
#     # default.  "ensure_ascii" defaults to true, so specifying it just
#     # protects us from changes in the default.
#     json_representation = json.dumps(
#         data, sort_keys=True, ensure_ascii=True, separators=(',', ':'))
#     sha1 = hashlib.sha1()
#     sha1.update(json_representation.encode("utf8"))
#     return sha1.hexdigest()
# 
# Path: peru/edit_yaml.py
# def set_module_field_in_file(yaml_file_path, module_name, field_name, new_val):
#     with open(yaml_file_path) as f:
#         yaml_text = f.read()
#     new_yaml_text = set_module_field(yaml_text, module_name, field_name,
#                                      new_val)
#     with open(yaml_file_path, "w") as f:
#         f.write(new_yaml_text)
# 
# Path: peru/plugin.py
# async def plugin_fetch(plugin_context, module_type, module_fields, dest,
#                        display_handle):
#     env = {'PERU_SYNC_DEST': dest}
#     await _plugin_job(plugin_context, module_type, module_fields, 'sync', env,
#                       display_handle)
# 
# async def plugin_get_reup_fields(plugin_context, module_type, module_fields,
#                                  display_handle):
#     with tmp_dir(plugin_context) as output_file_dir:
#         output_path = os.path.join(output_file_dir, 'reup_output')
#         env = {'PERU_REUP_OUTPUT': output_path}
#         await _plugin_job(plugin_context, module_type, module_fields, 'reup',
#                           env, display_handle)
#         with open(output_path) as output_file:
#             fields = yaml.safe_load(output_file) or {}
# 
#     for key, value in fields.items():
#         if not isinstance(key, str):
#             raise PluginModuleFieldError(
#                 'reup field name must be a string: {}'.format(key))
#         if not isinstance(value, str):
#             raise PluginModuleFieldError(
#                 'reup field value must be a string: {}'.format(value))
# 
#     return fields


# Path: peru/module.py
import json
import os
import textwrap
from .cache import compute_key
from .error import PrintableError, error_context
from .edit_yaml import set_module_field_in_file
from . import imports
from .plugin import plugin_fetch, plugin_get_reup_fields
from . import scope
        from . import parser  # avoid circular imports

        self.recursion_specified = recursive is not None

    async def _get_base_tree(self, runtime):
        override_path = runtime.get_override(self.name)
        if override_path is not None:
            # Marking overrides as used lets us print a warning when an
            # override is unused.
            runtime.mark_override_used(self.name)
            override_tree = await self._get_override_tree(
                runtime, override_path)
            return override_tree

        key = compute_key({
            'type': self.type,
            'plugin_fields': self.plugin_fields,
            'peru_file': self.peru_file,
        })
        # Use a lock to prevent the same module from being double fetched. The
        # lock is taken on the cache key, not the module itself, so two
        # different modules with identical fields will take the same lock and
        # avoid double fetching.
        cache_key_lock = runtime.cache_key_locks[key]
        async with cache_key_lock:
            # Skip reading the cache if --no-cache is set. This is the only
            # place in the code we check that flag. Deterministic operations
            # like tree merging still get read from cache, because there's no
            # reason to redo them.
            if key in runtime.cache.keyval and not runtime.no_cache:
                return runtime.cache.keyval[key]
            with runtime.tmp_dir() as tmp_dir:

                await plugin_fetch(runtime.get_plugin_context(), self.type,