<commit_before># -*- coding: utf-8 -*-
"""
Exceptions and warnings.
"""

__all__ = ['NoWriteNecessary', 'CauldronWarning', 'CauldronException', 'CauldronAPINotImplemented', 'ServiceNotStarted']

try:
    from DFW.Keyword import NoWriteNecessary
except ImportError:
    class NoWriteNecessary(Exception):
        """Raised to cancel a keyword write in progress."""
        pass

try:
    from DFW.Keyword import WrongDispatcher
except ImportError:
    class WrongDispatcher(ValueError):
        """Error raised for incorrect dispatchers in services."""
        pass

class CauldronException(Exception):
    """A base class to collect Cauldron Exceptions & Warnings."""
    pass

class CauldronWarning(CauldronException, RuntimeWarning):
    """A base class for all Cauldron warnings."""
    pass

class CauldronAPINotImplemented(CauldronException, NotImplementedError):
    """Exception raised when an API feature is not implemented."""
    pass

class ServiceNotStarted(CauldronException, KeyError):
    """Exception raised when starting a client which requires a dispatcher, and the dispatcher has not started."""
    pass

class CauldronAPINotImplementedWarning(CauldronWarning, CauldronAPINotImplemented):
    """Warning raised to indicate that an API feature is not implemented, and so was silently ignored."""
    pass

class CauldronXMLWarning(CauldronWarning):
    """Warning raised due to the non-strict use of XML in non-standard KTL backends."""
    pass

class DispatcherError(CauldronException):
    """Raised when something went wrong with the dispatcher."""
    pass

class ConfigurationMissing(CauldronWarning):
    """An exception raised when a configuration item is missing."""
    pass

<commit_msg>Recategorizing warnings and exceptions.<commit_after># -*- coding: utf-8 -*-
"""
Exceptions and warnings.
"""

__all__ = ['NoWriteNecessary', 'CauldronWarning', 'CauldronException', 'CauldronAPINotImplemented', 'ServiceNotStarted']

try:
    from DFW.Keyword import NoWriteNecessary
except ImportError:
    class NoWriteNecessary(Exception):
        """Raised to cancel a keyword write in progress."""
        pass

try:
    from DFW.Keyword import WrongDispatcher
except ImportError:
    class WrongDispatcher(ValueError):
        """Error raised for incorrect dispatchers in services."""
        pass

class CauldronException(Exception):
    """A base class to collect Cauldron Exceptions & Warnings."""
    pass

class CauldronWarning(Warning):
    """A base class for all Cauldron warnings."""
    pass

class CauldronAPINotImplemented(CauldronException, NotImplementedError):
    """Exception raised when an API feature is not implemented."""
    pass

class ServiceNotStarted(CauldronException, KeyError):
    """Exception raised when starting a client which requires a dispatcher, and the dispatcher has not started."""
    pass

class CauldronAPINotImplementedWarning(CauldronWarning):
    """Warning raised to indicate that an API feature is not implemented, and so was silently ignored."""
    pass

class CauldronXMLWarning(CauldronWarning):
    """Warning raised due to the non-strict use of XML in non-standard KTL backends."""
    pass

class DispatcherError(CauldronException):
    """Raised when something went wrong with the dispatcher."""
    pass

class ConfigurationMissing(CauldronWarning):
    """An exception raised when a configuration item is missing."""
    pass

<|endoftext|>


<commit_before># encoding: utf8
# This module provides shared datastructures for the hexlite engine.

# HEXLite Python-based solver for a fragment of HEX
# Copyright (C) 2017-2019  Peter Schueller <schueller.p@gmail.com>
#
# 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, either version 3 of the License, or
# (at your option) any later version.
#
# 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
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import sys, time, collections, logging, argparse, json

class Configuration:
  def __init__(self):
    # whether to produce verbose output
    self.verbose = False
    # whether to produce debug output
    self.debug = False
    # maxint setting (for compatibility with dlvhex, actually not necessary here)
    self.maxint = 0
    # which type of FLP check to use, or 'none'
    self.flpcheck = 'explicit'
    # number of answer sets to enumerate (0 = all)
    self.number = 0
    # whether to suppress facts in output
    self.nofacts = False
    # whether to give auxiliary facts in output
    self.auxfacts = False
    # whether to output stats as json lines on stderr
    self.stats = False
    # whether to enable a generic cache for external atom calls
    self.enable_generic_eatom_cache = True
    # whether to enable nogoods specified by external atoms (if false, these nogoods are just ignored)
    self.enable_eatom_specified_nogoods = True
    # whether to check before external atom evaluations if a nogood determines the result, and if yes, skip the evaluation
    self.consider_skipping_evaluation_if_nogood_determines_truth = True
    # additional arguments for backend (currently directly given to clingo)
    self.backend_additional_args = []

  def add_common_arguments(self, parser):
    assert(isinstance(parser, argparse.ArgumentParser))
    parser.add_argument('--liberalsafety', action='store_true', default=False,
      help='Whether liberal safety is requested (ignored).')
    parser.add_argument('--strongnegation-enable', action='store_true', default=False,
      help='Whether strong negation is enabled (ignored).')
    parser.add_argument('--flpcheck', choices=['explicit', 'none'], action='store', default='explicit',
      help='Which type of FLP check to use (explicit FLP check currently does not work for strong negation and optimization).')
    parser.add_argument('-n', '--number', metavar='N', action='store', default=0,
      help='Number of models to enumerate.')
    parser.add_argument('-N', '--maxint', metavar='N', action='store', default=None,
      help='Maximum integer (#maxint in the program can override this).')
    parser.add_argument('--nofacts', action='store_true', default=False,
      help='Whether to output given facts in answer set.')
    parser.add_argument('--auxfacts', action='store_true', default=False,
      help='Whether to output auxiliary facts in answer set.')
    parser.add_argument('--backend_arg', nargs=1, metavar='ARGUMENT', action='append', default=[],
      help='Argument to pass to backend. Can be given multiple times to pass multiple arguments.')
    parser.add_argument('--nocache', action='store_true', default=False,
      help='Disable caching of external atom calls.')
    parser.add_argument('--noeatomlearn', action='store_true', default=False,
      help='Disable processing of nogoods that are generated by external computations.')
    parser.add_argument('--noskipevalfromnogoods', action='store_true', default=False,
      help='Disable skipping of external evaluation based on existing nogoods provided by the external computation. (Use this if you are not sure if eatom nogoods mess up the search space.)')
    parser.add_argument('--verbose', action='store_true', default=False, help='Activate verbose mode.')
    parser.add_argument('--debug', action='store_true', default=False, help='Activate debugging mode.')
    parser.add_argument('--stats', action='store_true', default=False, help='Activate statistics output as JSON on stdout.')

  def setupLogging(self):
    level = logging.WARNING
    if self.verbose:
      level = logging.INFO
    if self.debug:
      level = logging.DEBUG
    # call only once
    logging.getLogger().setLevel(level)

  def process_arguments(self, args):
    self.verbose = args.verbose
    self.debug = args.debug
    self.stats = args.stats
    self.setupLogging()
    self.backend_additional_args = args.backend_arg
    if len(self.backend_additional_args) > 0:
      logging.info("passing additional arguments to backend: "+repr(self.backend_additional_args))
    if args.liberalsafety:
      logging.warning("ignored argument about liberal safety")
    if args.strongnegation_enable:
      logging.warning("ignored argument about strong negation")
    if args.nocache:
      self.enable_generic_eatom_cache = False
    if args.noeatomlearn:
      self.enable_eatom_specified_nogoods = False
    if args.noskipevalfromnogoods:
      self.consider_skipping_evaluation_if_nogood_determines_truth = False
    try:
      if args.maxint:
        self.maxint = int(args.maxint)
    except:
      raise ValueError("faulty maxint argument '{}'".format(args.maxint))
    if self.flpcheck not in ['explicit', 'none']:
      raise ValueError("invalid flpcheck setting '{}'".format(self.flpcheck))
    self.flpcheck = args.flpcheck
    try:
      if args.number:
        self.number = int(args.number)
    except:
      raise ValueError("faulty number argument '{}'".format(args.number))
    self.nofacts = args.nofacts
    self.auxfacts = args.auxfacts

class Plugin:
  def __init__(self, mname, pmodule, arguments=None):
    self.mname = mname
    self.pmodule = pmodule
    self.arguments = arguments

class Statistics:
  '''
  collect statistics (real time, cpu time) in categories
  '''
  def __init__(self):
    self.initial = time.perf_counter(), time.process_time()
    # accumulation of times and counts used in certain categories (real time, cpu time, counter)
    self.categories = collections.defaultdict(lambda: [0.0, 0.0, 0])
    # sequence of categories currently being benchmarked
    # each nesting creates another entry at the end
    self.statstack = [ 'all' ]
    # latest time taken
    self.latest = self.initial

  # not to be used from outside
  def _timestamp(self, addtocategory, increment):
    current = time.perf_counter(), time.process_time()
    for i in [0, 1]:
      self.categories[addtocategory][i] += current[i] - self.latest[i]
    if increment:
      self.categories[addtocategory][2] += 1
    self.latest = current

  # not to be used from outside
  class _Closure:
    def __init__(self, stats, category):
      self.stats = stats
      self.category = category

    def __enter__(self):
      # count time difference to last timestamp for statstack [-1]
      self.stats._timestamp(self.stats.statstack[-1], increment=False)
      # add element to statstack
      self.stats.statstack.append(self.category)

    def __exit__(self, exc_type, exc_value, exc_tb):
      assert(self.stats.statstack[-1] == self.category)
      # count time difference to last timestamp for statstack [-1] and increment counter (for leaving the closure)
      self.stats._timestamp(self.stats.statstack[-1], increment=True)
      # remove element from statstack
      self.stats.statstack.pop()

  # do "with stats.context('category'): ..." (nesting is OK)
  def context(self, categoryname):
    return Statistics._Closure(self, categoryname)

  def accumulate(self):
    # accumulate some categories
    eatoms = [ v for k,v in self.categories.items() if k.startswith('eatom') ]
    return {
      'all.real': sum([ v[0] for v in self.categories.values() ]),
      'all.cpu': sum([ v[1] for v in self.categories.values() ]),

      'eatoms.real': sum([ v[0] for v in eatoms ]),
      'eatoms.cpu': sum([ v[1] for v in eatoms ]),
      'eatoms.count': sum([ v[2] for v in eatoms ]),
    }

  def display(self, name):
    # count time difference to last timestamp for statstack[-1] and also count this once
    self._timestamp(self.statstack[-1], increment=True)
    # accumulate stats
    accum = self.accumulate()
    # print stats
    sys.stderr.write(json.dumps({ 'event':'stats', 'name':name, 'stack': self.statstack, 'categories': dict(self.categories), 'accumulated': accum })+'\n')
    # decrement again in case we display() multiple times
    self.categories[self.statstack[-1]][2] -= 1

