<commit_before>"""
pysat.utils.registry - user module registry operations in pysat
=========================================

Instantiating a pysat.Instrument object for a particular data set
requires a module for each instrument with routines that download,
load, and clean the respective data. Instructions for adding support
for external modules may be found here
https://pysat.readthedocs.io/en/latest/new_instrument.html

This module enables pysat to provide the same user experience for
external or custom instrument modules, as for those shipped
with pysat, by maintaining information about these outside
instrument files.

Instrument support modules must be registered before use. For
example, assume there is an implementation for myInstrument in the
module my.package.myInstrument having  platform and name attributes
'myplatform' and 'myname'. Such an instrument may be registered with

    registry.register('my.package.myInstrument')

or, alternatively, by instatiating an Instrument object using the
inst_module keyword

    Instrument(inst_module = 'my.package.myInstrument')

Either way, the full module name "my.package.myInstrument" will be
registered in pysat_dir/user_modules.txt and is also listed in
pysat.user_modules.

Once registered, subsequent calls to Instrument may use the platform
and name:

    Instrument('myplatform', 'myname')

pysat will search the instruments shipped with pysat before
checking the user_modules registry.
"""

import pysat
import os

def load_saved_modules():
    """get list of modules from user_modules.txt"""
    saved_modules = []
    with open(os.path.join(pysat.pysat_dir, 'user_modules.txt'), 'r') as f:
        for line in f:
            if line != '' and (line is not None):
                saved_modules.append(line.strip())
    return saved_modules

def store():
    """Rewrite user_modules.txt based on current listing"""
    with open(os.path.join(pysat.pysat_dir, 'user_modules.txt'), 'w') as f:
        for mod in pysat.user_modules:
            f.write(mod + '\n')

def register(module_name):
    """Registers a user module by name, returning the loaded module

    Parameters
    -----------
    module_name : string
        specify package name and instrument module
        examples:
            my.package.name.my_instrument
            my.pckage.name.myInstrument

    Returns
    --------
    Updates the user module registry specified in pysat_dir/user_module.txt

    Notes
    ------
    Modules should be importable using
        from my.package.name import my_instrument

    Module names do not have to follow the pysat platform_name naming
    convection.

    Warning: Registering a module that contains code other than pysat
    instrument files could result in unexpected consequences.

    Examples
    ---------
    from pysat import Instrument, user_modules
    from pysat.utils import registry

    registry.register('my.package.name.myInstrument')
    assert 'my.package.name.myInstrument' in user_modules

    testInst = Instrument()

    """
    import importlib
    inst_module = importlib.import_module(module_name)

    if module_name not in pysat.user_modules:
        print('registering user module {}'.format(module_name))
        print('warning')
        pysat.user_modules.append(module_name)
        store()

    return inst_module

def remove(*module_names):
    """Removes module from registered user modules

    Parameters
    -----------
    module_names : str or multiple str
        full module package names to remove from registry

    Notes
    ------
    Current list of user modules can be retrieved from pysat.user_modules

    """
    for module_name in module_names:
        try:
            pysat.user_modules.remove(module_name)
            store()
        except ValueError:
            print('User module {} not found'.format(module_name))

<commit_msg>removed print warning<commit_after>"""
pysat.utils.registry - user module registry operations in pysat
=========================================

Instantiating a pysat.Instrument object for a particular data set
requires a module for each instrument with routines that download,
load, and clean the respective data. Instructions for adding support
for external modules may be found here
https://pysat.readthedocs.io/en/latest/new_instrument.html

This module enables pysat to provide the same user experience for
external or custom instrument modules, as for those shipped
with pysat, by maintaining information about these outside
instrument files.

Instrument support modules must be registered before use. For
example, assume there is an implementation for myInstrument in the
module my.package.myInstrument having  platform and name attributes
'myplatform' and 'myname'. Such an instrument may be registered with

    registry.register('my.package.myInstrument')

or, alternatively, by instatiating an Instrument object using the
inst_module keyword

    Instrument(inst_module = 'my.package.myInstrument')

Either way, the full module name "my.package.myInstrument" will be
registered in pysat_dir/user_modules.txt and is also listed in
pysat.user_modules.

Once registered, subsequent calls to Instrument may use the platform
and name:

    Instrument('myplatform', 'myname')

pysat will search the instruments shipped with pysat before
checking the user_modules registry.
"""

import pysat
import os

def load_saved_modules():
    """get list of modules from user_modules.txt"""
    saved_modules = []
    with open(os.path.join(pysat.pysat_dir, 'user_modules.txt'), 'r') as f:
        for line in f:
            if line != '' and (line is not None):
                saved_modules.append(line.strip())
    return saved_modules

def store():
    """Rewrite user_modules.txt based on current listing"""
    with open(os.path.join(pysat.pysat_dir, 'user_modules.txt'), 'w') as f:
        for mod in pysat.user_modules:
            f.write(mod + '\n')

def register(module_name):
    """Registers a user module by name, returning the loaded module

    Parameters
    -----------
    module_name : string
        specify package name and instrument module
        examples:
            my.package.name.my_instrument
            my.pckage.name.myInstrument

    Returns
    --------
    Updates the user module registry specified in
    pysat_dir/user_module.txt

    Notes
    ------
    Modules should be importable using
        from my.package.name import my_instrument

    Module names do not have to follow the pysat platform_name naming
    convection.

    Warning: Registering a module that contains code other than
    pysat instrument files could result in unexpected consequences.

    Examples
    ---------
    from pysat import Instrument, user_modules
    from pysat.utils import registry

    registry.register('my.package.name.myInstrument')
    assert 'my.package.name.myInstrument' in user_modules

    testInst = Instrument()

    """
    import importlib
    inst_module = importlib.import_module(module_name)

    if module_name not in pysat.user_modules:
        print('registering user module {}'.format(module_name))
        pysat.user_modules.append(module_name)
        store()

    return inst_module

def remove(*module_names):
    """Removes module from registered user modules

    Parameters
    -----------
    module_names : str or multiple str
        full module package names to remove from registry

    Notes
    ------
    Current list of user modules can be retrieved from pysat.user_modules

    """
    for module_name in module_names:
        try:
            pysat.user_modules.remove(module_name)
            store()
        except ValueError:
            print('User module {} not found'.format(module_name))

<|endoftext|>


<commit_before># Copyright 2017-2018 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS-IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Launch the game and set up communication."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from absl import logging
import os
import shutil
import subprocess
import tempfile
import time

from absl import flags
from future.builtins import range  # pylint: disable=redefined-builtin

import portpicker
from pysc2.lib import remote_controller
from pysc2.lib import stopwatch

flags.DEFINE_bool("sc2_verbose", False, "Enable SC2 verbose logging.")
FLAGS = flags.FLAGS

sw = stopwatch.sw

class SC2LaunchError(Exception):
  pass

class StarcraftProcess(object):
  """Launch a starcraft server, initialize a controller, and later, clean up.

  This is best used from run_configs, which decides which version to run, and
  where to find it.

  It is important to call `close` or use it as a context manager, otherwise
  you'll likely leak temp files and SC2 processes.
  """

  def __init__(self, run_config, exec_path, version, full_screen=False,
               extra_args=None, verbose=False, host=None, port=None,
               connect=True, timeout_seconds=None, window_size=(640, 480),
               window_loc=(50, 50), **kwargs):
    """Launch the SC2 process.

    Args:
      run_config: A `run_configs.lib.RunConfig` object.
      exec_path: The path to the binary to run.
      version: A `run_configs.lib.Version` object.
      full_screen: Whether to launch the game window full_screen on win/mac.
      extra_args: A list of additional args for the SC2 process.
      verbose: Whether to have the SC2 process do verbose logging.
      host: Which ip for the game to listen on for its websocket. This is
          usually "127.0.0.1", or "::1", but could be others as well.
      port: Port SC2 should listen on for the websocket.
      connect: Whether to create a RemoteController to connect.
      timeout_seconds: The timeout for the remote controller.
      window_size: If not full screen, how big should the window be?
      window_loc: If not full screen, where should the window be?
      **kwargs: Anything extra for _launch, which is useful for subclasses.
    """
    self._proc = None
    self._controller = None
    self._check_exists(exec_path)
    self._tmp_dir = tempfile.mkdtemp(prefix="sc-", dir=run_config.tmp_dir)
    self._host = host or "127.0.0.1"
    self._port = port or portpicker.pick_unused_port()
    self._version = version

    args = [
        exec_path,
        "-listen", self._host,
        "-port", str(self._port),
        "-dataDir", os.path.join(run_config.data_dir, ""),
        "-tempDir", os.path.join(self._tmp_dir, ""),
    ]
    if ":" in self._host:
      args += ["-ipv6"]
    if full_screen:
      args += ["-displayMode", "1"]
    else:
      args += [
          "-displayMode", "0",
          "-windowwidth", str(window_size[0]),
          "-windowheight", str(window_size[1]),
          "-windowx", str(window_loc[0]),
          "-windowy", str(window_loc[1]),
      ]

    if verbose or FLAGS.sc2_verbose:
      args += ["-verbose"]
    if self._version and self._version.data_version:
      args += ["-dataVersion", self._version.data_version.upper()]
    if extra_args:
      args += extra_args
    logging.info("Launching SC2: %s", " ".join(args))
    try:
      with sw("startup"):
        self._proc = self._launch(run_config, args, **kwargs)
        if connect:
          self._controller = remote_controller.RemoteController(
              self._host, self._port, self, timeout_seconds=timeout_seconds)
    except:
      self.close()
      raise

  @sw.decorate
  def close(self):
    """Shut down the game and clean up."""
    if hasattr(self, "_controller") and self._controller:
      self._controller.quit()
      self._controller.close()
      self._controller = None
    self._shutdown()
    if hasattr(self, "_port") and self._port:
      portpicker.return_port(self._port)
      self._port = None
    if hasattr(self, "_tmp_dir") and os.path.exists(self._tmp_dir):
      shutil.rmtree(self._tmp_dir)

  @property
  def controller(self):
    return self._controller

  @property
  def host(self):
    return self._host

  @property
  def port(self):
    return self._port

  @property
  def version(self):
    return self._version

  def __enter__(self):
    return self.controller

  def __exit__(self, unused_exception_type, unused_exc_value, unused_traceback):
    self.close()

  def __del__(self):
    # Prefer using a context manager, but this cleans most other cases.
    self.close()

  def _check_exists(self, exec_path):
    if not os.path.isfile(exec_path):
      raise RuntimeError("Trying to run '%s', but it doesn't exist" % exec_path)
    if not os.access(exec_path, os.X_OK):
      raise RuntimeError(
          "Trying to run '%s', but it isn't executable." % exec_path)

  def _launch(self, run_config, args, **kwargs):
    """Launch the process and return the process object."""
    del kwargs
    try:
      with sw("popen"):
        return subprocess.Popen(args, cwd=run_config.cwd, env=run_config.env)
    except OSError:
      logging.exception("Failed to launch")
      raise SC2LaunchError("Failed to launch: %s" % args)

  def _shutdown(self):
    """Terminate the sub-process."""
    if self._proc:
      ret = _shutdown_proc(self._proc, 3)
      logging.info("Shutdown with return code: %s", ret)
      self._proc = None

  @property
  def running(self):
    # poll returns None if it's running, otherwise the exit code.
    return self._proc and (self._proc.poll() is None)

