<commit_before>##############################################################################
# Copyright (c) 2013-2015, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Written by <NAME>, <EMAIL>, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://scalability-llnl.github.io/spack
# Please also see the LICENSE file for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License (as published by
# the Free Software Foundation) version 2.1 dated February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import os
import time
import socket

from external import yaml
from external.yaml.error import MarkedYAMLError, YAMLError

import llnl.util.tty as tty
from llnl.util.filesystem import *
from llnl.util.lock import Lock

import spack.spec
from spack.version import Version
from spack.spec import Spec
from spack.error import SpackError

# DB goes in this directory underneath the root
_db_dirname = '.spack-db'

# DB version.  This is stuck in the DB file to track changes in format.
_db_version = Version('0.9')


def _autospec(function):
    """Decorator that automatically converts the argument of a single-arg
       function to a Spec."""
    def converter(self, spec_like, *args, **kwargs):
        if not isinstance(spec_like, spack.spec.Spec):
            spec_like = spack.spec.Spec(spec_like)
        return function(self, spec_like, *args, **kwargs)
    return converter


class InstallRecord(object):
    """A record represents one installation in the DB."""
    def __init__(self, spec, path):
        self.spec = spec
        self.path = path

    def to_dict(self):
        return { 'spec' : self.spec.to_node_dict(),
                 'path' : self.path }

    @classmethod
    def from_dict(cls, d):
        return InstallRecord(d['spec'], d['path'])


class Database(object):
    def __init__(self, root):
        """Create an empty Database.

        Location defaults to root/_index.yaml
        The individual data are dicts containing
        spec: the top level spec of a package
        path: the path to the install of that package
        dep_hash: a hash of the dependence DAG for that package
        """
        self._root = root

        # Set up layout of database files.
        self._db_dir     = join_path(self._root, _db_dirname)
        self._index_path = join_path(self._db_dir, 'index.yaml')
        self._lock_path  = join_path(self._db_dir, 'lock')

        # Create needed directories and files
        if not os.path.exists(self._db_dir):
            mkdirp(self._db_dir)

        if not os.path.exists(self._lock_path):
            touch(self._lock_path)

        # initialize rest of state.
        self.lock = Lock(self._lock_path)
        self._data = {}
        self._last_write_time = 0


    def write_lock(self):
        """Get a write lock context for use in a `with` block."""
        return self.lock.write_lock()


    def read_lock(self):
        """Get a read lock context for use in a `with` block."""
        return self.lock.read_lock()


    def _write_to_yaml(self, stream):
        """Write out the databsae to a YAML file."""
        # map from per-spec hash code to installation record.
        installs = dict((k, v.to_dict()) for k, v in self._data.items())

        # databaes includes installation list and version.

        # NOTE: this DB version does not handle multiple installs of
        # the same spec well.  If there are 2 identical specs with
        # different paths, it can't differentiate.
        # TODO: fix this before we support multiple install locations.
        database = {<commit_msg>Allow custom timeout for database locking.<commit_after>##############################################################################
# Copyright (c) 2013-2015, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Written by <NAME>, <EMAIL>, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://scalability-llnl.github.io/spack
# Please also see the LICENSE file for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License (as published by
# the Free Software Foundation) version 2.1 dated February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import os
import time
import socket

from external import yaml
from external.yaml.error import MarkedYAMLError, YAMLError

import llnl.util.tty as tty
from llnl.util.filesystem import *
from llnl.util.lock import Lock

import spack.spec
from spack.version import Version
from spack.spec import Spec
from spack.error import SpackError

# DB goes in this directory underneath the root
_db_dirname = '.spack-db'

# DB version.  This is stuck in the DB file to track changes in format.
_db_version = Version('0.9')

# Default timeout for spack database locks is 5 min.
_db_lock_timeout = 300

def _autospec(function):
    """Decorator that automatically converts the argument of a single-arg
       function to a Spec."""
    def converter(self, spec_like, *args, **kwargs):
        if not isinstance(spec_like, spack.spec.Spec):
            spec_like = spack.spec.Spec(spec_like)
        return function(self, spec_like, *args, **kwargs)
    return converter


class InstallRecord(object):
    """A record represents one installation in the DB."""
    def __init__(self, spec, path):
        self.spec = spec
        self.path = path

    def to_dict(self):
        return { 'spec' : self.spec.to_node_dict(),
                 'path' : self.path }

    @classmethod
    def from_dict(cls, d):
        return InstallRecord(d['spec'], d['path'])


class Database(object):
    def __init__(self, root):
        """Create an empty Database.

        Location defaults to root/_index.yaml
        The individual data are dicts containing
        spec: the top level spec of a package
        path: the path to the install of that package
        dep_hash: a hash of the dependence DAG for that package
        """
        self._root = root

        # Set up layout of database files.
        self._db_dir     = join_path(self._root, _db_dirname)
        self._index_path = join_path(self._db_dir, 'index.yaml')
        self._lock_path  = join_path(self._db_dir, 'lock')

        # Create needed directories and files
        if not os.path.exists(self._db_dir):
            mkdirp(self._db_dir)

        if not os.path.exists(self._lock_path):
            touch(self._lock_path)

        # initialize rest of state.
        self.lock = Lock(self._lock_path)
        self._data = {}
        self._last_write_time = 0


    def write_lock(self, timeout=_db_lock_timeout):
        """Get a write lock context for use in a `with` block."""
        return self.lock.write_lock(timeout)


    def read_lock(self, timeout=_db_lock_timeout):
        """Get a read lock context for use in a `with` block."""
        return self.lock.read_lock(timeout)


    def _write_to_yaml(self, stream):
        """Write out the databsae to a YAML file."""
        # map from per-spec hash code to installation record.
        installs = dict((k, v.to_dict()) for k, v in self._data.items())

        # databaes includes installation list and version.

        # NOTE: this DB version does not handle multiple installs of
        # the same spec well.  If there are 2 identical specs with
        # different paths, it can't differentiate.
        # TODO: fix this before we support multiple install locations.
        database = {
{'fraction_non_alphanumeric': 0.11482707453487045, 'fraction_numerical': 0.009131377696609976, 'mean_word_length': 3.0772452303396927, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4483062', 'n_tokens_mistral': 2475, 'n_tokens_neox': 2338, 'n_words': 962}