# statistics class that does nothing
class StatisticsDummy:
  def __init__(self):
    pass
  class _Closure:
    def __init__(self):
      pass
    def __enter__(self):
      pass
    def __exit__(self, exc_type, exc_value, exc_tb):
      pass

  def context(self, categoryname):
    return StatisticsDummy._Closure()
  def display(self, name):
    pass

class ProgramContext:
  '''
  program-global context
  * collects external atom signatures
  * remembers if maxint is given by program or by commandline
  * holds statistics
  '''
  def __init__(self):
    # key = eatomname, value = list of SignatureInfo
    self.eatoms = collections.defaultdict(set)
    self.wroteMaxint = False
    self.stats = StatisticsDummy()

  def addSignature(self, eatomname, relevancePred, replacementPred, arity):
   self.eatoms[eatomname].add(
      self.SignatureInfo(relevancePred, replacementPred, arity))

  class SignatureInfo:
    def __init__(self, relevancePred, replacementPred, arity):
      self.relevancePred = relevancePred
      self.replacementPred = replacementPred
      self.arity = arity

    def __hash__(self):
      return hash( (self.relevancePred, self.replacementPred, self.arity) )

    def __eq__(self, other):
      return (self.relevancePred == other.relevancePred) and (self.replacementPred == other.replacementPred) and (self.arity == other.arity)

    def __repr__(self):
      return str(self)

    def __str__(self):
      return "SignatureInfo(rel={},repl={},arity={})".format(self.relevancePred, self.replacementPred, self.arity)

def flatten(listoflists):
  return [x for y in listoflists for x in y]
<commit_msg>more documentation<commit_after># encoding: utf8
# This module provides shared datastructures for the hexlite engine.

# HEXLite Python-based solver for a fragment of HEX
# Copyright (C) 2017-2019  Peter Schueller <schueller.p@gmail.com>
#
# 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, either version 3 of the License, or
# (at your option) any later version.
#
# 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
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import sys, time, collections, logging, argparse, json

class Configuration:
  def __init__(self):
    # whether to produce verbose output
    self.verbose = False
    # whether to produce debug output
    self.debug = False
    # maxint setting (for compatibility with dlvhex, actually not necessary here)
    self.maxint = 0
    # which type of FLP check to use, or 'none'
    self.flpcheck = 'explicit'
    # number of answer sets to enumerate (0 = all)
    self.number = 0
    # whether to suppress facts in output
    self.nofacts = False
    # whether to give auxiliary facts in output
    self.auxfacts = False
    # whether to output stats as json lines on stderr
    self.stats = False
    # whether to enable a generic cache for external atom calls
    self.enable_generic_eatom_cache = True
    # whether to enable nogoods specified by external atoms (if false, these nogoods are just ignored)
    self.enable_eatom_specified_nogoods = True
    # whether to check before external atom evaluations if a nogood determines the result, and if yes, skip the evaluation
    self.consider_skipping_evaluation_if_nogood_determines_truth = True
    # additional arguments for backend (currently directly given to clingo)
    self.backend_additional_args = []

  def add_common_arguments(self, parser):
    assert(isinstance(parser, argparse.ArgumentParser))
    parser.add_argument('--liberalsafety', action='store_true', default=False,
      help='Whether liberal safety is requested (ignored).')
    parser.add_argument('--strongnegation-enable', action='store_true', default=False,
      help='Whether strong negation is enabled (ignored).')
    parser.add_argument('--flpcheck', choices=['explicit', 'none'], action='store', default='explicit',
      help='Which type of FLP check to use (explicit FLP check currently does not work for strong negation and optimization).')
    parser.add_argument('-n', '--number', metavar='N', action='store', default=0,
      help='Number of models to enumerate.')
    parser.add_argument('-N', '--maxint', metavar='N', action='store', default=None,
      help='Maximum integer (#maxint in the program can override this).')
    parser.add_argument('--nofacts', action='store_true', default=False,
      help='Whether to output given facts in answer set.')
    parser.add_argument('--auxfacts', action='store_true', default=False,
      help='Whether to output auxiliary facts in answer set.')
    parser.add_argument('--backend_arg', nargs=1, metavar='ARGUMENT', action='append', default=[],
      help='Argument to pass to backend. Can be given multiple times to pass multiple arguments.')
    parser.add_argument('--nocache', action='store_true', default=False,
      help='Disable caching of external atom calls.')
    parser.add_argument('--noeatomlearn', action='store_true', default=False,
      help='Disable processing of nogoods that are generated by external computations.')
    parser.add_argument('--noskipevalfromnogoods', action='store_true', default=False,
      help='Disable skipping of external evaluation based on existing nogoods provided by the external computation. (Use this if you are not sure if eatom nogoods mess up the search space.)')
    parser.add_argument('--verbose', action='store_true', default=False, help='Activate verbose mode.')
    parser.add_argument('--debug', action='store_true', default=False, help='Activate debugging mode.')
    parser.add_argument('--stats', action='store_true', default=False, help='Activate statistics output as JSON on stdout.')

  def setupLogging(self):
    level = logging.WARNING
    if self.verbose:
      level = logging.INFO
    if self.debug:
      level = logging.DEBUG
    # call only once
    logging.getLogger().setLevel(level)

  def process_arguments(self, args):
    self.verbose = args.verbose
    self.debug = args.debug
    self.stats = args.stats
    self.setupLogging()
    self.backend_additional_args = args.backend_arg
    if len(self.backend_additional_args) > 0:
      logging.info("passing additional arguments to backend: "+repr(self.backend_additional_args))
    if args.liberalsafety:
      logging.warning("ignored argument about liberal safety")
    if args.strongnegation_enable:
      logging.warning("ignored argument about strong negation")
    if args.nocache:
      self.enable_generic_eatom_cache = False
    if args.noeatomlearn:
      self.enable_eatom_specified_nogoods = False
    if args.noskipevalfromnogoods:
      self.consider_skipping_evaluation_if_nogood_determines_truth = False
    try:
      if args.maxint:
        self.maxint = int(args.maxint)
    except:
      raise ValueError("faulty maxint argument '{}'".format(args.maxint))
    if self.flpcheck not in ['explicit', 'none']:
      raise ValueError("invalid flpcheck setting '{}'".format(self.flpcheck))
    self.flpcheck = args.flpcheck
    try:
      if args.number:
        self.number = int(args.number)
    except:
      raise ValueError("faulty number argument '{}'".format(args.number))
    self.nofacts = args.nofacts
    self.auxfacts = args.auxfacts

class Plugin:
  def __init__(self, mname, pmodule, arguments=None):
    self.mname = mname
    self.pmodule = pmodule
    self.arguments = arguments

class Statistics:
  '''
  collect statistics (real time, cpu time, counter) in categories
  '''
  def __init__(self):
    self.initial = time.perf_counter(), time.process_time()
    # accumulation of times and counts used in certain categories (real time, cpu time, counter)
    self.categories = collections.defaultdict(lambda: [0.0, 0.0, 0])
    # sequence of categories currently being benchmarked
    # each nesting creates another entry at the end
    self.statstack = [ 'all' ]
    # latest time taken
    self.latest = self.initial

  # not to be used from outside
  def _timestamp(self, addtocategory, increment):
    current = time.perf_counter(), time.process_time()
    for i in [0, 1]:
      self.categories[addtocategory][i] += current[i] - self.latest[i]
    if increment:
      self.categories[addtocategory][2] += 1
    self.latest = current

  # not to be used from outside
  class _Closure:
    def __init__(self, stats, category):
      self.stats = stats
      self.category = category

    def __enter__(self):
      # count time difference to last timestamp for statstack [-1]
      self.stats._timestamp(self.stats.statstack[-1], increment=False)
      # add element to statstack
      self.stats.statstack.append(self.category)

    def __exit__(self, exc_type, exc_value, exc_tb):
      assert(self.stats.statstack[-1] == self.category)
      # count time difference to last timestamp for statstack [-1] and increment counter (for leaving the closure)
      self.stats._timestamp(self.stats.statstack[-1], increment=True)
      # remove element from statstack
      self.stats.statstack.pop()

  # do "with stats.context('category'): ..." (nesting is OK)
  def context(self, categoryname):
    return Statistics._Closure(self, categoryname)

  def accumulate(self):
    # accumulate some categories
    eatoms = [ v for k,v in self.categories.items() if k.startswith('eatom') ]
    return {
      'all.real': sum([ v[0] for v in self.categories.values() ]),
      'all.cpu': sum([ v[1] for v in self.categories.values() ]),

      'eatoms.real': sum([ v[0] for v in eatoms ]),
      'eatoms.cpu': sum([ v[1] for v in eatoms ]),
      'eatoms.count': sum([ v[2] for v in eatoms ]),
    }

  def display(self, name):
    # count time difference to last timestamp for statstack[-1] and also count this once
    self._timestamp(self.statstack[-1], increment=True)
    # accumulate stats
    accum = self.accumulate()
    # print stats
    sys.stderr.write(json.dumps({ 'event':'stats', 'name':name, 'stack': self.statstack, 'categories': dict(self.categories), 'accumulated': accum })+'\n')
    # decrement again in case we display() multiple times
    self.categories[self.statstack[-1]][2] -= 1

# statistics class that does nothing
class StatisticsDummy:
  def __init__(self):
    pass
  class _Closure:
    def __init__(self):
      pass
    def __enter__(self):
      pass
    def __exit__(self, exc_type, exc_value, exc_tb):
      pass

  def context(self, categoryname):
    return StatisticsDummy._Closure()
  def display(self, name):
    pass