def _shutdown_proc(p, timeout):
  """Wait for a proc to shut down, then terminate or kill it after `timeout`."""
  freq = 10  # how often to check per second
  for _ in range(1 + timeout * freq):
    ret = p.poll()
    if ret is not None:
      logging.info("Shutdown gracefully.")
      return ret
    time.sleep(1 / freq)
  logging.warning("Killing the process.")
  p.kill()
  return p.wait()
<commit_msg>Shorter, more concise doc strings.<commit_after># Copyright 2017-2018 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS-IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Launch the game and set up communication."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from absl import logging
import os
import shutil
import subprocess
import tempfile
import time

from absl import flags
from future.builtins import range  # pylint: disable=redefined-builtin

import portpicker
from pysc2.lib import remote_controller
from pysc2.lib import stopwatch

flags.DEFINE_bool("sc2_verbose", False, "Enable SC2 verbose logging.")
FLAGS = flags.FLAGS

sw = stopwatch.sw

class SC2LaunchError(Exception):
  pass

class StarcraftProcess(object):
  """Launch a starcraft server, initialize a controller, and later, clean up.

  This is best used from run_configs, which decides which version to run, and
  where to find it.

  It is important to call `close` or use it as a context manager, otherwise
  you'll likely leak temp files and SC2 processes.
  """

  def __init__(self, run_config, exec_path, version, full_screen=False,
               extra_args=None, verbose=False, host=None, port=None,
               connect=True, timeout_seconds=None, window_size=(640, 480),
               window_loc=(50, 50), **kwargs):
    """Launch the SC2 process.

    Args:
      run_config: `run_configs.lib.RunConfig` object.
      exec_path: Path to the binary to run.
      version: `run_configs.lib.Version` object.
      full_screen: Whether to launch the game window full_screen on win/mac.
      extra_args: List of additional args for the SC2 process.
      verbose: Whether to have the SC2 process do verbose logging.
      host: IP for the game to listen on for its websocket. This is
          usually "127.0.0.1", or "::1", but could be others as well.
      port: Port SC2 should listen on for the websocket.
      connect: Whether to create a RemoteController to connect.
      timeout_seconds: Timeout for the remote controller.
      window_size: Screen size if not full screen.
      window_loc: Screen location if not full screen.
      **kwargs: Extra arguments for _launch (useful for subclasses).
    """
    self._proc = None
    self._controller = None
    self._check_exists(exec_path)
    self._tmp_dir = tempfile.mkdtemp(prefix="sc-", dir=run_config.tmp_dir)
    self._host = host or "127.0.0.1"
    self._port = port or portpicker.pick_unused_port()
    self._version = version

    args = [
        exec_path,
        "-listen", self._host,
        "-port", str(self._port),
        "-dataDir", os.path.join(run_config.data_dir, ""),
        "-tempDir", os.path.join(self._tmp_dir, ""),
    ]
    if ":" in self._host:
      args += ["-ipv6"]
    if full_screen:
      args += ["-displayMode", "1"]
    else:
      args += [
          "-displayMode", "0",
          "-windowwidth", str(window_size[0]),
          "-windowheight", str(window_size[1]),
          "-windowx", str(window_loc[0]),
          "-windowy", str(window_loc[1]),
      ]

    if verbose or FLAGS.sc2_verbose:
      args += ["-verbose"]
    if self._version and self._version.data_version:
      args += ["-dataVersion", self._version.data_version.upper()]
    if extra_args:
      args += extra_args
    logging.info("Launching SC2: %s", " ".join(args))
    try:
      with sw("startup"):
        self._proc = self._launch(run_config, args, **kwargs)
        if connect:
          self._controller = remote_controller.RemoteController(
              self._host, self._port, self, timeout_seconds=timeout_seconds)
    except:
      self.close()
      raise

  @sw.decorate
  def close(self):
    """Shut down the game and clean up."""
    if hasattr(self, "_controller") and self._controller:
      self._controller.quit()
      self._controller.close()
      self._controller = None
    self._shutdown()
    if hasattr(self, "_port") and self._port:
      portpicker.return_port(self._port)
      self._port = None
    if hasattr(self, "_tmp_dir") and os.path.exists(self._tmp_dir):
      shutil.rmtree(self._tmp_dir)

  @property
  def controller(self):
    return self._controller

  @property
  def host(self):
    return self._host

  @property
  def port(self):
    return self._port

  @property
  def version(self):
    return self._version

  def __enter__(self):
    return self.controller

  def __exit__(self, unused_exception_type, unused_exc_value, unused_traceback):
    self.close()

  def __del__(self):
    # Prefer using a context manager, but this cleans most other cases.
    self.close()

  def _check_exists(self, exec_path):
    if not os.path.isfile(exec_path):
      raise RuntimeError("Trying to run '%s', but it doesn't exist" % exec_path)
    if not os.access(exec_path, os.X_OK):
      raise RuntimeError(
          "Trying to run '%s', but it isn't executable." % exec_path)

  def _launch(self, run_config, args, **kwargs):
    """Launch the process and return the process object."""
    del kwargs
    try:
      with sw("popen"):
        return subprocess.Popen(args, cwd=run_config.cwd, env=run_config.env)
    except OSError:
      logging.exception("Failed to launch")
      raise SC2LaunchError("Failed to launch: %s" % args)

  def _shutdown(self):
    """Terminate the sub-process."""
    if self._proc:
      ret = _shutdown_proc(self._proc, 3)
      logging.info("Shutdown with return code: %s", ret)
      self._proc = None

  @property
  def running(self):
    # poll returns None if it's running, otherwise the exit code.
    return self._proc and (self._proc.poll() is None)

def _shutdown_proc(p, timeout):
  """Wait for a proc to shut down, then terminate or kill it after `timeout`."""
  freq = 10  # how often to check per second
  for _ in range(1 + timeout * freq):
    ret = p.poll()
    if ret is not None:
      logging.info("Shutdown gracefully.")
      return ret
    time.sleep(1 / freq)
  logging.warning("Killing the process.")
  p.kill()
  return p.wait()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

"""SQL database output function."""

from __future__ import annotations

__all__ = ['ProjectFormat', 'ProjectWidget']
__author__ = "Yuan Chang"
__copyright__ = "Copyright (C) 2016-2021"
__license__ = "AGPL"
__email__ = "pyslvs@gmail.com"

from typing import TYPE_CHECKING, Mapping, Union
from qtpy.QtCore import Slot, QFileInfo, QDateTime
from qtpy.QtWidgets import (
    QUndoView,
    QVBoxLayout,
    QWidget,
    QInputDialog,
    QMessageBox,
)
from qtpy.QtGui import QPixmap, QIcon
from pyslvs import example_list, all_examples
from pyslvs_ui.info import logger, size_format
from pyslvs_ui.qt_patch import qt_image_format
from .project_yaml import YamlEditor
from .project_pickle import PickleEditor
from .project_ui import Ui_Form
from .format_editor import ProjectFormat

if TYPE_CHECKING:
    from pyslvs_ui.widgets import MainWindowBase

