<commit_before># Copyright (c) 2014, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.

import logging
import re
from abc import ABCMeta, abstractmethod
import json
import inspect
import hashlib

from .assignment import Assignment


# decorator for methods that assume assignments have been made
def requires_assignment(f):
  def wrapped_f(self, *args, **kwargs):
    if not self._assigned:
      self._assign()
    return f(self, *args, **kwargs)
  return wrapped_f

# decorator for methods that should be exposure logged
def exposure_logged(f):
  def wrapped_f(self, *args, **kwargs):
    if self._auto_exposure_log and self.in_experiment and not self.logged:
      self.log_exposure()
    return f(self, *args, **kwargs)
  return wrapped_f

class Experiment(object):
  """Abstract base class for PlanOut experiments"""
  __metaclass__ = ABCMeta

  logger_configured = False

  def __init__(self, **inputs):
    self.inputs = inputs       # input data
    self._logged = False       # True when assignments have been exposure logged
    self._salt = None          # Experiment-level salt
    self.in_experiment = True

    # use the name of the class as the default name
    self._name = self.__class__.__name__

    # auto-exposure logging is enabled by default
    self._auto_exposure_log = True

    self.setup_attributes()          # sets name, salt, etc.

    self._assignment = self.get_assignment()
    self._checksum = self.checksum()
    self._assigned = False

  def _assign(self):
    """Assignment and setup that only happens when we need to log data"""
    self.configure_logger() # sets up loggers
    self.assign(self._assignment, **self.inputs)
    self.in_experiment = \
      self._assignment.get('in_experiment', self.in_experiment)
    # check if inputs+params were previously logged
    self._logged = self.previously_logged()

  def setup_attributes(self):
    """Set experiment attributes, e.g., experiment name and salt."""
    # If the experiment name is not specified, just use the class name
    pass

  def get_assignment(self):
    return Assignment(self.salt)

  @property
  def salt(self):
    # use the experiment name as the salt if the salt is not set
    return self._salt if self._salt else self.name

  @salt.setter
  def salt(self, value):
    self._salt = value

  @property
  def name(self):
    return self._name

  @name.setter
  def name(self, value):
    self._name = re.sub(r'\s+', '-', value)

  @abstractmethod
  def assign(params, **kwargs):
    """Returns evaluated PlanOut mapper with experiment assignment"""
    pass

  @requires_assignment
  def __asBlob(self, extras={}):
    """Dictionary representation of experiment data"""
    d = {
      'name': self.name,
      'salt': self.salt,
      'inputs': self.inputs,
      'params': dict(self._assignment),
    }
    for k in extras:
      d[k] = extras[k]
    if self._checksum:
      d['checksum'] = self._checksum
    return d

  def checksum(self):
    # src doesn't count first line of code, which includes function name
    src = ''.join(inspect.getsourcelines(self.assign)[0][1:])
    return hashlib.sha1(src).hexdigest()[:8]

  # the logged setter / getter may be unnecessary
  @property
  def logged(self):
    return self._logged

  @logged.setter
  def logged(self, value):<commit_msg>special case checksum if running in interpreter<commit_after># Copyright (c) 2014, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.

import logging
import re
from abc import ABCMeta, abstractmethod
import json
import inspect
import hashlib
import __main__ as main

from .assignment import Assignment


# decorator for methods that assume assignments have been made
def requires_assignment(f):
  def wrapped_f(self, *args, **kwargs):
    if not self._assigned:
      self._assign()
    return f(self, *args, **kwargs)
  return wrapped_f

# decorator for methods that should be exposure logged
def exposure_logged(f):
  def wrapped_f(self, *args, **kwargs):
    if self._auto_exposure_log and self.in_experiment and not self.logged:
      self.log_exposure()
    return f(self, *args, **kwargs)
  return wrapped_f

class Experiment(object):
  """Abstract base class for PlanOut experiments"""
  __metaclass__ = ABCMeta

  logger_configured = False

  def __init__(self, **inputs):
    self.inputs = inputs       # input data
    self._logged = False       # True when assignments have been exposure logged
    self._salt = None          # Experiment-level salt
    self.in_experiment = True

    # use the name of the class as the default name
    self._name = self.__class__.__name__

    # auto-exposure logging is enabled by default
    self._auto_exposure_log = True

    self.setup_attributes()          # sets name, salt, etc.

    self._assignment = self.get_assignment()
    self._checksum = self.checksum()
    self._assigned = False

  def _assign(self):
    """Assignment and setup that only happens when we need to log data"""
    self.configure_logger() # sets up loggers
    self.assign(self._assignment, **self.inputs)
    self.in_experiment = \
      self._assignment.get('in_experiment', self.in_experiment)
    # check if inputs+params were previously logged
    self._logged = self.previously_logged()

  def setup_attributes(self):
    """Set experiment attributes, e.g., experiment name and salt."""
    # If the experiment name is not specified, just use the class name
    pass

  def get_assignment(self):
    return Assignment(self.salt)

  @property
  def salt(self):
    # use the experiment name as the salt if the salt is not set
    return self._salt if self._salt else self.name

  @salt.setter
  def salt(self, value):
    self._salt = value

  @property
  def name(self):
    return self._name

  @name.setter
  def name(self, value):
    self._name = re.sub(r'\s+', '-', value)

  @abstractmethod
  def assign(params, **kwargs):
    """Returns evaluated PlanOut mapper with experiment assignment"""
    pass

  @requires_assignment
  def __asBlob(self, extras={}):
    """Dictionary representation of experiment data"""
    d = {
      'name': self.name,
      'salt': self.salt,
      'inputs': self.inputs,
      'params': dict(self._assignment),
    }
    for k in extras:
      d[k] = extras[k]
    if self._checksum:
      d['checksum'] = self._checksum
    return d

  def checksum(self):
    # if we're running from a file and want to detect if the experiment file has changed
    if hasattr(main, '__file__'):
      # src doesn't count first line of code, which includes function name
      src = ''.join(inspect.getsourcelines(self.assign)[0][1:])
      return hashlib.sha1(src).hexdigest()[:8]
    # if we're running in an interpreter, don't worry about it
    else:
      return None

  # the logged setter / getter may be unnecessary
  @property
  def logged(self):
    return self._logged

  @logged.setter
  def logged(self, value):
{'fraction_non_alphanumeric': 0.09425444596443229, 'fraction_numerical': 0.002188782489740082, 'mean_word_length': 3.333728512151749, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6775928', 'n_tokens_mistral': 2215, 'n_tokens_neox': 2115, 'n_words': 757}