class ProgramContext:
  '''
  program-global context
  * collects external atom signatures
  * remembers if maxint is given by program or by commandline
  * holds statistics
  '''
  def __init__(self):
    # key = eatomname, value = list of SignatureInfo
    self.eatoms = collections.defaultdict(set)
    self.wroteMaxint = False
    self.stats = StatisticsDummy()

  def addSignature(self, eatomname, relevancePred, replacementPred, arity):
   self.eatoms[eatomname].add(
      self.SignatureInfo(relevancePred, replacementPred, arity))

  class SignatureInfo:
    def __init__(self, relevancePred, replacementPred, arity):
      self.relevancePred = relevancePred
      self.replacementPred = replacementPred
      self.arity = arity

    def __hash__(self):
      return hash( (self.relevancePred, self.replacementPred, self.arity) )

    def __eq__(self, other):
      return (self.relevancePred == other.relevancePred) and (self.replacementPred == other.replacementPred) and (self.arity == other.arity)

    def __repr__(self):
      return str(self)

    def __str__(self):
      return "SignatureInfo(rel={},repl={},arity={})".format(self.relevancePred, self.replacementPred, self.arity)

def flatten(listoflists):
  return [x for y in listoflists for x in y]
<|endoftext|>


<commit_before>from hitchtest.hitch_stacktrace import HitchStacktrace, TestPosition
from hitchtest.utils import log, warn
from hitchtest.scenario import Scenario
from hitchtest.result import Result
from hitchtest.environment import verify
from os import path
import inspect
import time
import imp
import re

class Test(object):
    def __init__(self, yaml_test, settings, filename, title):
        self.settings = settings
        self.filename = filename
        self.name = yaml_test.get('name', title)
        self.engine = yaml_test.get('engine', '')
        self.description = yaml_test.get('description')
        self.preconditions = yaml_test.get('preconditions')
        self.tags = yaml_test.get('tags')
        self.features = yaml_test.get('features')
        self.scenario = Scenario(yaml_test.get('scenario', []))

        if re.compile("^(.*?)\:(.*?)$").match(self.engine) is None:
            raise RuntimeError("ERROR : engine should be of the form 'engine_filename.py:ClassName'")
        else:
            module_source = path.abspath(path.join(self.settings['engine_folder'], self.engine.split(":")[0]))
            if not path.exists(module_source):
                raise RuntimeError("ERROR : engine filename '{}' not found.".format(self.engine.split(':')[0]))

            engine_class_name = self.engine.split(":")[1]
            engine_module = imp.load_source("engine", module_source)
            if len([x for x in inspect.getmembers(engine_module) if x[0] == engine_class_name]) == 0:
                raise RuntimeError("ERROR : Class {} not found in engine.".format(engine_class_name))
            self.engine_class = [x for x in inspect.getmembers(engine_module) if x[0] == engine_class_name][0][1]

        if self.name[0].isdigit():
            raise RuntimeError("ERROR : Test names cannot start with a digit.")
        if "test" in self.name.lower():
            warn("WARNING: The word 'test' should not appear in your test name - it is redundant.\n")

    def to_dict(self):
        return {
            "filename": self.filename,
            "name": self.name,
            "engine": self.engine,
            "preconditions": self.preconditions,
            "description": self.description,
            "tags": self.tags,
            "features": self.features,
            "scenario": self.scenario.to_dict(),
        }

    def run(self):
        start_time = time.time()
        stacktrace = None
        engine = None
        failure = False
        show_hitch_stacktrace = self.settings.get("show_hitch_stacktrace", False)

        try:
            engine = self.engine_class(self.settings, self.preconditions)
            engine.name = self.name
            engine.aborted = False
            engine.stacktrace = None
            engine._test = self
        except Exception as e:
            stacktrace = HitchStacktrace(self, TestPosition.SETUP, show_hitch_stacktrace)
            failure = True

        if not failure:
            log("RUNNING TEST {}\n".format(self.name))

            try:
                verify(self.settings.get("environment", []))
                engine.set_up()
            except Exception as e:
                stacktrace = HitchStacktrace(self, TestPosition.SETUP, show_hitch_stacktrace)
                failure = True

            if not failure:
                for step in self.scenario.steps:
                    try:
                        step.run(engine)
                    except Exception as e:
                        stacktrace = HitchStacktrace(self, TestPosition.STEP, show_hitch_stacktrace, step=step)
                        failure = True
                        break

            if not engine.aborted:
                if failure:
                    try:
                        engine.stacktrace = stacktrace
                        engine.on_failure()
                    except Exception as e:
                        stacktrace = HitchStacktrace(self, TestPosition.ON_FAILURE, show_hitch_stacktrace)
                else:
                    try:
                        engine.on_success()
                    except Exception as e:
                        failure = True
                        stacktrace = HitchStacktrace(self, TestPosition.ON_SUCCESS, show_hitch_stacktrace)

            try:
                engine.tear_down()
            except Exception as e:
                failure = True
                stacktrace = HitchStacktrace(self, TestPosition.TEARDOWN, show_hitch_stacktrace)

        duration = time.time() - start_time
        dict_stacktrace = stacktrace.to_dict() if stacktrace else None
        aborted = engine.aborted if engine is not None else False
        result = Result(self, failure, duration, stacktrace=dict_stacktrace, aborted=aborted)
        return result
<commit_msg>FEATURE : If no engine is specified, default to engine.py:ExecutionEngine.<commit_after>from hitchtest.hitch_stacktrace import HitchStacktrace, TestPosition
from hitchtest.utils import log, warn
from hitchtest.scenario import Scenario
from hitchtest.result import Result
from hitchtest.environment import verify
from os import path
import inspect
import time
import imp
import re

class Test(object):
    def __init__(self, yaml_test, settings, filename, title):
        self.settings = settings
        self.filename = filename
        self.name = yaml_test.get('name', title)
        self.engine = yaml_test.get('engine', 'engine.py:ExecutionEngine')
        self.description = yaml_test.get('description')
        self.preconditions = yaml_test.get('preconditions')
        self.tags = yaml_test.get('tags')
        self.features = yaml_test.get('features')
        self.scenario = Scenario(yaml_test.get('scenario', []))

        if re.compile("^(.*?)\:(.*?)$").match(self.engine) is None:
            raise RuntimeError("ERROR : engine should be of the form 'engine_filename.py:ClassName'")
        else:
            module_source = path.abspath(path.join(self.settings['engine_folder'], self.engine.split(":")[0]))
            if not path.exists(module_source):
                raise RuntimeError("ERROR : engine filename '{}' not found.".format(self.engine.split(':')[0]))

            engine_class_name = self.engine.split(":")[1]
            engine_module = imp.load_source("engine", module_source)
            if len([x for x in inspect.getmembers(engine_module) if x[0] == engine_class_name]) == 0:
                raise RuntimeError("ERROR : Class {} not found in engine.".format(engine_class_name))
            self.engine_class = [x for x in inspect.getmembers(engine_module) if x[0] == engine_class_name][0][1]

        if self.name[0].isdigit():
            raise RuntimeError("ERROR : Test names cannot start with a digit.")
        if "test" in self.name.lower():
            warn("WARNING: The word 'test' should not appear in your test name - it is redundant.\n")

    def to_dict(self):
        return {
            "filename": self.filename,
            "name": self.name,
            "engine": self.engine,
            "preconditions": self.preconditions,
            "description": self.description,
            "tags": self.tags,
            "features": self.features,
            "scenario": self.scenario.to_dict(),
        }

    def run(self):
        start_time = time.time()
        stacktrace = None
        engine = None
        failure = False
        show_hitch_stacktrace = self.settings.get("show_hitch_stacktrace", False)

        try:
            engine = self.engine_class(self.settings, self.preconditions)
            engine.name = self.name
            engine.aborted = False
            engine.stacktrace = None
            engine._test = self
        except Exception as e:
            stacktrace = HitchStacktrace(self, TestPosition.SETUP, show_hitch_stacktrace)
            failure = True

        if not failure:
            log("RUNNING TEST {}\n".format(self.name))

            try:
                verify(self.settings.get("environment", []))
                engine.set_up()
            except Exception as e:
                stacktrace = HitchStacktrace(self, TestPosition.SETUP, show_hitch_stacktrace)
                failure = True

            if not failure:
                for step in self.scenario.steps:
                    try:
                        step.run(engine)
                    except Exception as e:
                        stacktrace = HitchStacktrace(self, TestPosition.STEP, show_hitch_stacktrace, step=step)
                        failure = True
                        break

            if not engine.aborted:
                if failure:
                    try:
                        engine.stacktrace = stacktrace
                        engine.on_failure()
                    except Exception as e:
                        stacktrace = HitchStacktrace(self, TestPosition.ON_FAILURE, show_hitch_stacktrace)
                else:
                    try:
                        engine.on_success()
                    except Exception as e:
                        failure = True
                        stacktrace = HitchStacktrace(self, TestPosition.ON_SUCCESS, show_hitch_stacktrace)

            try:
                engine.tear_down()
            except Exception as e:
                failure = True
                stacktrace = HitchStacktrace(self, TestPosition.TEARDOWN, show_hitch_stacktrace)

        duration = time.time() - start_time
        dict_stacktrace = stacktrace.to_dict() if stacktrace else None
        aborted = engine.aborted if engine is not None else False
        result = Result(self, failure, duration, stacktrace=dict_stacktrace, aborted=aborted)
        return result
<|endoftext|>


<commit_before>import argparse
import codecs
import logging
import os
import sys
import signal
from collections import defaultdict
from pkg_resources import iter_entry_points

from honcho import __version__
from honcho.process import Popen
from honcho.manager import Manager
from honcho import compat, environ

logging.basicConfig(format='%(asctime)s [%(process)d] [%(levelname)s] '
                           '%(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S',
                    level=logging.INFO)
log = logging.getLogger(__name__)

BASENAME = os.path.basename(os.getcwd())

export_choices = dict((_export.name, _export)
                      for _export in iter_entry_points('honcho_exporters'))

try:
    # Python 3
    sys.stdout = codecs.getwriter("utf-8")(sys.stdout.buffer)
    sys.stderr = codecs.getwriter("utf-8")(sys.stderr.buffer)
except AttributeError:
    sys.stdout = codecs.getwriter("utf-8")(sys.stdout)
    sys.stderr = codecs.getwriter("utf-8")(sys.stderr)

class CommandError(Exception):
    pass

_parent_parser = argparse.ArgumentParser(
    'honcho',
    description='Manage Procfile-based applications',
    add_help=False)
_parent_parser.add_argument(
    '-e', '--env',
    help='environment file[,file]', default='.env')
_parent_parser.add_argument(
    '-d', '--app-root',
    help='procfile directory', default='.')
_parent_parser.add_argument(
    '-f', '--procfile',
    help='procfile path', default='Procfile')
_parent_parser.add_argument(
    '-v', '--version',
    action='version', version='%(prog)s ' + __version__)

_parser_defaults = {
    'parents': [_parent_parser],
    'formatter_class': argparse.ArgumentDefaultsHelpFormatter,
}