class ProjectWidget(QWidget, Ui_Form):
    """The table that stored project data and changes."""

    def __init__(self, parent: MainWindowBase):
        super(ProjectWidget, self).__init__(parent)
        self.setupUi(self)
        # Undo view
        self.command_stack = parent.cmd_stack
        undo_view = QUndoView(self.command_stack)
        undo_view.setEmptyLabel("~Start Pyslvs")
        w = QWidget(self)
        layout = QVBoxLayout(w)
        layout.addWidget(undo_view)
        history_icon = QIcon(QPixmap(":/icons/history.png"))
        self.history_tabs.addTab(w, history_icon, "Mechanism")
        # Settings
        self.prefer = parent.prefer
        # Check project saved function
        self.project_saved = parent.project_saved
        # Open file dialog
        self.input_from = parent.input_from
        # Parse function
        self.parse_expression = parent.parse_expression
        # Call to load inputs variables data
        self.load_inputs = parent.inputs_widget.add_inputs_variables
        # Clear function for main window
        self.main_clear = parent.clear
        # Environment path
        self.env_path = parent.env_path

        self.overview_button.clicked.connect(parent.show_overview)
        self.ex_expression_button.clicked.connect(parent.show_expr)
        self.ex_dxf_button.clicked.connect(parent.export_dxf)
        self.ex_slvs_button.clicked.connect(parent.export_slvs)
        self.ex_pmks_button.clicked.connect(parent.save_pmks)
        self.ex_py_button.clicked.connect(parent.py_script)
        self.ex_image_button.clicked.connect(parent.export_image)
        self.ex_capture_button.clicked.connect(parent.save_picture_clipboard)

        self.im_pmks_button.clicked.connect(parent.import_pmks_url)
        self.im_example_button.clicked.connect(lambda: self.load_example(is_import=True))

        self.background_option.textChanged.connect(parent.main_canvas.set_background)
        self.background_opacity_option.valueChanged.connect(parent.main_canvas.set_background_opacity)
        self.background_x_option.valueChanged.connect(parent.main_canvas.set_background_offset_x)
        self.background_y_option.valueChanged.connect(parent.main_canvas.set_background_offset_y)
        self.background_scale_option.valueChanged.connect(parent.main_canvas.set_background_scale)

        # Editors
        self.yaml_editor = YamlEditor(self, parent)
        self.pickle_editor = PickleEditor(self, parent)
        # Reset
        self.__file_name = QFileInfo()
        self.__changed = False
        self.reset()

    def reset(self) -> None:
        """Clear all the things that dependent on database."""
        self.set_file_name(self.env_path() + "/Untitled")
        self.__changed = False
        self.command_stack.clear()
        self.command_stack.setUndoLimit(self.prefer.undo_limit_option)
        self.set_background_config({})

    def set_file_name(self, file_name: str, *, is_example: bool = False) -> None:
        """Set file name."""
        self.__file_name = QFileInfo(file_name)
        self.file_name_label.setText(self.__file_name.fileName())
        self.path_label.setText(self.__file_name.absolutePath())
        self.owner_label.setText(self.__file_name.owner())
        time: QDateTime = self.__file_name.lastModified()
        self.last_modified_label.setText(time.toString())
        self.file_size_label.setText(size_format(self.__file_name.size()))
        if is_example:
            t = "Example (In memory)"
        elif self.file_exist():
            t = f"File ({self.prefer.file_type_option.format_name})"
        else:
            t = "In memory"
        self.type_label.setText(t)

    def file_name(self) -> QFileInfo:
        """Expose file name."""
        return self.__file_name

    def file_path(self) -> str:
        """Expose absolute file path."""
        return self.__file_name.absoluteFilePath()

    def base_file_name(self) -> str:
        """Expose base file name."""
        return self.__file_name.baseName()

    def file_suffix(self) -> str:
        """Expose file name suffix."""
        return self.__file_name.suffix()

    def file_exist(self) -> bool:
        """Return true if the file is exist."""
        return self.__file_name.isFile()

    def set_changed(self, changed: bool) -> None:
        """Set file state."""
        self.__changed = changed

    def changed(self) -> bool:
        """Expose file state."""
        return self.__changed

    def save(self, file_name: str = "") -> None:
        """Save database, append commit to new branch function."""
        if not file_name:
            file_name = self.file_path()
        if self.prefer.file_type_option == ProjectFormat.PICKLE:
            self.pickle_editor.save(file_name)
        else:
            self.yaml_editor.save(file_name)
        self.set_file_name(file_name)

    def read(self, file_name: str) -> None:
        """Load database commit."""
        if not QFileInfo(file_name).isFile():
            QMessageBox.warning(self, "File not exist", "The path is invalid.")
            return
        if YamlEditor.test(file_name):
            self.yaml_editor.load(file_name)
        else:
            self.pickle_editor.load(file_name)
        if self.prefer.open_project_actions_option == 0:
            self.command_stack.clear()
            self.command_stack.setUndoLimit(self.prefer.undo_limit_option)
        self.set_file_name(file_name)

    def load_example(self, is_import: bool = False) -> bool:
        """Load example to new project."""
        # load example by expression
        example_name, ok = QInputDialog.getItem(
            self,
            "Examples",
            "Select an example to load:",
            all_examples(),
            0,
            False
        )
        if not ok:
            return False
        if not is_import:
            self.reset()
            self.main_clear()
            if self.prefer.open_project_actions_option == 1:
                self.command_stack.beginMacro("Add mechanism")
        expr, inputs = example_list(example_name)
        self.parse_expression(expr)
        if not is_import:
            if self.prefer.open_project_actions_option == 1:
                self.command_stack.endMacro()
                self.command_stack.beginMacro("Add inputs data")
            # Import without inputs data
            self.load_inputs(inputs)
            if self.prefer.open_project_actions_option == 0:
                self.command_stack.clear()
                self.command_stack.setUndoLimit(self.prefer.undo_limit_option)
            elif self.prefer.open_project_actions_option == 1:
                self.command_stack.endMacro()
        self.set_file_name(example_name, is_example=True)
        self.project_saved()
        logger.info(f"Example \"{example_name}\" has been loaded.")
        return True

    @Slot(name='on_background_choose_dir_clicked')
    def __background_choose_dir(self) -> None:
        """Choose background directory."""
        file_name = self.input_from("background image", qt_image_format)
        if file_name:
            self.background_option.setText(file_name)

    def background_config(self) -> Mapping[str, Union[str, float]]:
        """Return background config."""
        env = self.__file_name.absoluteDir()
        return {
            'background': env.relativeFilePath(self.background_option.text()),
            'background_x': self.background_x_option.value(),
            'background_y': self.background_y_option.value(),
            'background_scale': self.background_scale_option.value(),
            'background_opacity': self.background_opacity_option.value(),
        }

    def set_background_config(self, config: Mapping[str, Union[str, float]]) -> None:
        """Set background config by dict object."""
        env = self.__file_name.absoluteDir()
        file = QFileInfo(env, config.get('background', ""))
        path = file.absoluteFilePath()
        self.background_option.setText(path if file.isFile() else "")
        self.background_x_option.setValue(config.get('background_x', 0.))
        self.background_y_option.setValue(config.get('background_y', 0.))
        self.background_scale_option.setValue(config.get('background_scale', 1.))
        self.background_opacity_option.setValue(config.get('background_opacity', 1.))

    def get_background_path(self) -> str:
        """Get background path."""
        path = self.background_option.text()
        return path if QFileInfo(path).isFile() else ""
<commit_msg>Unify "untitled" path sep.<commit_after># -*- coding: utf-8 -*-

"""SQL database output function."""

from __future__ import annotations

__all__ = ['ProjectFormat', 'ProjectWidget']
__author__ = "Yuan Chang"
__copyright__ = "Copyright (C) 2016-2021"
__license__ = "AGPL"
__email__ = "pyslvs@gmail.com"

from typing import TYPE_CHECKING, Mapping, Union
from os.path import join
from qtpy.QtCore import Slot, QFileInfo, QDateTime
from qtpy.QtWidgets import (
    QUndoView,
    QVBoxLayout,
    QWidget,
    QInputDialog,
    QMessageBox,
)
from qtpy.QtGui import QPixmap, QIcon
from pyslvs import example_list, all_examples
from pyslvs_ui.info import logger, size_format
from pyslvs_ui.qt_patch import qt_image_format
from .project_yaml import YamlEditor
from .project_pickle import PickleEditor
from .project_ui import Ui_Form
from .format_editor import ProjectFormat

if TYPE_CHECKING:
    from pyslvs_ui.widgets import MainWindowBase