parser = argparse.ArgumentParser(**_parser_defaults)

subparsers = parser.add_subparsers(title='tasks', dest='command')
subparsers.required = True

def command_check(args):
    procfile = _procfile(_procfile_path(args.app_root, args.procfile))

    log.info('Valid procfile detected ({0})'.format(', '.join(procfile.processes)))

parser_check = subparsers.add_parser(
    'check',
    help="validate a Procfile",
    **_parser_defaults)

def command_export(args):
    if args.log == "/var/log/APP":
        args.log = args.log.replace('APP', args.app)

    if args.user is None:
        if compat.ON_WINDOWS:
            args.user = os.environ.get('USERNAME')
        else:
            args.user = os.environ.get('USER')

    if args.user is None:
        raise CommandError('Could not automatically deduce user: please '
                           'supply the -u/--user option.')

    args.app_root = os.path.abspath(args.app_root)

    procfile_path = _procfile_path(args.app_root, args.procfile)
    procfile = _procfile(procfile_path)
    env = _read_env(procfile_path, args.env)
    concurrency = _parse_concurrency(args.concurrency)

    export_class = export_choices[args.format].load()
    export = export_class(procfile, args, env, concurrency)
    export.export()

parser_export = subparsers.add_parser(
    'export',
    help="export a Procfile to another format",
    **_parser_defaults)
parser_export.add_argument(
    '-a', '--app',
    help="alternative app name", default=BASENAME, type=str, metavar='APP')
parser_export.add_argument(
    '-l', '--log',
    help="directory to place process logs in",
    default="/var/log/APP", type=str, metavar='DIR')
parser_export.add_argument('-p', '--port', default=5000, type=int, metavar='N')
parser_export.add_argument(
    '-c', '--concurrency',
    help='number of each process type to run.',
    type=str, metavar='process=num,process=num')
parser_export.add_argument(
    '-u', '--user',
    help="user the application should run as",
    type=str)
parser_export.add_argument(
    '-s', '--shell',
    help="the shell that should run the application",
    default='/bin/sh', type=str)
parser_export.add_argument(
    'location',
    help="folder to export to",
    type=str, metavar="LOCATION")
parser_export.add_argument(
    'format',
    help="format in which to export",
    choices=list(export_choices),
    type=str, metavar="FORMAT")

def command_help(args):
    argv = ['--help']
    if args.task:
        argv.append(args.task)
    return parser.parse_args(argv[::-1])

parser_help = subparsers.add_parser(
    'help',
    help="describe available tasks or one specific task",
    **_parser_defaults)
parser_help.add_argument('task', help='task to show help for', nargs='?')

def command_run(args):
    procfile_path = _procfile_path(args.app_root, args.procfile)
    os.environ.update(_read_env(procfile_path, args.env))

    if compat.ON_WINDOWS:
        # do not quote on Windows, subprocess will handle it for us
        # using the MSFT quoting rules
        cmd = args.argv
    else:
        cmd = ' '.join(compat.shellquote(arg) for arg in args.argv)

    p = Popen(cmd, stdout=sys.stdout, stderr=sys.stderr,
              start_new_session=False)
    signal.signal(signal.SIGINT, signal.SIG_IGN)
    p.wait()
    sys.exit(p.returncode)

parser_run = subparsers.add_parser(
    'run',
    help="run a command using your application's environment",
    **_parser_defaults)
parser_run.add_argument(
    'argv',
    nargs=argparse.REMAINDER,
    help='command to run')

def command_start(args):
    procfile_path = _procfile_path(args.app_root, args.procfile)
    procfile = _procfile(procfile_path)

    port = int(os.environ.get('PORT', args.port))
    concurrency = _parse_concurrency(args.concurrency)
    env = _read_env(procfile_path, args.env)
    quiet = _parse_quiet(args.quiet)

    if args.processes:
        processes = compat.OrderedDict()
        for name in args.processes:
            try:
                processes[name] = procfile.processes[name]
            except KeyError:
                raise CommandError("Process type '{0}' does not exist in Procfile".format(name))
    else:
        processes = procfile.processes

    manager = Manager()

    for p in environ.expand_processes(processes,
                                      concurrency=concurrency,
                                      env=env,
                                      quiet=quiet,
                                      port=port):
        e = os.environ.copy()
        e.update(p.env)
        manager.add_process(p.name, p.cmd, quiet=p.quiet, env=e)

    manager.loop()
    sys.exit(manager.returncode)

parser_start = subparsers.add_parser(
    'start',
    help="start the application (or a specific PROCESS)",
    **_parser_defaults)
parser_start.add_argument(
    '-p', '--port',
    help="starting port number",
    type=int, default=5000, metavar='N')
parser_start.add_argument(
    '-c', '--concurrency',
    help='the number of each process type to run.',
    type=str, metavar='process=num,process=num')
parser_start.add_argument(
    '-q', '--quiet',
    help='process names for which to suppress output',
    type=str, metavar='process1,process2,process3')
parser_start.add_argument(
    'processes', nargs='*',
    help='process(es) to start (default: all processes will be run)')

COMMANDS = {
    'check': command_check,
    'export': command_export,
    'help': command_help,
    'run': command_run,
    'start': command_start,
}

def main(argv=None):
    if argv is not None:
        args = parser.parse_args(argv)
    else:
        args = parser.parse_args()

    try:
        COMMANDS[args.command](args)
    except CommandError as e:
        log.error(str(e))
        sys.exit(1)

def _procfile_path(app_root, procfile):
    return os.path.join(app_root, procfile)

def _procfile(filename):
    try:
        with open(filename) as f:
            content = f.read()
    except IOError:
        raise CommandError('Procfile does not exist or is not a file')

    try:
        procfile = environ.parse_procfile(content)
    except AssertionError as e:
        raise CommandError(str(e))

    return procfile

def _read_env(procfile_path, env):
    app_root = os.path.dirname(procfile_path)
    files = [e.strip() for e in env.split(',')]
    content = []
    for envfile in files:
        try:
            with open(os.path.join(app_root, envfile)) as f:
                content.append(f.read())
        except IOError:
            pass

    return environ.parse('\n'.join(content))

def _parse_concurrency(desc):
    result = defaultdict(lambda: 1)
    if desc is None:
        return result
    for item in desc.split(','):
        key, concurrency = item.split('=', 1)
        result[key] = int(concurrency)
    return result

def _parse_quiet(desc):
    result = []
    if desc is None:
        return result
    result = desc.split(',')
    return result

if __name__ == '__main__':
    main()
<commit_msg>honcho export --help: List format choices<commit_after>import argparse
import codecs
import logging
import os
import sys
import signal
from collections import defaultdict
from pkg_resources import iter_entry_points

from honcho import __version__
from honcho.process import Popen
from honcho.manager import Manager
from honcho import compat, environ

logging.basicConfig(format='%(asctime)s [%(process)d] [%(levelname)s] '
                           '%(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S',
                    level=logging.INFO)
log = logging.getLogger(__name__)

BASENAME = os.path.basename(os.getcwd())

export_choices = dict((_export.name, _export)
                      for _export in iter_entry_points('honcho_exporters'))

try:
    # Python 3
    sys.stdout = codecs.getwriter("utf-8")(sys.stdout.buffer)
    sys.stderr = codecs.getwriter("utf-8")(sys.stderr.buffer)
except AttributeError:
    sys.stdout = codecs.getwriter("utf-8")(sys.stdout)
    sys.stderr = codecs.getwriter("utf-8")(sys.stderr)

class CommandError(Exception):
    pass

_parent_parser = argparse.ArgumentParser(
    'honcho',
    description='Manage Procfile-based applications',
    add_help=False)
_parent_parser.add_argument(
    '-e', '--env',
    help='environment file[,file]', default='.env')
_parent_parser.add_argument(
    '-d', '--app-root',
    help='procfile directory', default='.')
_parent_parser.add_argument(
    '-f', '--procfile',
    help='procfile path', default='Procfile')
_parent_parser.add_argument(
    '-v', '--version',
    action='version', version='%(prog)s ' + __version__)

_parser_defaults = {
    'parents': [_parent_parser],
    'formatter_class': argparse.ArgumentDefaultsHelpFormatter,
}

parser = argparse.ArgumentParser(**_parser_defaults)

subparsers = parser.add_subparsers(title='tasks', dest='command')
subparsers.required = True

def command_check(args):
    procfile = _procfile(_procfile_path(args.app_root, args.procfile))

    log.info('Valid procfile detected ({0})'.format(', '.join(procfile.processes)))

parser_check = subparsers.add_parser(
    'check',
    help="validate a Procfile",
    **_parser_defaults)

def command_export(args):
    if args.log == "/var/log/APP":
        args.log = args.log.replace('APP', args.app)

    if args.user is None:
        if compat.ON_WINDOWS:
            args.user = os.environ.get('USERNAME')
        else:
            args.user = os.environ.get('USER')

    if args.user is None:
        raise CommandError('Could not automatically deduce user: please '
                           'supply the -u/--user option.')

    args.app_root = os.path.abspath(args.app_root)

    procfile_path = _procfile_path(args.app_root, args.procfile)
    procfile = _procfile(procfile_path)
    env = _read_env(procfile_path, args.env)
    concurrency = _parse_concurrency(args.concurrency)

    export_class = export_choices[args.format].load()
    export = export_class(procfile, args, env, concurrency)
    export.export()

parser_export = subparsers.add_parser(
    'export',
    help="export a Procfile to another format",
    **_parser_defaults)
parser_export.add_argument(
    '-a', '--app',
    help="alternative app name", default=BASENAME, type=str, metavar='APP')
parser_export.add_argument(
    '-l', '--log',
    help="directory to place process logs in",
    default="/var/log/APP", type=str, metavar='DIR')
parser_export.add_argument('-p', '--port', default=5000, type=int, metavar='N')
parser_export.add_argument(
    '-c', '--concurrency',
    help='number of each process type to run.',
    type=str, metavar='process=num,process=num')
parser_export.add_argument(
    '-u', '--user',
    help="user the application should run as",
    type=str)
parser_export.add_argument(
    '-s', '--shell',
    help="the shell that should run the application",
    default='/bin/sh', type=str)
parser_export.add_argument(
    'location',
    help="folder to export to",
    type=str, metavar="LOCATION")
parser_export.add_argument(
    'format',
    help="format to export to; one of %(choices)s",
    choices=list(export_choices),
    type=str, metavar="FORMAT")