class ProjectWidget(QWidget, Ui_Form):
    """The table that stored project data and changes."""

    def __init__(self, parent: MainWindowBase):
        super(ProjectWidget, self).__init__(parent)
        self.setupUi(self)
        # Undo view
        self.command_stack = parent.cmd_stack
        undo_view = QUndoView(self.command_stack)
        undo_view.setEmptyLabel("~Start Pyslvs")
        w = QWidget(self)
        layout = QVBoxLayout(w)
        layout.addWidget(undo_view)
        history_icon = QIcon(QPixmap(":/icons/history.png"))
        self.history_tabs.addTab(w, history_icon, "Mechanism")
        # Settings
        self.prefer = parent.prefer
        # Check project saved function
        self.project_saved = parent.project_saved
        # Open file dialog
        self.input_from = parent.input_from
        # Parse function
        self.parse_expression = parent.parse_expression
        # Call to load inputs variables data
        self.load_inputs = parent.inputs_widget.add_inputs_variables
        # Clear function for main window
        self.main_clear = parent.clear
        # Environment path
        self.env_path = parent.env_path

        self.overview_button.clicked.connect(parent.show_overview)
        self.ex_expression_button.clicked.connect(parent.show_expr)
        self.ex_dxf_button.clicked.connect(parent.export_dxf)
        self.ex_slvs_button.clicked.connect(parent.export_slvs)
        self.ex_pmks_button.clicked.connect(parent.save_pmks)
        self.ex_py_button.clicked.connect(parent.py_script)
        self.ex_image_button.clicked.connect(parent.export_image)
        self.ex_capture_button.clicked.connect(parent.save_picture_clipboard)

        self.im_pmks_button.clicked.connect(parent.import_pmks_url)
        self.im_example_button.clicked.connect(lambda: self.load_example(is_import=True))

        self.background_option.textChanged.connect(parent.main_canvas.set_background)
        self.background_opacity_option.valueChanged.connect(parent.main_canvas.set_background_opacity)
        self.background_x_option.valueChanged.connect(parent.main_canvas.set_background_offset_x)
        self.background_y_option.valueChanged.connect(parent.main_canvas.set_background_offset_y)
        self.background_scale_option.valueChanged.connect(parent.main_canvas.set_background_scale)

        # Editors
        self.yaml_editor = YamlEditor(self, parent)
        self.pickle_editor = PickleEditor(self, parent)
        # Reset
        self.__file_name = QFileInfo()
        self.__changed = False
        self.reset()

    def reset(self) -> None:
        """Clear all the things that dependent on database."""
        self.set_file_name(join(self.env_path(), "Untitled"))
        self.__changed = False
        self.command_stack.clear()
        self.command_stack.setUndoLimit(self.prefer.undo_limit_option)
        self.set_background_config({})

    def set_file_name(self, file_name: str, *, is_example: bool = False) -> None:
        """Set file name."""
        self.__file_name = QFileInfo(file_name)
        self.file_name_label.setText(self.__file_name.fileName())
        self.path_label.setText(self.__file_name.absolutePath())
        self.owner_label.setText(self.__file_name.owner())
        time: QDateTime = self.__file_name.lastModified()
        self.last_modified_label.setText(time.toString())
        self.file_size_label.setText(size_format(self.__file_name.size()))
        if is_example:
            t = "Example (In memory)"
        elif self.file_exist():
            t = f"File ({self.prefer.file_type_option.format_name})"
        else:
            t = "In memory"
        self.type_label.setText(t)

    def file_name(self) -> QFileInfo:
        """Expose file name."""
        return self.__file_name

    def file_path(self) -> str:
        """Expose absolute file path."""
        return self.__file_name.absoluteFilePath()

    def base_file_name(self) -> str:
        """Expose base file name."""
        return self.__file_name.baseName()

    def file_suffix(self) -> str:
        """Expose file name suffix."""
        return self.__file_name.suffix()

    def file_exist(self) -> bool:
        """Return true if the file is exist."""
        return self.__file_name.isFile()

    def set_changed(self, changed: bool) -> None:
        """Set file state."""
        self.__changed = changed

    def changed(self) -> bool:
        """Expose file state."""
        return self.__changed

    def save(self, file_name: str = "") -> None:
        """Save database, append commit to new branch function."""
        if not file_name:
            file_name = self.file_path()
        if self.prefer.file_type_option == ProjectFormat.PICKLE:
            self.pickle_editor.save(file_name)
        else:
            self.yaml_editor.save(file_name)
        self.set_file_name(file_name)

    def read(self, file_name: str) -> None:
        """Load database commit."""
        if not QFileInfo(file_name).isFile():
            QMessageBox.warning(self, "File not exist", "The path is invalid.")
            return
        if YamlEditor.test(file_name):
            self.yaml_editor.load(file_name)
        else:
            self.pickle_editor.load(file_name)
        if self.prefer.open_project_actions_option == 0:
            self.command_stack.clear()
            self.command_stack.setUndoLimit(self.prefer.undo_limit_option)
        self.set_file_name(file_name)

    def load_example(self, is_import: bool = False) -> bool:
        """Load example to new project."""
        # load example by expression
        example_name, ok = QInputDialog.getItem(
            self,
            "Examples",
            "Select an example to load:",
            all_examples(),
            0,
            False
        )
        if not ok:
            return False
        if not is_import:
            self.reset()
            self.main_clear()
            if self.prefer.open_project_actions_option == 1:
                self.command_stack.beginMacro("Add mechanism")
        expr, inputs = example_list(example_name)
        self.parse_expression(expr)
        if not is_import:
            if self.prefer.open_project_actions_option == 1:
                self.command_stack.endMacro()
                self.command_stack.beginMacro("Add inputs data")
            # Import without inputs data
            self.load_inputs(inputs)
            if self.prefer.open_project_actions_option == 0:
                self.command_stack.clear()
                self.command_stack.setUndoLimit(self.prefer.undo_limit_option)
            elif self.prefer.open_project_actions_option == 1:
                self.command_stack.endMacro()
        self.set_file_name(example_name, is_example=True)
        self.project_saved()
        logger.info(f"Example \"{example_name}\" has been loaded.")
        return True

    @Slot(name='on_background_choose_dir_clicked')
    def __background_choose_dir(self) -> None:
        """Choose background directory."""
        file_name = self.input_from("background image", qt_image_format)
        if file_name:
            self.background_option.setText(file_name)

    def background_config(self) -> Mapping[str, Union[str, float]]:
        """Return background config."""
        env = self.__file_name.absoluteDir()
        return {
            'background': env.relativeFilePath(self.background_option.text()),
            'background_x': self.background_x_option.value(),
            'background_y': self.background_y_option.value(),
            'background_scale': self.background_scale_option.value(),
            'background_opacity': self.background_opacity_option.value(),
        }

    def set_background_config(self, config: Mapping[str, Union[str, float]]) -> None:
        """Set background config by dict object."""
        env = self.__file_name.absoluteDir()
        file = QFileInfo(env, config.get('background', ""))
        path = file.absoluteFilePath()
        self.background_option.setText(path if file.isFile() else "")
        self.background_x_option.setValue(config.get('background_x', 0.))
        self.background_y_option.setValue(config.get('background_y', 0.))
        self.background_scale_option.setValue(config.get('background_scale', 1.))
        self.background_opacity_option.setValue(config.get('background_opacity', 1.))

    def get_background_path(self) -> str:
        """Get background path."""
        path = self.background_option.text()
        return path if QFileInfo(path).isFile() else ""
<|endoftext|>


<commit_before>try:
    string_type = basestring
except NameError:
    string_type = str

from pysm.errors import InvalidStateTransition

class StateMeta(type):

    def __eq__(self, other):
        return other is WhateverState or self is other

    def __ne__(self, other):
        return not self == other

    def __unicode__(self):
        return self.name
    __str__ = __unicode__

class State(object):

    __metaclass__ = StateMeta

    def enter_state(self, from_state):
        raise NotImplementedError

    def exit_state(self, to_state):
        raise NotImplementedError

    def __eq__(self, other):
        if isinstance(other, string_type):
            return self.__class__.__name__ == other
        elif isinstance(other, State):
            return self.__class__ == other.__class__
        else:
            return False

    def __ne__(self, other):
        return not self == other

class WhateverState(State):

    name = 'WhateverState'

    def enter_state(self, from_state):
        pass

    def exit_state(self, to_state):
        pass

class Event(object):

    def __init__(self, from_states, to_state):
        assert from_states
        assert to_state
        self.to_state = to_state
        self.from_states = from_states

    @property
    def from_states(self):
        return self._from_states

    @from_states.setter
    def from_states(self, from_states):
        if isinstance(from_states, (tuple, list)):
            self._from_states = tuple(from_states)
        else:
            self._from_states = (from_states,)

    def __get__(self, instance, owner):
        self.instance, self.owner = instance, owner
        return self

    def __call__(self):
        #assert current state
        instance = self.instance
        current_state = self.instance.current_state
        if current_state not in self.from_states:
            raise InvalidStateTransition(
                'calling `%s` from state `%s`, valid states `%s`' % (
                    self.name, current_state, self.from_states
                )
            )
        self.__switch__(instance, current_state, self.to_state)

    def __switch__(self, instance, from_state, to_state):
        from_state.exit_state(instance, to_state)
        instance.__class__ = instance.current_state = to_state
        instance._adaptor.update(instance, to_state.name)
        to_state.enter_state(instance, from_state)

class RestoreEvent(Event):

    def __call__(self):
        restore_event = getattr(self.instance, 'restore_from_' + self.name)
        restore_event.to_state = self.instance.current_state
        return super(RestoreEvent, self).__call__()
<commit_msg>python3 compatibility<commit_after>import six
from pysm.errors import InvalidStateTransition

try:
    string_type = basestring
except NameError:
    string_type = str

class StateMeta(type):

    def __eq__(self, other):
        return other is WhateverState or self is other

    def __ne__(self, other):
        return not self == other

    def __unicode__(self):
        return self.name
    __str__ = __unicode__

@six.add_metaclass(StateMeta)
class State(object):

    def enter_state(self, from_state):
        raise NotImplementedError

    def exit_state(self, to_state):
        raise NotImplementedError

    def __eq__(self, other):
        if isinstance(other, string_type):
            return self.__class__.__name__ == other
        elif isinstance(other, State):
            return self.__class__ == other.__class__
        else:
            return False

    def __ne__(self, other):
        return not self == other

class WhateverState(State):

    name = 'WhateverState'

    def enter_state(self, from_state):
        pass

    def exit_state(self, to_state):
        pass

class Event(object):

    def __init__(self, from_states, to_state):
        assert from_states
        assert to_state
        self.to_state = to_state
        self.from_states = from_states

    @property
    def from_states(self):
        return self._from_states

    @from_states.setter
    def from_states(self, from_states):
        if isinstance(from_states, (tuple, list)):
            self._from_states = tuple(from_states)
        else:
            self._from_states = (from_states,)

    def __get__(self, instance, owner):
        self.instance, self.owner = instance, owner
        return self

    def __call__(self):
        #assert current state
        instance = self.instance
        current_state = self.instance.current_state
        if current_state not in self.from_states:
            raise InvalidStateTransition(
                'calling `%s` from state `%s`, valid states `%s`' % (
                    self.name, current_state, self.from_states
                )
            )
        self.__switch__(instance, current_state, self.to_state)

    def __switch__(self, instance, from_state, to_state):
        from_state.exit_state(instance, to_state)
        instance.__class__ = instance.current_state = to_state
        instance._adaptor.update(instance, to_state.name)
        to_state.enter_state(instance, from_state)

class RestoreEvent(Event):

    def __call__(self):
        restore_event = getattr(self.instance, 'restore_from_' + self.name)
        restore_event.to_state = self.instance.current_state
        return super(RestoreEvent, self).__call__()
<|endoftext|>


<commit_before>"""Methods for generating perturbations of two-dimensional motion fields.

The methods in this module implement the following interface for initialization:

  inizialize_xxx(V, optional arguments)

where V (2,m,n) is the motion field.
The output of each initialization method is a dictionary containing the perturbator
that can be supplied to generate_xxx.

The methods in this module implement the following interface for the generation
of a motion perturbation field:

  generate_xxx(perturbator, t)

where perturbator is a dictionary returned by an initialize_xxx method.
The output of each generator method is an array of shape (2,m,n) containing the
x- and y-components of the motion vector perturbations, where m and n are
determined from the perturbator.

"""

import numpy as np
from scipy import linalg