def command_help(args):
    argv = ['--help']
    if args.task:
        argv.append(args.task)
    return parser.parse_args(argv[::-1])

parser_help = subparsers.add_parser(
    'help',
    help="describe available tasks or one specific task",
    **_parser_defaults)
parser_help.add_argument('task', help='task to show help for', nargs='?')

def command_run(args):
    procfile_path = _procfile_path(args.app_root, args.procfile)
    os.environ.update(_read_env(procfile_path, args.env))

    if compat.ON_WINDOWS:
        # do not quote on Windows, subprocess will handle it for us
        # using the MSFT quoting rules
        cmd = args.argv
    else:
        cmd = ' '.join(compat.shellquote(arg) for arg in args.argv)

    p = Popen(cmd, stdout=sys.stdout, stderr=sys.stderr,
              start_new_session=False)
    signal.signal(signal.SIGINT, signal.SIG_IGN)
    p.wait()
    sys.exit(p.returncode)

parser_run = subparsers.add_parser(
    'run',
    help="run a command using your application's environment",
    **_parser_defaults)
parser_run.add_argument(
    'argv',
    nargs=argparse.REMAINDER,
    help='command to run')

def command_start(args):
    procfile_path = _procfile_path(args.app_root, args.procfile)
    procfile = _procfile(procfile_path)

    port = int(os.environ.get('PORT', args.port))
    concurrency = _parse_concurrency(args.concurrency)
    env = _read_env(procfile_path, args.env)
    quiet = _parse_quiet(args.quiet)

    if args.processes:
        processes = compat.OrderedDict()
        for name in args.processes:
            try:
                processes[name] = procfile.processes[name]
            except KeyError:
                raise CommandError("Process type '{0}' does not exist in Procfile".format(name))
    else:
        processes = procfile.processes

    manager = Manager()

    for p in environ.expand_processes(processes,
                                      concurrency=concurrency,
                                      env=env,
                                      quiet=quiet,
                                      port=port):
        e = os.environ.copy()
        e.update(p.env)
        manager.add_process(p.name, p.cmd, quiet=p.quiet, env=e)

    manager.loop()
    sys.exit(manager.returncode)

parser_start = subparsers.add_parser(
    'start',
    help="start the application (or a specific PROCESS)",
    **_parser_defaults)
parser_start.add_argument(
    '-p', '--port',
    help="starting port number",
    type=int, default=5000, metavar='N')
parser_start.add_argument(
    '-c', '--concurrency',
    help='the number of each process type to run.',
    type=str, metavar='process=num,process=num')
parser_start.add_argument(
    '-q', '--quiet',
    help='process names for which to suppress output',
    type=str, metavar='process1,process2,process3')
parser_start.add_argument(
    'processes', nargs='*',
    help='process(es) to start (default: all processes will be run)')

COMMANDS = {
    'check': command_check,
    'export': command_export,
    'help': command_help,
    'run': command_run,
    'start': command_start,
}

def main(argv=None):
    if argv is not None:
        args = parser.parse_args(argv)
    else:
        args = parser.parse_args()

    try:
        COMMANDS[args.command](args)
    except CommandError as e:
        log.error(str(e))
        sys.exit(1)

def _procfile_path(app_root, procfile):
    return os.path.join(app_root, procfile)

def _procfile(filename):
    try:
        with open(filename) as f:
            content = f.read()
    except IOError:
        raise CommandError('Procfile does not exist or is not a file')

    try:
        procfile = environ.parse_procfile(content)
    except AssertionError as e:
        raise CommandError(str(e))

    return procfile

def _read_env(procfile_path, env):
    app_root = os.path.dirname(procfile_path)
    files = [e.strip() for e in env.split(',')]
    content = []
    for envfile in files:
        try:
            with open(os.path.join(app_root, envfile)) as f:
                content.append(f.read())
        except IOError:
            pass

    return environ.parse('\n'.join(content))

def _parse_concurrency(desc):
    result = defaultdict(lambda: 1)
    if desc is None:
        return result
    for item in desc.split(','):
        key, concurrency = item.split('=', 1)
        result[key] = int(concurrency)
    return result

def _parse_quiet(desc):
    result = []
    if desc is None:
        return result
    result = desc.split(',')
    return result

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before># Copyright (c) 2009-2021 The Regents of the University of Michigan
# This file is part of the HOOMD-blue project, released under the BSD 3-Clause
# License.

"""Apply forces to particles."""

import hoomd
from hoomd import _hoomd
from hoomd.md import _md
from hoomd.operation import _HOOMDBaseObject
from hoomd.logging import log
from hoomd.data.typeparam import TypeParameter
from hoomd.data.typeconverter import OnlyTypes
from hoomd.data.parameterdicts import ParameterDict, TypeParameterDict
from hoomd.data.attacherror import AttachedDataError
from hoomd.filter import ParticleFilter
from hoomd.md.constrain import ConstraintForce

def _ellip_preprocessing(constraint):
    if constraint is not None:
        if constraint.__class__.__name__ == "constraint_ellipsoid":
            return constraint
        else:
            raise RuntimeError(
                "Active force constraint is not accepted (currently only "
                "accepts ellipsoids)")
    else:
        return None

class _force:  # noqa - This will be removed eventually. Needed to build docs.
    pass

class Force(_HOOMDBaseObject):
    """Defines a force in HOOMD-blue.

    Pair, angle, bond, and other forces are subclasses of this class.

    Note:
        :py:class:`Force` is the base class for all loggable forces.
        Users should not instantiate this class directly.

    Initializes some loggable quantities.
    """

    @log
    def energy(self):
        """float: Sum of the energy of the whole system."""
        if self._attached:
            self._cpp_obj.compute(self._simulation.timestep)
            return self._cpp_obj.calcEnergySum()
        else:
            raise AttachedDataError("energy")

    @log(category="particle")
    def energies(self):
        """(*N_particles*, ) `numpy.ndarray` of ``numpy.float64``: The \
        energies for all particles."""
        if self._attached:
            self._cpp_obj.compute(self._simulation.timestep)
            return self._cpp_obj.getEnergies()
        else:
            raise AttachedDataError("energies")

    @log(category="particle")
    def forces(self):
        """(*N_particles*, 3) `numpy.ndarray` of ``numpy.float64``: The \
        forces for all particles."""
        if self._attached:
            self._cpp_obj.compute(self._simulation.timestep)
            return self._cpp_obj.getForces()
        else:
            raise AttachedDataError("forces")

    @log(category="particle")
    def torques(self):
        """(*N_particles*, 3) `numpy.ndarray` of ``numpy.float64``: The torque \
        for all particles."""
        if self._attached:
            self._cpp_obj.compute(self._simulation.timestep)
            return self._cpp_obj.getTorques()
        else:
            raise AttachedDataError("torques")

    @log(category="particle")
    def virials(self):
        """(*N_particles*, ) `numpy.ndarray` of ``numpy.float64``: The virial \
        for all particles."""
        if self._attached:
            self._cpp_obj.compute(self._simulation.timestep)
            return self._cpp_obj.getVirials()
        else:
            raise AttachedDataError("virials")

class constant(Force):  # noqa - this will be renamed when it is ported to v3
    R"""Constant force.

    Args:
        fvec (tuple): force vector (in force units)
        tvec (tuple): torque vector (in torque units)
        fx (float): x component of force, retained for backwards compatibility
        fy (float): y component of force, retained for backwards compatibility
        fz (float): z component of force, retained for backwards compatibility
        group (``hoomd.group``): Group for which the force will be set.
        callback (`callable`): A python callback invoked every time the forces
            are computed

    :py:class:`constant` specifies that a constant force should be added to
    every particle in the simulation or optionally to all particles in a group.

    Note:
        Forces are kept constant during the simulation. If a callback should
        re-compute particle forces every time step, it needs to overwrite the
        old forces of **all** particles with new values.

    Note:
        Per-particle forces take precedence over a particle group, which takes
        precedence over constant forces for all particles.

    Examples::

        force.constant(fx=1.0, fy=0.5, fz=0.25)
        const = force.constant(fvec=(0.4,1.0,0.5))
        const = force.constant(fvec=(0.4,1.0,0.5),group=fluid)
        const = force.constant(fvec=(0.4,1.0,0.5), tvec=(0,0,1) ,group=fluid)

        def updateForces(timestep):
            global const
            const.setForce(tag=1, fvec=(1.0*timestep,2.0*timestep,3.0*timestep))
        const = force.constant(callback=updateForces)
    """

    def __init__(
        self,
        fx=None,
        fy=None,
        fz=None,
        fvec=None,
        tvec=None,
        group=None,
        callback=None,
    ):

        if (fx is not None) and (fy is not None) and (fz is not None):
            self.fvec = (fx, fy, fz)
        elif fvec is not None:
            self.fvec = fvec
        else:
            self.fvec = (0, 0, 0)

        if tvec is not None:
            self.tvec = tvec
        else:
            self.tvec = (0, 0, 0)

        if (self.fvec == (0, 0, 0)) and (self.tvec == (0, 0, 0)
                                         and callback is None):
            hoomd.context.current.device.cpp_msg.warning(
                "The constant force specified has no non-zero components\n")

        # initialize the base class
        Force.__init__(self)

        # create the c++ mirror class
        if group is not None:
            self.cppForce = _hoomd.ConstForceCompute(
                hoomd.context.current.system_definition,
                group.cpp_group,
                self.fvec[0],
                self.fvec[1],
                self.fvec[2],
                self.tvec[0],
                self.tvec[1],
                self.tvec[2],
            )
        else:
            self.cppForce = _hoomd.ConstForceCompute(
                hoomd.context.current.system_definition,
                self.fvec[0],
                self.fvec[1],
                self.fvec[2],
                self.tvec[0],
                self.tvec[1],
                self.tvec[2],
            )

        if callback is not None:
            self.cppForce.setCallback(callback)

        hoomd.context.current.system.addCompute(self.cppForce, self.force_name)

    R""" Change the value of the constant force.

    Args:
        fx (float) New x-component of the force (in force units)
        fy (float) New y-component of the force (in force units)
        fz (float) New z-component of the force (in force units)
        fvec (tuple) New force vector
        tvec (tuple) New torque vector
        group Group for which the force will be set
        tag (int) Particle tag for which the force will be set
            .. versionadded:: 2.3

     Using setForce() requires that you saved the created constant force in a
     variable. i.e.

     Examples:
        const = force.constant(fx=0.4, fy=1.0, fz=0.5)

        const.setForce(fx=0.2, fy=0.1, fz=-0.5)
        const.setForce(fx=0.2, fy=0.1, fz=-0.5, group=fluid)
        const.setForce(fvec=(0.2,0.1,-0.5), tvec=(0,0,1), group=fluid)
    """

    def setForce(  # noqa - this will be documented when it is ported to v3
        self,
        fx=None,
        fy=None,
        fz=None,
        fvec=None,
        tvec=None,
        group=None,
        tag=None,
    ):

        if (fx is not None) and (fy is not None) and (fx is not None):
            self.fvec = (fx, fy, fz)
        elif fvec is not None:
            self.fvec = fvec
        else:
            self.fvec = (0, 0, 0)

        if tvec is not None:
            self.tvec = tvec
        else:
            self.tvec = (0, 0, 0)

        if (fvec == (0, 0, 0)) and (tvec == (0, 0, 0)):
            hoomd.context.current.device.cpp_msg.warning(
                "You are setting the constant force to have no non-zero "
                "components\n")

        self.check_initialization()
        if group is not None:
            self.cppForce.setGroupForce(
                group.cpp_group,
                self.fvec[0],
                self.fvec[1],
                self.fvec[2],
                self.tvec[0],
                self.tvec[1],
                self.tvec[2],
            )
        elif tag is not None:
            self.cppForce.setParticleForce(
                tag,
                self.fvec[0],
                self.fvec[1],
                self.fvec[2],
                self.tvec[0],
                self.tvec[1],
                self.tvec[2],
            )
        else:
            self.cppForce.setForce(
                self.fvec[0],
                self.fvec[1],
                self.fvec[2],
                self.tvec[0],
                self.tvec[1],
                self.tvec[2],
            )

    R""" Set a python callback to be called before the force is evaluated

    Args:
        callback (`callable`) The callback function

     Examples:
        const = force.constant(fx=0.4, fy=1.0, fz=0.5)

        def updateForces(timestep):
            global const
            const.setForce(tag=1, fvec=(1.0*timestep,2.0*timestep,3.0*timestep))

        const.set_callback(updateForces)
        run(100)

        # Reset the callback
        const.set_callback(None)
    """

    def set_callback(self, callback=None):  # noqa - will be ported to v3
        self.cppForce.setCallback(callback)

    # there are no coeffs to update in the constant force compute
    def update_coeffs(self):  # noqa - will be ported to v3
        pass