def initialize_bps(V, p_pert_par, p_pert_perp, pixelsperkm, timestep, seed=None):
    """Initialize the motion field perturbator described in Bowler et al.
    2006: STEPS: A probabilistic precipitation forecasting scheme which merges
    an extrapolation nowcast with downscaled NWP. For simplicity, the bias
    adjustment procedure described in the above reference has not been
    implemented. The perturbator generates a constant field whose magnitude
    depends on lead time, see generate_motion_perturbations_bps.

    Parameters
    ----------
    V : array_like
      Array of shape (2,m,n) containing the x- and y-components of the m*n
      motion field to perturb.
    p_pert_par : tuple
      Tuple containing the parameters a,b and c for the standard deviation of
      the perturbations in the direction parallel to the motion vectors. The
      standard deviations are modeled by the function f_par(t) = a*t^b+c, where
      t is lead time.
    p_pert_perp : tuple
      Tuple containing the parameters a,b and c for the standard deviation of
      the perturbations in the direction perpendicular to the motion vectors.
      The standard deviations are modeled by the function f_par(t) = a*t^b+c,
      where t is lead time.
    pixelsperkm : float
      Spatial resolution of the motion field (pixels/kilometer).
    timestep : float
      Time step for the motion vectors (minutes).
    seed : int
      Optional seed number for the random generator.

    Returns
    -------
    out : dict
      A dictionary containing the perturbator that can be supplied to
      generate_motion_perturbations_bps.
    """
    if len(V.shape) != 3:
        raise ValueError("V is not a three-dimensional array")
    if V.shape[0] != 2:
        raise ValueError("the first dimension of V is not 2")
    if len(p_pert_par) != 3:
        raise ValueError("the length of p_pert_par is not 3")
    if len(p_pert_perp) != 3:
        raise ValueError("the length of p_pert_perp is not 3")

    perturbator = {}

    np.random.seed(seed)

    v_pert_x = np.random.laplace()
    v_pert_y = np.random.laplace()
    V_pert = np.stack([v_pert_x*np.ones(V.shape[1:3]),
                       v_pert_y*np.ones(V.shape[1:3])])

    # scale factor for converting the unit of the advection velocities into km/h
    vsf = 60.0 / (timestep * pixelsperkm)
    V = V * vsf

    N = linalg.norm(V, axis=0)
    V_n = V / np.stack([N, N])
    DP = np.sum(V_pert*V_n, axis=0)

    perturbator["vsf"]    = vsf
    perturbator["p_par"]  = p_pert_par
    perturbator["p_perp"] = p_pert_perp
    V_pert_par = V_n * np.stack([DP, DP])
    perturbator["V_pert_par"]  = V_pert_par
    perturbator["V_pert_perp"] = V_pert - V_pert_par

    return perturbator

def generate_bps(perturbator, t):
    """Generate a motion perturbation field by using the method described in
    Bowler et al. 2006: STEPS: A probabilistic precipitation forecasting scheme
    which merges an extrapolation nowcast with downscaled NWP.

    Parameters
    ----------
    perturbator : dict
      A dictionary returned by initialize_motion_perturbations_bps.
    t : float
      Lead time for the perturbation field (minutes).

    Returns
    -------
    out : ndarray
      Array of shape (2,m,n) containing the x- and y-components of the motion
      vector perturbations, where m and n are determined from the perturbator.
    """
    vsf         = perturbator["vsf"]
    p_par       = perturbator["p_par"]
    p_perp      = perturbator["p_perp"]
    V_pert_par  = perturbator["V_pert_par"]
    V_pert_perp = perturbator["V_pert_perp"]

    g_par  = p_par[0]  * pow(t, p_par[1])  + p_par[2]
    g_perp = p_perp[0] * pow(t, p_perp[1]) + p_perp[2]

    return (g_par*V_pert_par + g_perp*V_pert_perp) / vsf
<commit_msg>Add option for user-specified random generator<commit_after>"""Methods for generating perturbations of two-dimensional motion fields.

The methods in this module implement the following interface for initialization:

  inizialize_xxx(V, optional arguments)

where V (2,m,n) is the motion field.
The output of each initialization method is a dictionary containing the perturbator
that can be supplied to generate_xxx.

The methods in this module implement the following interface for the generation
of a motion perturbation field:

  generate_xxx(perturbator, t, randstate=np.random, seed=None)

where perturbator is a dictionary returned by an initialize_xxx method. Optional
random generator can be specified with the randstate and seed arguments,
respectively.
The output of each generator method is an array of shape (2,m,n) containing the
x- and y-components of the motion vector perturbations, where m and n are
determined from the perturbator."""

import numpy as np
from scipy import linalg

def initialize_bps(V, p_pert_par, p_pert_perp, pixelsperkm, timestep,
                   randstate=np.random, seed=None):
    """Initialize the motion field perturbator described in Bowler et al.
    2006: STEPS: A probabilistic precipitation forecasting scheme which merges
    an extrapolation nowcast with downscaled NWP. For simplicity, the bias
    adjustment procedure described in the above reference has not been
    implemented. The perturbator generates a constant field whose magnitude
    depends on lead time, see generate_motion_perturbations_bps.

    Parameters
    ----------
    V : array_like
      Array of shape (2,m,n) containing the x- and y-components of the m*n
      motion field to perturb.
    p_pert_par : tuple
      Tuple containing the parameters a,b and c for the standard deviation of
      the perturbations in the direction parallel to the motion vectors. The
      standard deviations are modeled by the function f_par(t) = a*t^b+c, where
      t is lead time.
    p_pert_perp : tuple
      Tuple containing the parameters a,b and c for the standard deviation of
      the perturbations in the direction perpendicular to the motion vectors.
      The standard deviations are modeled by the function f_par(t) = a*t^b+c,
      where t is lead time.
    pixelsperkm : float
      Spatial resolution of the motion field (pixels/kilometer).
    timestep : float
      Time step for the motion vectors (minutes).
    randstate : mtrand.RandomState
      Optional random generator to use. If set to None, use numpy.random.
    seed : int
      Optional seed number for the random generator.

    Returns
    -------
    out : dict
      A dictionary containing the perturbator that can be supplied to
      generate_motion_perturbations_bps.
    """
    if len(V.shape) != 3:
        raise ValueError("V is not a three-dimensional array")
    if V.shape[0] != 2:
        raise ValueError("the first dimension of V is not 2")
    if len(p_pert_par) != 3:
        raise ValueError("the length of p_pert_par is not 3")
    if len(p_pert_perp) != 3:
        raise ValueError("the length of p_pert_perp is not 3")

    perturbator = {}

    randstate.seed(seed)

    v_pert_x = randstate.laplace()
    v_pert_y = randstate.laplace()
    V_pert = np.stack([v_pert_x*np.ones(V.shape[1:3]),
                       v_pert_y*np.ones(V.shape[1:3])])

    # scale factor for converting the unit of the advection velocities into km/h
    vsf = 60.0 / (timestep * pixelsperkm)
    V = V * vsf

    N = linalg.norm(V, axis=0)
    V_n = V / np.stack([N, N])
    DP = np.sum(V_pert*V_n, axis=0)

    perturbator["randstate"] = randstate
    perturbator["vsf"]    = vsf
    perturbator["p_par"]  = p_pert_par
    perturbator["p_perp"] = p_pert_perp
    V_pert_par = V_n * np.stack([DP, DP])
    perturbator["V_pert_par"]  = V_pert_par
    perturbator["V_pert_perp"] = V_pert - V_pert_par

    return perturbator

def generate_bps(perturbator, t):
    """Generate a motion perturbation field by using the method described in
    Bowler et al. 2006: STEPS: A probabilistic precipitation forecasting scheme
    which merges an extrapolation nowcast with downscaled NWP.

    Parameters
    ----------
    perturbator : dict
      A dictionary returned by initialize_motion_perturbations_bps.
    t : float
      Lead time for the perturbation field (minutes).

    Returns
    -------
    out : ndarray
      Array of shape (2,m,n) containing the x- and y-components of the motion
      vector perturbations, where m and n are determined from the perturbator.
    """
    vsf         = perturbator["vsf"]
    p_par       = perturbator["p_par"]
    p_perp      = perturbator["p_perp"]
    V_pert_par  = perturbator["V_pert_par"]
    V_pert_perp = perturbator["V_pert_perp"]

    g_par  = p_par[0]  * pow(t, p_par[1])  + p_par[2]
    g_perp = p_perp[0] * pow(t, p_perp[1]) + p_perp[2]

    return (g_par*V_pert_par + g_perp*V_pert_perp) / vsf
<|endoftext|>


<commit_before>"""Handle arguments from the command line

This module is a simplifying proxy to stdlib's argparse
"""

import argparse
import re
import sys
from functools import partial
from itertools import chain

from pysyte.cli.config import user

def config(arguments):
    return user(arguments.prog)

def extract_strings(names, name):
    result = {}
    try:
        value = names[name]
    except KeyError:
        return []
    if isinstance(value, str):
        return [value]
    try:
        return [v for v in value if isinstance(v, str)]
    except TypeError:
        return []

class ArgumentsParser(object):
    def __init__(self, description, usage, epilog):
        self.parser = argparse.ArgumentParser(
            description=description, usage=usage, epilog=epilog)
        self._parsed = None

        self.parse = self.parse_args

        self.string = self.arg = self.parser.add_argument
        self.boolean = self.opt = self.true = partial(self.arg, action='store_true')
        self.integer = self.int = partial(self.arg, type=int)
        self.strings = partial(self.arg, type=str, nargs='*')

    def __repr__(self):
        return f'<{self.__class__.__name__}>'

    def positional(self, *args, **kwargs):
        """Add optional positional args"""
        return self.string(*args, **kwargs, nargs='*')

    def positionals(self, *args, **kwargs):
        """Add mandatory positional args"""
        return self.string(*args, **kwargs, nargs='+')

    def parse_args(self, arguments=None, post_parser=None):
        post_parse = post_parser if post_parser else getattr(
            self, 'post_parser', False)
        poster = post_parse if post_parse else lambda x: x
        return poster(ArgumentsNamespace(self.parser.parse_args(arguments)))