class Active(Force):
    r"""Active force.

    Attributes:
        filter (:py:mod:`hoomd.filter`): Subset of particles on which to apply
            active forces.
        rotation_diff (float): rotational diffusion constant, :math:`D_r`, for
            all particles in the group.
        active_force (tuple): active force vector in reference to the
            orientation of a particle. It is defined per particle type and stays
            constant during the simulation.
        active_torque (tuple): active torque vector in reference to the
            orientation of a particle. It is defined per particle type and stays
            constant during the simulation.

    :py:class:`Active` specifies that an active force should be added to all
    particles.  Obeys :math:`\delta {\bf r}_i = \delta t v_0 \hat{p}_i`, where
    :math:`v_0` is the active velocity. In 2D :math:`\hat{p}_i = (\cos \theta_i,
    \sin \theta_i)` is the active force vector for particle :math:`i` and the
    diffusion of the active force vector follows :math:`\delta \theta / \delta t
    = \sqrt{2 D_r / \delta t} \Gamma`, where :math:`D_r` is the rotational
    diffusion constant, and the gamma function is a unit-variance random
    variable, whose components are uncorrelated in time, space, and between
    particles.  In 3D, :math:`\hat{p}_i` is a unit vector in 3D space, and
    diffusion follows :math:`\delta \hat{p}_i / \delta t = \sqrt{2 D_r / \delta
    t} \Gamma (\hat{p}_i (\cos \theta - 1) + \hat{p}_r \sin \theta)`, where
    :math:`\hat{p}_r` is an uncorrelated random unit vector. The persistence
    length of an active particle's path is :math:`v_0 / D_r`.  The rotational
    diffusion is applied to the orientation vector/quaternion of each particle.
    This implies that both the active force and the active torque vectors in the
    particle frame stay constant during the simulation. Hence, the active forces
    in the system frame are composed of the forces in particle frame and the
    current orientation of the particle.

    Examples::

        all = filter.All()
        active = hoomd.md.force.Active(
            filter=hoomd.filter.All(), rotation_diff=0.01
            )
        active.active_force['A','B'] = (1,0,0)
        active.active_torque['A','B'] = (0,0,0)
    """

    def __init__(self, filter, rotation_diff=0.1):
        # store metadata
        param_dict = ParameterDict(
            filter=ParticleFilter,
            rotation_diff=float(rotation_diff),
            constraint=OnlyTypes(ConstraintForce,
                                 allow_none=True,
                                 preprocess=_ellip_preprocessing),
        )
        param_dict.update(
            dict(
                constraint=None,
                rotation_diff=rotation_diff,
                filter=filter,
            ))
        # set defaults
        self._param_dict.update(param_dict)

        active_force = TypeParameter(
            "active_force",
            type_kind="particle_types",
            param_dict=TypeParameterDict((1.0, 0.0, 0.0), len_keys=1),
        )
        active_torque = TypeParameter(
            "active_torque",
            type_kind="particle_types",
            param_dict=TypeParameterDict((0.0, 0.0, 0.0), len_keys=1),
        )

        self._extend_typeparam([active_force, active_torque])

    def _add(self, simulation):
        """Add the operation to a simulation.

        Active forces use RNGs. Warn the user if they did not set the seed.
        """
        if simulation is not None:
            simulation._warn_if_seed_unset()

        super()._add(simulation)

    def _attach(self):
        # initialize the reflected c++ class
        if isinstance(self._simulation.device, hoomd.device.CPU):
            my_class = _md.ActiveForceCompute
        else:
            my_class = _md.ActiveForceComputeGPU

        self._cpp_obj = my_class(
            self._simulation.state._cpp_sys_def,
            self._simulation.state._get_group(self.filter),
            self.rotation_diff,
            _hoomd.make_scalar3(0, 0, 0),
            0,
            0,
            0,
        )

        # Attach param_dict and typeparam_dict
        super()._attach()
<commit_msg>Show example of new log requires_attach argument<commit_after># Copyright (c) 2009-2021 The Regents of the University of Michigan
# This file is part of the HOOMD-blue project, released under the BSD 3-Clause
# License.

"""Apply forces to particles."""

import hoomd
from hoomd import _hoomd
from hoomd.md import _md
from hoomd.operation import _HOOMDBaseObject
from hoomd.logging import log
from hoomd.data.typeparam import TypeParameter
from hoomd.data.typeconverter import OnlyTypes
from hoomd.data.parameterdicts import ParameterDict, TypeParameterDict
from hoomd.data.attacherror import AttachedDataError
from hoomd.filter import ParticleFilter
from hoomd.md.constrain import ConstraintForce

def _ellip_preprocessing(constraint):
    if constraint is not None:
        if constraint.__class__.__name__ == "constraint_ellipsoid":
            return constraint
        else:
            raise RuntimeError(
                "Active force constraint is not accepted (currently only "
                "accepts ellipsoids)")
    else:
        return None

class _force:  # noqa - This will be removed eventually. Needed to build docs.
    pass

class Force(_HOOMDBaseObject):
    """Defines a force in HOOMD-blue.

    Pair, angle, bond, and other forces are subclasses of this class.

    Note:
        :py:class:`Force` is the base class for all loggable forces.
        Users should not instantiate this class directly.

    Initializes some loggable quantities.
    """

    @log(requires_attach=True)
    def energy(self):
        """float: Sum of the energy of the whole system."""
        self._cpp_obj.compute(self._simulation.timestep)
        return self._cpp_obj.calcEnergySum()

    @log(category="particle")
    def energies(self):
        """(*N_particles*, ) `numpy.ndarray` of ``numpy.float64``: The \
        energies for all particles."""
        if self._attached:
            self._cpp_obj.compute(self._simulation.timestep)
            return self._cpp_obj.getEnergies()
        else:
            raise AttachedDataError("energies")

    @log(category="particle")
    def forces(self):
        """(*N_particles*, 3) `numpy.ndarray` of ``numpy.float64``: The \
        forces for all particles."""
        if self._attached:
            self._cpp_obj.compute(self._simulation.timestep)
            return self._cpp_obj.getForces()
        else:
            raise AttachedDataError("forces")

    @log(category="particle")
    def torques(self):
        """(*N_particles*, 3) `numpy.ndarray` of ``numpy.float64``: The torque \
        for all particles."""
        if self._attached:
            self._cpp_obj.compute(self._simulation.timestep)
            return self._cpp_obj.getTorques()
        else:
            raise AttachedDataError("torques")

    @log(category="particle")
    def virials(self):
        """(*N_particles*, ) `numpy.ndarray` of ``numpy.float64``: The virial \
        for all particles."""
        if self._attached:
            self._cpp_obj.compute(self._simulation.timestep)
            return self._cpp_obj.getVirials()
        else:
            raise AttachedDataError("virials")

class constant(Force):  # noqa - this will be renamed when it is ported to v3
    R"""Constant force.

    Args:
        fvec (tuple): force vector (in force units)
        tvec (tuple): torque vector (in torque units)
        fx (float): x component of force, retained for backwards compatibility
        fy (float): y component of force, retained for backwards compatibility
        fz (float): z component of force, retained for backwards compatibility
        group (``hoomd.group``): Group for which the force will be set.
        callback (`callable`): A python callback invoked every time the forces
            are computed

    :py:class:`constant` specifies that a constant force should be added to
    every particle in the simulation or optionally to all particles in a group.

    Note:
        Forces are kept constant during the simulation. If a callback should
        re-compute particle forces every time step, it needs to overwrite the
        old forces of **all** particles with new values.

    Note:
        Per-particle forces take precedence over a particle group, which takes
        precedence over constant forces for all particles.

    Examples::

        force.constant(fx=1.0, fy=0.5, fz=0.25)
        const = force.constant(fvec=(0.4,1.0,0.5))
        const = force.constant(fvec=(0.4,1.0,0.5),group=fluid)
        const = force.constant(fvec=(0.4,1.0,0.5), tvec=(0,0,1) ,group=fluid)

        def updateForces(timestep):
            global const
            const.setForce(tag=1, fvec=(1.0*timestep,2.0*timestep,3.0*timestep))
        const = force.constant(callback=updateForces)
    """

    def __init__(
        self,
        fx=None,
        fy=None,
        fz=None,
        fvec=None,
        tvec=None,
        group=None,
        callback=None,
    ):

        if (fx is not None) and (fy is not None) and (fz is not None):
            self.fvec = (fx, fy, fz)
        elif fvec is not None:
            self.fvec = fvec
        else:
            self.fvec = (0, 0, 0)

        if tvec is not None:
            self.tvec = tvec
        else:
            self.tvec = (0, 0, 0)

        if (self.fvec == (0, 0, 0)) and (self.tvec == (0, 0, 0)
                                         and callback is None):
            hoomd.context.current.device.cpp_msg.warning(
                "The constant force specified has no non-zero components\n")

        # initialize the base class
        Force.__init__(self)

        # create the c++ mirror class
        if group is not None:
            self.cppForce = _hoomd.ConstForceCompute(
                hoomd.context.current.system_definition,
                group.cpp_group,
                self.fvec[0],
                self.fvec[1],
                self.fvec[2],
                self.tvec[0],
                self.tvec[1],
                self.tvec[2],
            )
        else:
            self.cppForce = _hoomd.ConstForceCompute(
                hoomd.context.current.system_definition,
                self.fvec[0],
                self.fvec[1],
                self.fvec[2],
                self.tvec[0],
                self.tvec[1],
                self.tvec[2],
            )

        if callback is not None:
            self.cppForce.setCallback(callback)

        hoomd.context.current.system.addCompute(self.cppForce, self.force_name)

    R""" Change the value of the constant force.

    Args:
        fx (float) New x-component of the force (in force units)
        fy (float) New y-component of the force (in force units)
        fz (float) New z-component of the force (in force units)
        fvec (tuple) New force vector
        tvec (tuple) New torque vector
        group Group for which the force will be set
        tag (int) Particle tag for which the force will be set
            .. versionadded:: 2.3

     Using setForce() requires that you saved the created constant force in a
     variable. i.e.

     Examples:
        const = force.constant(fx=0.4, fy=1.0, fz=0.5)

        const.setForce(fx=0.2, fy=0.1, fz=-0.5)
        const.setForce(fx=0.2, fy=0.1, fz=-0.5, group=fluid)
        const.setForce(fvec=(0.2,0.1,-0.5), tvec=(0,0,1), group=fluid)
    """

    def setForce(  # noqa - this will be documented when it is ported to v3
        self,
        fx=None,
        fy=None,
        fz=None,
        fvec=None,
        tvec=None,
        group=None,
        tag=None,
    ):

        if (fx is not None) and (fy is not None) and (fx is not None):
            self.fvec = (fx, fy, fz)
        elif fvec is not None:
            self.fvec = fvec
        else:
            self.fvec = (0, 0, 0)

        if tvec is not None:
            self.tvec = tvec
        else:
            self.tvec = (0, 0, 0)

        if (fvec == (0, 0, 0)) and (tvec == (0, 0, 0)):
            hoomd.context.current.device.cpp_msg.warning(
                "You are setting the constant force to have no non-zero "
                "components\n")

        self.check_initialization()
        if group is not None:
            self.cppForce.setGroupForce(
                group.cpp_group,
                self.fvec[0],
                self.fvec[1],
                self.fvec[2],
                self.tvec[0],
                self.tvec[1],
                self.tvec[2],
            )
        elif tag is not None:
            self.cppForce.setParticleForce(
                tag,
                self.fvec[0],
                self.fvec[1],
                self.fvec[2],
                self.tvec[0],
                self.tvec[1],
                self.tvec[2],
            )
        else:
            self.cppForce.setForce(
                self.fvec[0],
                self.fvec[1],
                self.fvec[2],
                self.tvec[0],
                self.tvec[1],
                self.tvec[2],
            )

    R""" Set a python callback to be called before the force is evaluated

    Args:
        callback (`callable`) The callback function

     Examples:
        const = force.constant(fx=0.4, fy=1.0, fz=0.5)

        def updateForces(timestep):
            global const
            const.setForce(tag=1, fvec=(1.0*timestep,2.0*timestep,3.0*timestep))

        const.set_callback(updateForces)
        run(100)

        # Reset the callback
        const.set_callback(None)
    """

    def set_callback(self, callback=None):  # noqa - will be ported to v3
        self.cppForce.setCallback(callback)

    # there are no coeffs to update in the constant force compute
    def update_coeffs(self):  # noqa - will be ported to v3
        pass

class Active(Force):
    r"""Active force.

    Attributes:
        filter (:py:mod:`hoomd.filter`): Subset of particles on which to apply
            active forces.
        rotation_diff (float): rotational diffusion constant, :math:`D_r`, for
            all particles in the group.
        active_force (tuple): active force vector in reference to the
            orientation of a particle. It is defined per particle type and stays
            constant during the simulation.
        active_torque (tuple): active torque vector in reference to the
            orientation of a particle. It is defined per particle type and stays
            constant during the simulation.

    :py:class:`Active` specifies that an active force should be added to all
    particles.  Obeys :math:`\delta {\bf r}_i = \delta t v_0 \hat{p}_i`, where
    :math:`v_0` is the active velocity. In 2D :math:`\hat{p}_i = (\cos \theta_i,
    \sin \theta_i)` is the active force vector for particle :math:`i` and the
    diffusion of the active force vector follows :math:`\delta \theta / \delta t
    = \sqrt{2 D_r / \delta t} \Gamma`, where :math:`D_r` is the rotational
    diffusion constant, and the gamma function is a unit-variance random
    variable, whose components are uncorrelated in time, space, and between
    particles.  In 3D, :math:`\hat{p}_i` is a unit vector in 3D space, and
    diffusion follows :math:`\delta \hat{p}_i / \delta t = \sqrt{2 D_r / \delta
    t} \Gamma (\hat{p}_i (\cos \theta - 1) + \hat{p}_r \sin \theta)`, where
    :math:`\hat{p}_r` is an uncorrelated random unit vector. The persistence
    length of an active particle's path is :math:`v_0 / D_r`.  The rotational
    diffusion is applied to the orientation vector/quaternion of each particle.
    This implies that both the active force and the active torque vectors in the
    particle frame stay constant during the simulation. Hence, the active forces
    in the system frame are composed of the forces in particle frame and the
    current orientation of the particle.

    Examples::

        all = filter.All()
        active = hoomd.md.force.Active(
            filter=hoomd.filter.All(), rotation_diff=0.01
            )
        active.active_force['A','B'] = (1,0,0)
        active.active_torque['A','B'] = (0,0,0)
    """

    def __init__(self, filter, rotation_diff=0.1):
        # store metadata
        param_dict = ParameterDict(
            filter=ParticleFilter,
            rotation_diff=float(rotation_diff),
            constraint=OnlyTypes(ConstraintForce,
                                 allow_none=True,
                                 preprocess=_ellip_preprocessing),
        )
        param_dict.update(
            dict(
                constraint=None,
                rotation_diff=rotation_diff,
                filter=filter,
            ))
        # set defaults
        self._param_dict.update(param_dict)

        active_force = TypeParameter(
            "active_force",
            type_kind="particle_types",
            param_dict=TypeParameterDict((1.0, 0.0, 0.0), len_keys=1),
        )
        active_torque = TypeParameter(
            "active_torque",
            type_kind="particle_types",
            param_dict=TypeParameterDict((0.0, 0.0, 0.0), len_keys=1),
        )

        self._extend_typeparam([active_force, active_torque])

    def _add(self, simulation):
        """Add the operation to a simulation.

        Active forces use RNGs. Warn the user if they did not set the seed.
        """
        if simulation is not None:
            simulation._warn_if_seed_unset()

        super()._add(simulation)

    def _attach(self):
        # initialize the reflected c++ class
        if isinstance(self._simulation.device, hoomd.device.CPU):
            my_class = _md.ActiveForceCompute
        else:
            my_class = _md.ActiveForceComputeGPU

        self._cpp_obj = my_class(
            self._simulation.state._cpp_sys_def,
            self._simulation.state._get_group(self.filter),
            self.rotation_diff,
            _hoomd.make_scalar3(0, 0, 0),
            0,
            0,
            0,
        )

        # Attach param_dict and typeparam_dict
        super()._attach()
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk
import os, os.path

import codecs  #evitar problemas escribiendo el log

import re

# Globals

NOMBRE_LOG = "./SeRe.log"

SEP_TEMP = "S"
SEP_CAPI = "E"

modelos = set([])

#REGEX_MODEL = "(.*)[S|T](\d\d)E(\d\d)(.*)\.(\w\w\w)"

modelos.add("(.*)[S|T](\d\d)E(\d\d)(.*)\.(\w\w\w)")

#para las que tienen el formato con una x delante

#REGEX_MODEL = "(.*)([\d|\d\d])x(\d\d)(.*)\.(\w\w\w)"

modelos.add("(.*)([\d|\d\d])x(\d\d)(.*)\.(\w\w\w)")

expres=set([])

for mod in modelos:

    expres.add(re.compile(mod))

# Inicializamos variables necesarias

nserie=""   #esto es temporal, habra que poner entrada para elegir

# flags:

flag_log = False
flag_serie = False

capis = set([])

# nuestra bonita clase capitulos

# file tiene que ser un string con una estructura de archivo de serie, con una
#temporada y un capitulo

#Siempre hay que verificar el campo true que es el que nos dice si tiene una
# estructura valida de archivo
class Capitulo:

    def __init__(self,file,expres):

        self.filename = file

        for expre in expres:

            blocks = expre.match(file)

# chequea si cumple el regex y si no lo marca como no valido

            if blocks == None:
                self.valid = False

            else:
                self.valid = True

                self.show = blocks.groups()[0]

                temp = blocks.groups()[1]
                if len(temp) == 1:
                     temp="0"+temp
                self.temp = temp

                self.capi = blocks.groups()[2]
                self.vers = blocks.groups()[3]
                self.ext  = blocks.groups()[4]
                break

        # Vamos con las extensiones

###       Este bloque mola un huevo, pero habra que reescribirlo con expresiones regulares
##
##        bloque = list(file)
##
##        ext =[]
##
##        a = bloque.pop()
##
##        while not (a == "."):
##            ext.append(a)
##            a = bloque.pop()
##
##        ext.reverse()
##
##
##        #esto mola un huevo para unir, pero pos
##        self.ext = "".join(str(x) for x in ext)
##

# helper function to get two functions en un boton

def combine_funcs(*funcs):
    def combined_func(*args, **kwargs):
        for f in funcs:
            f(*args, **kwargs)
    return combined_func