class ArgumentsNamespace(object):
    def __init__(self, result):
        self._result = result

    def __getattr__(self, name):
        try:
            super(ArgumentsNamespace, self).__getattr__(name)
        except AttributeError:
            return getattr(self._result, name)

    def get_arg(self, name):
        if not self._result:
            return None
        return getattr(self._result, name)

    def get_strings(self, name):
        if not self._result:
            return None
        return extract_strings(self._result.__dict__, name)

def parser(description=None, usage=None, epilog=None):
    """Make a command line argument parser"""

    if usage:
        return ArgumentsParser(description, usage, epilog)
    match = lambda x: re.match('^[uU]sage:', x)
    lines = (description or "").splitlines()
    usages = [l for l in lines if match(l)]
    if usages:
        usage = usages.pop()
        epilog = '\n\n'.join([l for l in lines if l != usage])
    elif len(lines) > 1 and not lines[1]:
        usage, description = lines[0], '\n\n'.join(lines[2:])
    return ArgumentsParser(description, usage, epilog)
<commit_msg>Allow single positional optional argument<commit_after>"""Handle arguments from the command line

This module is a simplifying proxy to stdlib's argparse
"""

import argparse
import re
import sys
from functools import partial
from itertools import chain

from pysyte.cli.config import user

def config(arguments):
    return user(arguments.prog)

def extract_strings(names, name):
    result = {}
    try:
        value = names[name]
    except KeyError:
        return []
    if isinstance(value, str):
        return [value]
    try:
        return [v for v in value if isinstance(v, str)]
    except TypeError:
        return []

class ArgumentsParser(object):
    def __init__(self, description, usage, epilog):
        self.parser = argparse.ArgumentParser(
            description=description, usage=usage, epilog=epilog)
        self._parsed = None

        self.parse = self.parse_args

        self.string = self.arg = self.parser.add_argument
        self.boolean = self.opt = self.true = partial(self.arg, action='store_true')
        self.integer = self.int = partial(self.arg, type=int)
        self.strings = partial(self.arg, type=str, nargs='*')

    def __repr__(self):
        return f'<{self.__class__.__name__}>'

    def optional(self, *args, **kwargs):
        """Add an optional positional arg"""
        return self.string(*args, **kwargs, nargs='?')

    def positional(self, *args, **kwargs):
        """Add optional positional args"""
        return self.string(*args, **kwargs, nargs='*')

    def positionals(self, *args, **kwargs):
        """Add mandatory positional args"""
        return self.string(*args, **kwargs, nargs='+')

    def parse_args(self, arguments=None, post_parser=None):
        post_parse = post_parser if post_parser else getattr(
            self, 'post_parser', False)
        poster = post_parse if post_parse else lambda x: x
        return poster(ArgumentsNamespace(self.parser.parse_args(arguments)))

class ArgumentsNamespace(object):
    def __init__(self, result):
        self._result = result

    def __getattr__(self, name):
        try:
            super(ArgumentsNamespace, self).__getattr__(name)
        except AttributeError:
            return getattr(self._result, name)

    def get_arg(self, name):
        if not self._result:
            return None
        return getattr(self._result, name)

    def get_strings(self, name):
        if not self._result:
            return None
        return extract_strings(self._result.__dict__, name)

def parser(description=None, usage=None, epilog=None):
    """Make a command line argument parser"""

    if usage:
        return ArgumentsParser(description, usage, epilog)
    match = lambda x: re.match('^[uU]sage:', x)
    lines = (description or "").splitlines()
    usages = [l for l in lines if match(l)]
    if usages:
        usage = usages.pop()
        epilog = '\n\n'.join([l for l in lines if l != usage])
    elif len(lines) > 1 and not lines[1]:
        usage, description = lines[0], '\n\n'.join(lines[2:])
    return ArgumentsParser(description, usage, epilog)
<|endoftext|>


<commit_before>from django import forms
from pytask.taskapp.models import Task, WorkReport

class CreateTaskForm(forms.ModelForm):
    class Meta:
        model = Task
        fields = ['title', 'desc', 'tags_field', 'pynts']

    def clean_title(self):

        data = self.cleaned_data['title'].strip()

        if not data:
            raise forms.ValidationError("This field is required")

        try:
            Task.objects.exclude(status="DL").get(title__iexact=data)
            raise forms.ValidationError("Another task with same title exists")
        except Task.DoesNotExist:
            return data

    def clean_desc(self):

        data = self.cleaned_data['desc'].strip()

        if not data:
            raise forms.ValidationError("This field is required")

        return data

class EditTaskForm(forms.ModelForm):
    class Meta:
        model = Task
        fields = ['title', 'desc', 'tags_field', 'pynts']

    def clean_desc(self):
        data = self.cleaned_data['desc'].strip()
        if not data:
            raise forms.ValidationError("Enter some description for the task")

        return data

    def clean_title(self):
        data = self.cleaned_data['title'].strip()
        try:
            prev_task = Task.objects.exclude(status="DL").get(title__iexact=data)
            if prev_task.id != self.instance.id:
                raise forms.ValidationError("Another task with same title exists")
            else:
                return data
        except Task.DoesNotExist:
            return data

def AddReviewerForm(choices,instance=None):
    """ return a form object with appropriate choices """

    class myform(forms.Form):
        reviewer = forms.ChoiceField(choices=choices, required=True)
    form = myform(instance) if instance else myform()
    return form

class ClaimTaskForm(forms.Form):
    message = forms.CharField(label="Proposal")

    def clean_message(self):
        data = self.cleaned_data['message'].strip()
        if not data:
            raise forms.ValidationError('Enter something as a proposal')
        return data

def ChoiceForm(choices, instance=None):
    """ return a form object with appropriate choices """

    class myform(forms.Form):
        choice = forms.ChoiceField(choices=choices, required=True)
    form = myform(instance) if instance else myform()
    return form

def AddTaskForm(task_choices, is_plain=False):
    """ if is_plain is true, it means the task has no subs/deps.
    so we also give a radio button to choose between subs and dependencies.
    else we only give choices.
    """

    class myForm(forms.Form):
        if is_plain:
            type_choices = [('S','Subtasks'),('D','Dependencies')]
            type = forms.ChoiceField(type_choices, widget=forms.RadioSelect)

        task = forms.ChoiceField(choices=task_choices)
    return myForm()

def AssignPyntForm(choices, instance=None):

    class myForm(forms.Form):
        user = forms.ChoiceField(choices=choices, required=True)
        pynts = forms.IntegerField(min_value=0, required=True, help_text="Choose wisely since it cannot be undone.")
    return myForm(instance) if instance else myForm()

def RemoveUserForm(choices, instance=None):

    class myForm(forms.Form):
        user = forms.ChoiceField(choices=choices, required=True)
        reason = forms.CharField(min_length=1, required=True)
    return myForm(instance) if instance else myForm()

class WorkReportForm(forms.ModelForm):

    class Meta:
        model = WorkReport
        fields = ['remarks', 'attachment']

<commit_msg>added a form for commenting on tasks<commit_after>from django import forms
from pytask.taskapp.models import Task, WorkReport, TaskComment

class CreateTaskForm(forms.ModelForm):
    class Meta:
        model = Task
        fields = ['title', 'desc', 'tags_field', 'pynts']

    def clean_title(self):

        data = self.cleaned_data['title'].strip()

        if not data:
            raise forms.ValidationError("This field is required")

        try:
            Task.objects.exclude(status="DL").get(title__iexact=data)
            raise forms.ValidationError("Another task with same title exists")
        except Task.DoesNotExist:
            return data

    def clean_desc(self):

        data = self.cleaned_data['desc'].strip()

        if not data:
            raise forms.ValidationError("This field is required")

        return data

class EditTaskForm(forms.ModelForm):
    class Meta:
        model = Task
        fields = ['title', 'desc', 'tags_field', 'pynts']

    def clean_desc(self):
        data = self.cleaned_data['desc'].strip()
        if not data:
            raise forms.ValidationError("Enter some description for the task")

        return data

    def clean_title(self):
        data = self.cleaned_data['title'].strip()
        try:
            prev_task = Task.objects.exclude(status="DL").get(title__iexact=data)
            if prev_task.id != self.instance.id:
                raise forms.ValidationError("Another task with same title exists")
            else:
                return data
        except Task.DoesNotExist:
            return data

class TaskCommentForm(forms.ModelForm):

    class Meta:
        model = TaskComment
        fields = ['data']

    def clean_data(self):

        data = self.cleaned_data['data'].strip()
        if not data:
            raise forms.ValidationError("Please add some content")

        return data

def AddReviewerForm(choices,instance=None):
    """ return a form object with appropriate choices """

    class myform(forms.Form):
        reviewer = forms.ChoiceField(choices=choices, required=True)
    form = myform(instance) if instance else myform()
    return form

class ClaimTaskForm(forms.Form):
    message = forms.CharField(label="Proposal")

    def clean_message(self):
        data = self.cleaned_data['message'].strip()
        if not data:
            raise forms.ValidationError('Enter something as a proposal')
        return data

def ChoiceForm(choices, instance=None):
    """ return a form object with appropriate choices """

    class myform(forms.Form):
        choice = forms.ChoiceField(choices=choices, required=True)
    form = myform(instance) if instance else myform()
    return form

def AddTaskForm(task_choices, is_plain=False):
    """ if is_plain is true, it means the task has no subs/deps.
    so we also give a radio button to choose between subs and dependencies.
    else we only give choices.
    """

    class myForm(forms.Form):
        if is_plain:
            type_choices = [('S','Subtasks'),('D','Dependencies')]
            type = forms.ChoiceField(type_choices, widget=forms.RadioSelect)

        task = forms.ChoiceField(choices=task_choices)
    return myForm()

def AssignPyntForm(choices, instance=None):

    class myForm(forms.Form):
        user = forms.ChoiceField(choices=choices, required=True)
        pynts = forms.IntegerField(min_value=0, required=True, help_text="Choose wisely since it cannot be undone.")
    return myForm(instance) if instance else myForm()

def RemoveUserForm(choices, instance=None):

    class myForm(forms.Form):
        user = forms.ChoiceField(choices=choices, required=True)
        reason = forms.CharField(min_length=1, required=True)
    return myForm(instance) if instance else myForm()

class WorkReportForm(forms.ModelForm):

    class Meta:
        model = WorkReport
        fields = ['remarks', 'attachment']

<|endoftext|>


<commit_before>import os
import sys
import re
import py
import pytest
import logging
import time
import datetime

PY2 = sys.version_info[0] == 2

if PY2:
    string_type = basestring
else:
    string_type = str

def pytest_addhooks(pluginmanager):
    pluginmanager.add_hookspecs(LoggerHookspec)

def _late_addoptions(parser, logcfg):
    """Add options to control logger"""
    parser.addini(
        name='logger_logsdir',
        help='base directory with log files for file loggers [basetemp]',
        default=None,
    )
    group = parser.getgroup('logger')
    group.addoption('--logger-logsdir', dest='logger_logsdir')
    if logcfg._enabled:
        # TODO: add ini options
        # XXX: implement me (check logger)
        group.addoption('--log', dest='logger_stdout_setting')

@pytest.hookimpl(trylast=True)
def pytest_load_initial_conftests(early_config, parser, args):
    logcfg = LogConfig()
    # TODO: detect error if multiple cmdlines
    early_config.hook.pytest_logger_cmdline(logcfg=logcfg)
    early_config.pluginmanager.register(EarlyLoggerPlugin(logcfg), '_early_logger')
    _late_addoptions(parser, logcfg)

def pytest_configure(config):
    early_logger = config.pluginmanager.getplugin('_early_logger')
    config.pluginmanager.register(LoggerPlugin(config, early_logger._logcfg), '_logger')

class EarlyLoggerPlugin(object):
    def __init__(self, logcfg):
        self._logcfg = logcfg

class LoggerPlugin(object):
    def __init__(self, config, logcfg):
        self._config = config
        self._logdirlinks = config.hook.pytest_logger_logdirlink(config=config)
        self._loggers = _loggers_from_logcfg(config, logcfg)
        self._logsdir = None

    def logsdir(self):
        ldir = self._logsdir
        if ldir:
            return ldir
        logger_logsdir = self._config.getoption('logger_logsdir')
        if not logger_logsdir:
            logger_logsdir = self._config.getini('logger_logsdir')
        if logger_logsdir:
            ldir = _make_logsdir_dir(logger_logsdir)
        else:
            ldir = _make_logsdir_tmpdir(self._config._tmpdirhandler)

        self._logsdir = ldir

        for link in self._logdirlinks:
            _refresh_link(str(ldir), link)

        return ldir

    def pytest_runtest_setup(self, item):
        loggers = _loggers_from_hooks(item)
        item._logger = state = LoggerState(plugin=self,
                                           item=item,
                                           stdoutloggers=loggers.stdout,
                                           fileloggers=loggers.file)
        state.on_setup()

    def pytest_runtest_teardown(self, item, nextitem):
        logger = getattr(item, '_logger', None)
        if logger:
            logger.on_teardown()

    def pytest_runtest_makereport(self, item, call):
        logger = getattr(item, '_logger', None)
        if logger:
            if call.when == 'teardown':
                logger.on_makereport()

class LoggerState(object):
    def __init__(self, plugin, item, stdoutloggers, fileloggers):
        self._put_newlines = bool(item.config.option.capture == 'no' and stdoutloggers)
        self.handlers = _make_handlers(stdoutloggers, fileloggers, item)

    def put_newline(self):
        if self._put_newlines:
            sys.stdout.write('\n')

    def on_setup(self):
        self.put_newline()
        _enable(self.handlers)

    def on_teardown(self):
        self.put_newline()

    def on_makereport(self):
        _disable(self.handlers)

# XXX: implement me
class LogConfig(object):
    def __init__(self):
        self._enabled = False

    def add_loggers(self, loggers, stdout_level=logging.NOTSET, file_level=logging.NOTSET):
        self._enabled = True

    def set_default_stdout(self, value):
        pass

class LoggerHookspec(object):
    def pytest_logger_cmdline(self, logcfg):
        """ called before cmdline options parsing. If implemented, stdoutloggers
        and fileloggers hooks will be ignored. Accepts terse configuration
        of both stdout and file logging, adds cmdline options to manipulate
        stdout logging.

        :arg logcfg: allows setting loggers and their default settings.
        """

    def pytest_logger_stdoutloggers(self, item):
        """ called before testcase setup. If implemented, given loggers
        will emit their output to terminal output.

        :arg item: test item for which handlers are to be setup.

        :return list: List should contain logger name strings
                      or tuples with logger name string and logging level.
        """

    def pytest_logger_fileloggers(self, item):
        """ called before testcase setup. If implemented, given loggers
        will emit their output to files within logs temporary directory.

        :arg item: test item for which handlers are to be setup.

        :return list: List should contain logger name strings
                      or tuples with logger name string and logging level.
        """

    def pytest_logger_logdirlink(self, config):
        """ called after cmdline options parsing.
        If implemented, symlink to logs directory will be created.

        :arg config: pytest config object, holds e.g. options

        :return string: Absolute path of requested link to logs directory.
        """

class Formatter(logging.Formatter):
    short_level_names = {
        logging.FATAL: 'ftl',
        logging.ERROR: 'err',
        logging.WARN: 'wrn',
        logging.INFO: 'inf',
        logging.DEBUG: 'dbg',
    }

    def __init__(self, *args, **kwargs):
        super(Formatter, self).__init__(*args, **kwargs)
        self._start = time.time()

    def formatTime(self, record, datefmt=None):
        ct = record.created - self._start
        dt = datetime.datetime.utcfromtimestamp(ct)
        return dt.strftime("%M:%S.%f")[:-3]  # omit useconds, leave mseconds

    def format(self, record):
        record.levelshortname = Formatter.short_level_names.get(record.levelno,
                                                                'l%s' % record.levelno)
        return super(Formatter, self).format(record)

@pytest.fixture
def logdir(request):
    return _make_logdir(request._pyfuncitem)

def _sanitize(filename):
    filename = filename.replace('::()::', '.')
    filename = filename.replace('::', '/')
    filename = re.sub(r'\[(.+)\]', r'-\1', filename)
    return filename

def _refresh_link(source, link_name):
    try:
        os.unlink(link_name)
    except OSError:
        pass
    try:
        os.symlink(source, link_name)
    except (OSError, AttributeError, NotImplementedError):
        pass

def _make_logsdir_tmpdir(tmpdirhandler):
    logsdir = tmpdirhandler.getbasetemp()
    if logsdir.basename.startswith('popen-gw'):
        logsdir = logsdir.join('..')
    logsdir = logsdir.join('logs').ensure(dir=1)
    return logsdir

def _make_logsdir_dir(dstname, cleandir=True):
    logsdir = py.path.local(dstname)
    if cleandir:
        if logsdir.check():
            logsdir.remove()
        logsdir.mkdir()
    return logsdir

def _make_logdir(item):
    plugin = item.config.pluginmanager.getplugin('_logger')
    return plugin.logsdir().join(_sanitize(item.nodeid)).ensure(dir=1)

def _enable(handlers):
    for hdlr in handlers:
        hdlr.logger.addHandler(hdlr)

def _disable(handlers):
    for hdlr in handlers:
        hdlr.logger.removeHandler(hdlr)
        hdlr.close()

def _loggers_from_hooks(item):
    def to_loggers(configs_lists):
        def to_logger_and_level(cfg):
            if isinstance(cfg, string_type):
                name, level = cfg, logging.NOTSET
            else:
                name, level = cfg
            logger = logging.getLogger(name)
            return logger, level
        return [to_logger_and_level(cfg) for configs in configs_lists for cfg in configs]
    # TODO: try to make a class Loggers with named constructors
    class Loggers(object):
        pass
    loggers = Loggers()
    loggers.stdout = to_loggers(item.config.hook.pytest_logger_stdoutloggers(item=item))
    loggers.file = to_loggers(item.config.hook.pytest_logger_fileloggers(item=item))
    return loggers

def _loggers_from_logcfg(config, logcfg):
    # XXX: implement me
    return None

def _make_handlers(stdoutloggers, fileloggers, item):
    FORMAT = '%(asctime)s %(levelshortname)s %(name)s: %(message)s'
    fmt = Formatter(fmt=FORMAT)
    handlers = []
    if stdoutloggers:
        handlers += _make_stdout_handlers(stdoutloggers, fmt)
    if fileloggers:
        logdir = _make_logdir(item)
        handlers += _make_file_handlers(fileloggers, fmt, logdir)
    return handlers

def _make_stdout_handlers(loggers, fmt):
    def make_handler(logger_and_level, fmt):
        logger, level = logger_and_level
        handler = logging.StreamHandler(stream=sys.stdout)
        handler.setFormatter(fmt)
        handler.setLevel(level)
        handler.logger = logger
        return handler

    return [make_handler(lgr, fmt) for lgr in loggers]

def _make_file_handlers(loggers, fmt, logdir):
    def make_handler(logdir, logger_and_level, fmt):
        logger, level = logger_and_level
        name = logger.name == 'root' and 'logs' or logger.name
        logfile = str(logdir.join(name))
        handler = logging.FileHandler(filename=logfile, mode='w', delay=True)
        handler.setFormatter(fmt)
        handler.setLevel(level)
        handler.logger = logger
        return handler

    return [make_handler(logdir, lgr, fmt) for lgr in loggers]