# funcion auxiliar que saca un menu para pedir el directorio y lo cambia
def getdir():
    direc=tk.filedialog.askdirectory()
    os.chdir(direc)

# mete en el set capis un Capitulo por cada archivo
def procdir():

    global log

    if not(flag_serie):
        return()

    openlog()

    base = os.listdir()

    for arch in base:

#   Ignoramos los archivos ocultos, por que hay un huevo por tanto mac
#   Tambien ignoro las cosas que no tienen un punto (y que no van a ser archivos interesantes, y ademas nos clava la cosa)
        if (not(arch[0] == ".") and ("." in arch)):
            capis.add(Capitulo(arch,expres))

    for capi in list(capis):
        if capi.valid:

            newname = nserie + SEP_TEMP + capi.temp + SEP_CAPI + capi.capi + "." + capi.ext

# compruebo que no este ya en el formato
            if newname == capi.filename:
                log.write("old  "+capi.filename+ " ya estaba en el formato"+"\n")

# compruebo que el archivo destino no existe (para no pisarlo)
            elif os.path.isfile(newname):
                log.write("rep  "+ capi.filename+" el archivo objetivo ya existia"+"\n")

            else:
                os.rename(capi.filename,newname)
                log.write("mv   " +capi.filename + " -> " + newname +"\n")

# si no es valido lo logamos tambien
        else:
            log.write("not  " + capi.filename + " no me parece un capitulo"+"\n")
# una vez hemos acabado con el lo sacamos del set por si queremos hacer varios directorios seguidos

        capis.discard(capi)

    closelog()

# esta se usa a veces de placeholder
def test():
    print("test")

def openlog():

    global log
    global flag_log

    if flag_log:
        closelog()

### Esto funcionaba bien, pero es mas comodo usarlo con un flag
### compruebo si ya esta abierto, y si lo esta lo cierro
##    try:
##      log
##    except NameError:
##      pass
##    else:
##      log.close()

#   Abro el que haria falta para este directorio

#    log = open(NOMBRE_LOG,"a")
# a ver si evitamos problema con los nombres de archivo

    log=codecs.open(NOMBRE_LOG, "a", "utf-8-sig")

    flag_log=True
# TODO:  aqui hay que gestionar el si es viejo, para encender el undo

def closelog():

    global log
    global flag_log

    if flag_log:
        log.close()

    flag_log = False

### esto era chulo pero creo que no hace falta ta
### compruebo si ya esta abierto, y si lo esta lo cierro
##    try:
##      log
##    except NameError:
##      pass
##    else:
##      log.close()

def aceptar():

    global nserie, label_nombre,flag_serie

    nserie = cuadro_nom.get()
    label_nombre.config(text=("Nombre base: "+nserie))
    flag_serie = True

def update_labeldir():

    label_dir.config(text=("directorio activo"+os.getcwd()))

def salir():
    #compruebo si hay log abierto y si lo hay lo cierro
    global log
    global root

    if flag_log:
        log.close()

    root.destroy()

# Y aqui empezamos el meollo del asunto
root = tk.Tk()
root.title("SeRe")

#creamos los botones
#en algun momento hay que dejarlo con grid

eledir = tk.Button(root, text='Elegir directorio', width=50,
                   command=combine_funcs(getdir,update_labeldir))
eledir.pack()

label_dir = tk.Label(root,text="directorio activo"+os.getcwd())

label_dir.pack()

procdir = tk.Button(root, text='Procesar directorio', width=50,
                   command=combine_funcs(test,procdir))

procdir.pack()

cuadro_nom = tk.Entry(root)

cuadro_nom.pack()

acept=tk.Button(root, text='Aceptar', width=50,
                   command=aceptar)

acept.pack()

label_nombre =tk.Label(root,text="elija un nombre base")
label_nombre.pack()

bquit = tk.Button(root, text = 'Quit', width = 50, command = salir)
bquit.pack()

root.mainloop()
<commit_msg>Primera version con grid<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk
import os, os.path

import codecs  #evitar problemas escribiendo el log

import re

# Globals

NOMBRE_LOG = "./SeRe.log"

SEP_TEMP = "S"
SEP_CAPI = "E"

modelos = set([])

#REGEX_MODEL = "(.*)[S|T](\d\d)E(\d\d)(.*)\.(\w\w\w)"

modelos.add("(.*)[S|T](\d\d)E(\d\d)(.*)\.(\w\w\w)")

#para las que tienen el formato con una x delante

#REGEX_MODEL = "(.*)([\d|\d\d])x(\d\d)(.*)\.(\w\w\w)"

modelos.add("(.*)([\d|\d\d])x(\d\d)(.*)\.(\w\w\w)")

expres=set([])

for mod in modelos:

    expres.add(re.compile(mod))

# Inicializamos variables necesarias

nserie=""   #esto es temporal, habra que poner entrada para elegir

# flags:

flag_log = False
flag_serie = False

capis = set([])

# nuestra bonita clase capitulos

# file tiene que ser un string con una estructura de archivo de serie, con una
#temporada y un capitulo

#Siempre hay que verificar el campo true que es el que nos dice si tiene una
# estructura valida de archivo
class Capitulo:

    def __init__(self,file,expres):

        self.filename = file

        for expre in expres:

            blocks = expre.match(file)

# chequea si cumple el regex y si no lo marca como no valido

            if blocks == None:
                self.valid = False

            else:
                self.valid = True

                self.show = blocks.groups()[0]

                temp = blocks.groups()[1]
                if len(temp) == 1:
                     temp="0"+temp
                self.temp = temp

                self.capi = blocks.groups()[2]
                self.vers = blocks.groups()[3]
                self.ext  = blocks.groups()[4]
                break

        # Vamos con las extensiones

###       Este bloque mola un huevo, pero habra que reescribirlo con expresiones regulares
##
##        bloque = list(file)
##
##        ext =[]
##
##        a = bloque.pop()
##
##        while not (a == "."):
##            ext.append(a)
##            a = bloque.pop()
##
##        ext.reverse()
##
##
##        #esto mola un huevo para unir, pero pos
##        self.ext = "".join(str(x) for x in ext)
##

# helper function to get two functions en un boton

def combine_funcs(*funcs):
    def combined_func(*args, **kwargs):
        for f in funcs:
            f(*args, **kwargs)
    return combined_func

# funcion auxiliar que saca un menu para pedir el directorio y lo cambia
def getdir():
    direc=tk.filedialog.askdirectory()
    os.chdir(direc)

# mete en el set capis un Capitulo por cada archivo
def procdir():

    global log

    if not(flag_serie):
        return()

    openlog()

    base = os.listdir()

    for arch in base:

#   Ignoramos los archivos ocultos, por que hay un huevo por tanto mac
#   Tambien ignoro las cosas que no tienen un punto (y que no van a ser archivos interesantes, y ademas nos clava la cosa)
        if (not(arch[0] == ".") and ("." in arch)):
            capis.add(Capitulo(arch,expres))

    for capi in list(capis):
        if capi.valid:

            newname = nserie + SEP_TEMP + capi.temp + SEP_CAPI + capi.capi + "." + capi.ext

# compruebo que no este ya en el formato
            if newname == capi.filename:
                log.write("old  "+capi.filename+ " ya estaba en el formato"+"\n")

# compruebo que el archivo destino no existe (para no pisarlo)
            elif os.path.isfile(newname):
                log.write("rep  "+ capi.filename+" el archivo objetivo ya existia"+"\n")

            else:
                os.rename(capi.filename,newname)
                log.write("mv   " +capi.filename + " -> " + newname +"\n")

# si no es valido lo logamos tambien
        else:
            log.write("not  " + capi.filename + " no me parece un capitulo"+"\n")
# una vez hemos acabado con el lo sacamos del set por si queremos hacer varios directorios seguidos

        capis.discard(capi)

    closelog()

# esta se usa a veces de placeholder
def test():
    print("test")

def openlog():

    global log
    global flag_log

    if flag_log:
        closelog()

### Esto funcionaba bien, pero es mas comodo usarlo con un flag
### compruebo si ya esta abierto, y si lo esta lo cierro
##    try:
##      log
##    except NameError:
##      pass
##    else:
##      log.close()

#   Abro el que haria falta para este directorio

#    log = open(NOMBRE_LOG,"a")
# a ver si evitamos problema con los nombres de archivo

    log=codecs.open(NOMBRE_LOG, "a", "utf-8-sig")

    flag_log=True
# TODO:  aqui hay que gestionar el si es viejo, para encender el undo

def closelog():

    global log
    global flag_log

    if flag_log:
        log.close()

    flag_log = False

### esto era chulo pero creo que no hace falta ta
### compruebo si ya esta abierto, y si lo esta lo cierro
##    try:
##      log
##    except NameError:
##      pass
##    else:
##      log.close()

def aceptar():

    global nserie, label_nombre,flag_serie

    nserie = cuadro_nom.get()
    label_nombre.config(text=("Nombre base: "+nserie))
    flag_serie = True

def update_labeldir():

    label_dir.config(text=("directorio activo"+os.getcwd()))

def salir():
    #compruebo si hay log abierto y si lo hay lo cierro
    global log
    global root

    if flag_log:
        log.close()

    root.destroy()

# Y aqui empezamos el meollo del asunto
root = tk.Tk()
root.title("SeRe")

#creamos los botones
#en algun momento hay que dejarlo con grid

eledir = tk.Button(root, text='Elegir directorio',
                   command=combine_funcs(getdir,update_labeldir))
#eledir.pack()

label_dir = tk.Label(root,text="directorio activo"+os.getcwd())

#label_dir.pack()
#width = 50

procdir = tk.Button(root, text='Procesar directorio',
                   command=combine_funcs(test,procdir))

#procdir.pack()

cuadro_nom = tk.Entry(root)

#cuadro_nom.pack()

acept=tk.Button(root, text='Aceptar',
                   command=aceptar)

#acept.pack()

label_nombre =tk.Label(root,text="elija un nombre base")
#label_nombre.pack()

bquit = tk.Button(root, text = 'Quit', command = salir)
#bquit.pack()

eledir.grid(row=0,column=1)
label_dir.grid(row=0,column=0)

cuadro_nom.grid(row=2,column=0)
acept.grid(row=2,column=1)

label_nombre.grid(row=1,columnspan=2)

procdir.grid(row=3,columnspan=2)

bquit.grid(row=4,columnspan=2)

root.mainloop()
<|endoftext|>