<commit_msg>log option and skeleton parser, still have to figure out level sanitization<commit_after>import os
import sys
import re
import py
import pytest
import logging
import time
import datetime

PY2 = sys.version_info[0] == 2

if PY2:
    string_type = basestring
else:
    string_type = str

def pytest_addhooks(pluginmanager):
    pluginmanager.add_hookspecs(LoggerHookspec)

def _late_addoptions(parser, logcfg):
    """Add options to control logger"""
    parser.addini(
        name='logger_logsdir',
        help='base directory with log files for file loggers [basetemp]',
        default=None,
    )
    group = parser.getgroup('logger')
    group.addoption('--logger-logsdir',
                    help='pick you own logs directory instead of default '
                         'directory under session tmpdir')
    if logcfg._enabled:
        # TODO: add ini options
        group.addoption('--log',
                        default=logcfg._log_option_default,
                        type=_log_option_parser(logcfg._loggers),
                        metavar='LOGGER,LOGGER.LEVEL,...',
                        help='comma delimited list of loggers optionally suffixed with level '
                             'preceded by a dot. Levels can be lower or uppercase, or numeric. '
                             'For example: "logger1,logger2.info,logger3.FATAL,logger4.25"')

@pytest.hookimpl(trylast=True)
def pytest_load_initial_conftests(early_config, parser, args):
    logcfg = LogConfig()
    # TODO: detect error if multiple cmdlines
    early_config.hook.pytest_logger_cmdline(logcfg=logcfg)
    early_config.pluginmanager.register(EarlyLoggerPlugin(logcfg), '_early_logger')
    _late_addoptions(parser, logcfg)

def pytest_configure(config):
    early_logger = config.pluginmanager.getplugin('_early_logger')
    config.pluginmanager.register(LoggerPlugin(config, early_logger._logcfg), '_logger')

class EarlyLoggerPlugin(object):
    def __init__(self, logcfg):
        self._logcfg = logcfg

class LoggerPlugin(object):
    def __init__(self, config, logcfg):
        self._config = config
        self._logdirlinks = config.hook.pytest_logger_logdirlink(config=config)
        self._loggers = _loggers_from_logcfg(config, logcfg)
        self._logsdir = None

    def logsdir(self):
        ldir = self._logsdir
        if ldir:
            return ldir
        logger_logsdir = self._config.getoption('logger_logsdir')
        if not logger_logsdir:
            logger_logsdir = self._config.getini('logger_logsdir')
        if logger_logsdir:
            ldir = _make_logsdir_dir(logger_logsdir)
        else:
            ldir = _make_logsdir_tmpdir(self._config._tmpdirhandler)

        self._logsdir = ldir

        for link in self._logdirlinks:
            _refresh_link(str(ldir), link)

        return ldir

    def pytest_runtest_setup(self, item):
        loggers = _loggers_from_hooks(item)
        item._logger = state = LoggerState(plugin=self,
                                           item=item,
                                           stdoutloggers=loggers.stdout,
                                           fileloggers=loggers.file)
        state.on_setup()

    def pytest_runtest_teardown(self, item, nextitem):
        logger = getattr(item, '_logger', None)
        if logger:
            logger.on_teardown()

    def pytest_runtest_makereport(self, item, call):
        logger = getattr(item, '_logger', None)
        if logger:
            if call.when == 'teardown':
                logger.on_makereport()

class LoggerState(object):
    def __init__(self, plugin, item, stdoutloggers, fileloggers):
        self._put_newlines = bool(item.config.option.capture == 'no' and stdoutloggers)
        self.handlers = _make_handlers(stdoutloggers, fileloggers, item)

    def put_newline(self):
        if self._put_newlines:
            sys.stdout.write('\n')

    def on_setup(self):
        self.put_newline()
        _enable(self.handlers)

    def on_teardown(self):
        self.put_newline()

    def on_makereport(self):
        _disable(self.handlers)

# XXX: implement me
class LogConfig(object):
    def __init__(self):
        self._enabled = False
        self._loggers = []
        self._log_option_default = ''

    def add_loggers(self, loggers, stdout_level=logging.NOTSET, file_level=logging.NOTSET):
        # XXX: sanitize levels and report errors like pytest does
        self._enabled = True
        self._loggers += (loggers, stdout_level, file_level)

    def set_log_option_default(self, value):
        self._log_option_default = value

class LoggerHookspec(object):
    def pytest_logger_cmdline(self, logcfg):
        """ called before cmdline options parsing. If implemented, stdoutloggers
        and fileloggers hooks will be ignored. Accepts terse configuration
        of both stdout and file logging, adds cmdline options to manipulate
        stdout logging.

        :arg logcfg: allows setting loggers and their default settings.
        """

    def pytest_logger_stdoutloggers(self, item):
        """ called before testcase setup. If implemented, given loggers
        will emit their output to terminal output.

        :arg item: test item for which handlers are to be setup.

        :return list: List should contain logger name strings
                      or tuples with logger name string and logging level.
        """

    def pytest_logger_fileloggers(self, item):
        """ called before testcase setup. If implemented, given loggers
        will emit their output to files within logs temporary directory.

        :arg item: test item for which handlers are to be setup.

        :return list: List should contain logger name strings
                      or tuples with logger name string and logging level.
        """

    def pytest_logger_logdirlink(self, config):
        """ called after cmdline options parsing.
        If implemented, symlink to logs directory will be created.

        :arg config: pytest config object, holds e.g. options

        :return string: Absolute path of requested link to logs directory.
        """

class Formatter(logging.Formatter):
    short_level_names = {
        logging.FATAL: 'ftl',
        logging.ERROR: 'err',
        logging.WARN: 'wrn',
        logging.INFO: 'inf',
        logging.DEBUG: 'dbg',
    }

    def __init__(self, *args, **kwargs):
        super(Formatter, self).__init__(*args, **kwargs)
        self._start = time.time()

    def formatTime(self, record, datefmt=None):
        ct = record.created - self._start
        dt = datetime.datetime.utcfromtimestamp(ct)
        return dt.strftime("%M:%S.%f")[:-3]  # omit useconds, leave mseconds

    def format(self, record):
        record.levelshortname = Formatter.short_level_names.get(record.levelno,
                                                                'l%s' % record.levelno)
        return super(Formatter, self).format(record)

@pytest.fixture
def logdir(request):
    return _make_logdir(request._pyfuncitem)

def _sanitize(filename):
    filename = filename.replace('::()::', '.')
    filename = filename.replace('::', '/')
    filename = re.sub(r'\[(.+)\]', r'-\1', filename)
    return filename

def _refresh_link(source, link_name):
    try:
        os.unlink(link_name)
    except OSError:
        pass
    try:
        os.symlink(source, link_name)
    except (OSError, AttributeError, NotImplementedError):
        pass

def _make_logsdir_tmpdir(tmpdirhandler):
    logsdir = tmpdirhandler.getbasetemp()
    if logsdir.basename.startswith('popen-gw'):
        logsdir = logsdir.join('..')
    logsdir = logsdir.join('logs').ensure(dir=1)
    return logsdir

def _make_logsdir_dir(dstname, cleandir=True):
    logsdir = py.path.local(dstname)
    if cleandir:
        if logsdir.check():
            logsdir.remove()
        logsdir.mkdir()
    return logsdir

def _make_logdir(item):
    plugin = item.config.pluginmanager.getplugin('_logger')
    return plugin.logsdir().join(_sanitize(item.nodeid)).ensure(dir=1)

def _enable(handlers):
    for hdlr in handlers:
        hdlr.logger.addHandler(hdlr)

def _disable(handlers):
    for hdlr in handlers:
        hdlr.logger.removeHandler(hdlr)
        hdlr.close()

def _log_option_parser(loggers):
    def parser(arg):
        # XXX: implement me
        return 'result'
    return parser

def _loggers_from_hooks(item):
    def to_loggers(configs_lists):
        def to_logger_and_level(cfg):
            if isinstance(cfg, string_type):
                name, level = cfg, logging.NOTSET
            else:
                name, level = cfg
            logger = logging.getLogger(name)
            return logger, level
        return [to_logger_and_level(cfg) for configs in configs_lists for cfg in configs]
    # TODO: try to make a class Loggers with named constructors
    class Loggers(object):
        pass
    loggers = Loggers()
    loggers.stdout = to_loggers(item.config.hook.pytest_logger_stdoutloggers(item=item))
    loggers.file = to_loggers(item.config.hook.pytest_logger_fileloggers(item=item))
    return loggers

def _loggers_from_logcfg(config, logcfg):
    # XXX: implement me
    return None

def _make_handlers(stdoutloggers, fileloggers, item):
    FORMAT = '%(asctime)s %(levelshortname)s %(name)s: %(message)s'
    fmt = Formatter(fmt=FORMAT)
    handlers = []
    if stdoutloggers:
        handlers += _make_stdout_handlers(stdoutloggers, fmt)
    if fileloggers:
        logdir = _make_logdir(item)
        handlers += _make_file_handlers(fileloggers, fmt, logdir)
    return handlers

def _make_stdout_handlers(loggers, fmt):
    def make_handler(logger_and_level, fmt):
        logger, level = logger_and_level
        handler = logging.StreamHandler(stream=sys.stdout)
        handler.setFormatter(fmt)
        handler.setLevel(level)
        handler.logger = logger
        return handler

    return [make_handler(lgr, fmt) for lgr in loggers]

def _make_file_handlers(loggers, fmt, logdir):
    def make_handler(logdir, logger_and_level, fmt):
        logger, level = logger_and_level
        name = logger.name == 'root' and 'logs' or logger.name
        logfile = str(logdir.join(name))
        handler = logging.FileHandler(filename=logfile, mode='w', delay=True)
        handler.setFormatter(fmt)
        handler.setLevel(level)
        handler.logger = logger
        return handler

    return [make_handler(logdir, lgr, fmt) for lgr in loggers]
<|endoftext|>