<commit_before>from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals

import errno
import io
import itertools
import logging
import os
import platform
import stat
import sys

import six

try:
    from os import scandir
except ImportError:
    from scandir import scandir

from . import errors
from .errors import FileExists
from .base import FS
from .enums import ResourceType
from .info import Info
from .path import abspath, basename, normpath
from .permissions import Permissions
from .error_tools import convert_os_errors
from .mode import Mode, validate_open_mode

log = logging.getLogger('fs.osfs')

_WINDOWS_PLATFORM = platform.system() == 'Windows'

@six.python_2_unicode_compatible
class OSFS(FS):
    """
    Create an OSFS.

    :param root_path: An OS path to the location on your HD you
        wish to manage.
    :type root_path: str
    :param create: Set to ``True`` to create the root directory if it
        does not already exist, otherwise the directory should exist
        prior to creating the ``OSFS`` instance.
    :type create: bool
    :param encoding: The encoding to use for paths, or ``None``
        (default) to autodetect.
    :type encoding: str
    :raises `fs.errors.CreateFailed`: If ``root_path`` does not
        exists, or could not be created.

    Here are some examples of creating ``OSFS`` objects::

        current_directory_fs = OSFS('.')
        home_fs = OSFS('~/')
        windows_system32_fs = OSFS('c://system32')

    """

    def __init__(self,
                 root_path,
                 create=False,
                 create_mode=0o777,
                 encoding=None):
        """Create an OSFS instance."""

        super(OSFS, self).__init__()
        self.encoding = encoding or sys.getfilesystemencoding()

        _root_path = os.path.expanduser(os.path.expandvars(root_path))
        _root_path = os.path.normpath(os.path.abspath(_root_path))
        self.root_path = _root_path

        if create:
            try:
                if not os.path.isdir(_root_path):
                    os.makedirs(_root_path, mode=create_mode)
            except OSError as error:
                raise errors.CreateFailed(
                    'unable to create {} ({})'.format(root_path, error)
                )
        else:
            if not os.path.isdir(_root_path):
                raise errors.CreateFailed(
                    'root path does not exists'
                )

        _meta = self._meta = {
            'case_insensitive': os.path.normcase('Aa') != 'aa',
            'network': False,
            'read_only': False,
            'supports_rename': True,
            'thread_safe': True,
            'unicode_paths': os.path.supports_unicode_filenames,
            'virtual': False,
        }

        if _WINDOWS_PLATFORM:  # pragma: nocover
            _meta["invalid_path_chars"] =\
                ''.join(six.unichr(n) for n in range(31)) + '\\:*?"<>|'
        else:
            _meta["invalid_path_chars"] = '\0'

        if 'PC_PATH_MAX' in os.pathconf_names:
            _meta['max_sys_path_length'] =\
                os.pathconf(_root_path, os.pathconf_names['PC_PATH_MAX'])

    def __repr__(self):
        _fmt = "{}({!r}, encoding={!r})"
        return _fmt.format(self.__class__.__name__,
                           self.root_path,
                           self.encoding)

    def __str__(self):
        fmt = "<{} '{}'>"
        return fmt.format(self.__class__.__name__.lower(),
                          self.root_path)

    def _to_sys_path(self, path):
        """Convert a FS path to a path on the OS."""
        path = normpath(path).lstrip('/').replace('/', os.sep)
        sys_path = os.path.join(self.root_path, path)
        return sys_path

    @classmethod
    def _make_details_from_stat(cls, stat_result):
        """Make an info dict from a stat_result object."""
        details = {
            '_write': ['accessed', 'modified'],
            'accessed': stat_result.st_atime,
            'modified': stat_result.st_mtime,
            'size': stat_result.st_size,
            'type': int(cls._get_type_from_stat(stat_result))
        }
        if hasattr(stat_result, 'st_birthtime'):
            details['created'] = stat_result.st_birthtime
        ctime_key = (
            'created'
            if _WINDOWS_PLATFORM
            else 'metadata_changed'
        )
        details[ctime_key] = stat_result.st_ctime
        return details

    @classmethod
    def _make_access_from_stat(cls, stat_result):
        access = {}
        access['permissions'] = Permissions(
            mode=stat_result.st_mode
        ).dump()
        access['gid'] = stat_result.st_gid
        access['uid'] = stat_result.st_uid
        if not _WINDOWS_PLATFORM:
            import grp
            import pwd
            try:
                access['group'] = grp.getgrgid(access['gid']).gr_name
            except KeyError:  # pragma: nocover
                pass

            try:
                access['user'] = pwd.getpwuid(access['uid']).pw_name
            except KeyError:  # pragma: nocover
                pass
        return access

    STAT_TO_RESOURCE_TYPE = {
        stat.S_IFDIR: ResourceType.directory,
        stat.S_IFCHR: ResourceType.character,
        stat.S_IFBLK: ResourceType.block_special_file,
        stat.S_IFREG: ResourceType.file,
        stat.S_IFIFO: ResourceType.fifo,
        stat.S_IFLNK: ResourceType.symlink,
        stat.S_IFSOCK: ResourceType.socket
    }

    @classmethod
    def _get_type_from_stat(cls, _stat):
        """Get the resource type from a stat_result object."""
        st_mode = _stat.st_mode
        st_type = stat.S_IFMT(st_mode)
        return cls.STAT_TO_RESOURCE_TYPE.get(st_type, ResourceType.unknown)

    # --------------------------------------------------------
    # Required Methods
    # --------------------------------------------------------

    def getinfo(self, path, namespaces=None):
        self.check()
        namespaces = namespaces or ()
        sys_path = self.getsyspath(path)
        with convert_os_errors('getinfo', path):
            _stat = os.stat(sys_path)

        info = {
            'basic': {
                'name': basename(path),
                'is_dir': stat.S_ISDIR(_stat.st_mode)
            }
        }
        if 'details' in namespaces:
            info['details'] = self._make_details_from_stat(_stat)
        if 'stat' in namespaces:
            info['stat'] = {
                k: getattr(stat, k)
                for k in dir(stat) if k.startswith('st_')
            }
        if 'access' in namespaces:
            info['access'] = self._make_access_from_stat(_stat)

        return Info(info)

    def listdir(self, path):
        self.check()
        sys_path = self._to_sys_path(path)
        with convert_os_errors('listdir', path, directory=True):
            names = os.listdir(sys_path)
        return names

    def makedir(self, path, permissions=None, recreate=False):
        self.check()
        mode = Permissions.get_mode(permissions)
        self.validatepath(path)
        sys_path = self._to_sys_path(path)
        with convert_os_errors('makedir', path, directory=True):
            try:
                os.mkdir(sys_path, mode)
            except OSError as error:
                if error.errno == errno.ENOENT:
                    raise errors.ResourceNotFound(path)
                elif error.errno == errno.EEXIST and recreate:
                    pass
                else:
                    raise
            return self.opendir(path)

    def openbin(self, path, mode="r", buffering=-1, **options):
        _mode = Mode(mode)
        _mode.validate_bin()
        self.check()
        self.validatepath(path)
        sys_path = self._to_sys_path(path)
        with convert_os_errors('openbin', path):
            if six.PY2 and _mode.exclusive and self.exists(path):
                raise errors.FileExists(path)
            binary_file = io.open(
                sys_path,
                mode=_mode.to_platform_bin(),
                buffering=buffering,
                **options
            )
        return binary_file

    def remove(self, path):
        self.check()
        sys_path = self._to_sys_path(path)
        with convert_os_errors('remove', path):
            try:
                os.remove(sys_path)
            except OSError as error:
                if error.errno == errno.EACCES and sys.platform == "win32":
                    # sometimes windows says this for attempts to remove a dir
                    if os.path.isdir(sys_path):  # pragma: nocover
                        raise errors.FileExpected(path)
                if error.errno == errno.EPERM and sys.platform == "darwin":
                    # sometimes OSX says this for attempts to remove a dir
                    if os.path.isdir(sys_path):  # pragma: nocover
                        raise errors.FileExpected(path)
                raise

    def removedir(self, path):
        self.check()
        _path = abspath(normpath(path))
        if _path == '/':
            raise errors.RemoveRootError()
        sys_path = self._to_sys_path(path)
        with convert_os_errors('removedir', path, directory=True):
            os.rmdir(sys_path)

    # --------------------------------------------------------
    # Optional Methods
    # --------------------------------------------------------

    def getsyspath(self, path):
        sys_path = self._to_sys_path(path)
        return sys_path

    def gettype(self, path):
        self.check()
        sys_path = self._to_sys_path(path)
        with convert_os_errors('gettype', path):
            stat = os.stat(sys_path)
        resource_type = self._get_type_from_stat(stat)
        return resource_type

    def open(self,
             path,
             mode="r",
             buffering=-1,
             encoding=None,
             errors=None,
             newline=None,
             line_buffering=False,
             **options):
        _mode = Mode(mode)
        validate_open_mode(mode)
        self.check()
        self.validatepath(path)
        sys_path = self._to_sys_path(path)
        with convert_os_errors('open', path):
            if six.PY2 and _mode.exclusive and self.exists(path):
                raise FileExists(path)
            _encoding = encoding or 'utf-8'
            return io.open(
                sys_path,
                mode=_mode.to_platform(),
                buffering=buffering,
                encoding=None if _mode.binary else _encoding,
                errors=errors,
                newline=newline,
                **options
            )

    def setinfo(self, path, info):
        self.check()
        sys_path = self._to_sys_path(path)
        if not os.path.exists(sys_path):
            raise errors.ResourceNotFound(path)
        if 'details' in info:
            details = info['details']
            if 'accessed' in details or 'modified' in details:
                accessed = details.get("accessed")
                modified = details.get("modified", accessed)
                accessed = int(modified if accessed is None else accessed)
                modified = int(modified)
                if accessed is not None or modified is not None:
                    with convert_os_errors('setinfo', path):
                        os.utime(sys_path, (accessed, modified))

    def _scandir(self, path, namespaces=None):
        self.check()
        namespaces = namespaces or ()
        _path = abspath(normpath(path))
        sys_path = self._to_sys_path(_path)
        with convert_os_errors('scandir', path, directory=True):
            for dir_entry in scandir(sys_path):
                info = {
                    "basic": {
                        "name": dir_entry.name,
                        "is_dir": dir_entry.is_dir()
                    }
                }
                if 'details' in namespaces:
                    stat_result = dir_entry.stat()
                    info['details'] = \
                        self._make_details_from_stat(stat_result)
                if 'stat' in namespaces:
                    stat_result = dir_entry.stat()
                    info['stat'] = {
                        k: getattr(stat, k)
                        for k in dir(stat) if k.startswith('st_')
                    }
                if 'access' in namespaces:
                    stat_result = dir_entry.stat()
                    info['access'] =\
                        self._make_access_from_stat(stat_result)

                yield Info(info)

    def scandir(self, path, namespaces=None, page=None):
        iter_info = self._scandir(path, namespaces=namespaces)
        if page is not None:
            start, end = page
            iter_info = itertools.islice(iter_info, start, end)
        return iter_info
<commit_msg>windows fix<commit_after>from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals

import errno
import io
import itertools
import logging
import os
import platform
import stat
import sys

import six

try:
    from os import scandir
except ImportError:
    from scandir import scandir

from . import errors
from .errors import FileExists
from .base import FS
from .enums import ResourceType
from .info import Info
from .path import abspath, basename, normpath
from .permissions import Permissions
from .error_tools import convert_os_errors
from .mode import Mode, validate_open_mode

log = logging.getLogger('fs.osfs')

_WINDOWS_PLATFORM = platform.system() == 'Windows'

@six.python_2_unicode_compatible
class OSFS(FS):
    """
    Create an OSFS.

    :param root_path: An OS path to the location on your HD you
        wish to manage.
    :type root_path: str
    :param create: Set to ``True`` to create the root directory if it
        does not already exist, otherwise the directory should exist
        prior to creating the ``OSFS`` instance.
    :type create: bool
    :param encoding: The encoding to use for paths, or ``None``
        (default) to autodetect.
    :type encoding: str
    :raises `fs.errors.CreateFailed`: If ``root_path`` does not
        exists, or could not be created.

    Here are some examples of creating ``OSFS`` objects::

        current_directory_fs = OSFS('.')
        home_fs = OSFS('~/')
        windows_system32_fs = OSFS('c://system32')

    """

    def __init__(self,
                 root_path,
                 create=False,
                 create_mode=0o777,
                 encoding=None):
        """Create an OSFS instance."""

        super(OSFS, self).__init__()
        self.encoding = encoding or sys.getfilesystemencoding()

        _root_path = os.path.expanduser(os.path.expandvars(root_path))
        _root_path = os.path.normpath(os.path.abspath(_root_path))
        self.root_path = _root_path

        if create:
            try:
                if not os.path.isdir(_root_path):
                    os.makedirs(_root_path, mode=create_mode)
            except OSError as error:
                raise errors.CreateFailed(
                    'unable to create {} ({})'.format(root_path, error)
                )
        else:
            if not os.path.isdir(_root_path):
                raise errors.CreateFailed(
                    'root path does not exists'
                )

        _meta = self._meta = {
            'case_insensitive': os.path.normcase('Aa') != 'aa',
            'network': False,
            'read_only': False,
            'supports_rename': True,
            'thread_safe': True,
            'unicode_paths': os.path.supports_unicode_filenames,
            'virtual': False,
        }

        if _WINDOWS_PLATFORM:  # pragma: nocover
            _meta["invalid_path_chars"] =\
                ''.join(six.unichr(n) for n in range(31)) + '\\:*?"<>|'
        else:
            _meta["invalid_path_chars"] = '\0'

            if 'PC_PATH_MAX' in os.pathconf_names:
                _meta['max_sys_path_length'] = (
                    os.pathconf(
                        _root_path,
                        os.pathconf_names['PC_PATH_MAX']
                    )
                )

    def __repr__(self):
        _fmt = "{}({!r}, encoding={!r})"
        return _fmt.format(self.__class__.__name__,
                           self.root_path,
                           self.encoding)

    def __str__(self):
        fmt = "<{} '{}'>"
        return fmt.format(self.__class__.__name__.lower(),
                          self.root_path)

    def _to_sys_path(self, path):
        """Convert a FS path to a path on the OS."""
        path = normpath(path).lstrip('/').replace('/', os.sep)
        sys_path = os.path.join(self.root_path, path)
        return sys_path

    @classmethod
    def _make_details_from_stat(cls, stat_result):
        """Make an info dict from a stat_result object."""
        details = {
            '_write': ['accessed', 'modified'],
            'accessed': stat_result.st_atime,
            'modified': stat_result.st_mtime,
            'size': stat_result.st_size,
            'type': int(cls._get_type_from_stat(stat_result))
        }
        if hasattr(stat_result, 'st_birthtime'):
            details['created'] = stat_result.st_birthtime
        ctime_key = (
            'created'
            if _WINDOWS_PLATFORM
            else 'metadata_changed'
        )
        details[ctime_key] = stat_result.st_ctime
        return details

    @classmethod
    def _make_access_from_stat(cls, stat_result):
        access = {}
        access['permissions'] = Permissions(
            mode=stat_result.st_mode
        ).dump()
        access['gid'] = stat_result.st_gid
        access['uid'] = stat_result.st_uid
        if not _WINDOWS_PLATFORM:
            import grp
            import pwd
            try:
                access['group'] = grp.getgrgid(access['gid']).gr_name
            except KeyError:  # pragma: nocover
                pass

            try:
                access['user'] = pwd.getpwuid(access['uid']).pw_name
            except KeyError:  # pragma: nocover
                pass
        return access

    STAT_TO_RESOURCE_TYPE = {
        stat.S_IFDIR: ResourceType.directory,
        stat.S_IFCHR: ResourceType.character,
        stat.S_IFBLK: ResourceType.block_special_file,
        stat.S_IFREG: ResourceType.file,
        stat.S_IFIFO: ResourceType.fifo,
        stat.S_IFLNK: ResourceType.symlink,
        stat.S_IFSOCK: ResourceType.socket
    }

    @classmethod
    def _get_type_from_stat(cls, _stat):
        """Get the resource type from a stat_result object."""
        st_mode = _stat.st_mode
        st_type = stat.S_IFMT(st_mode)
        return cls.STAT_TO_RESOURCE_TYPE.get(st_type, ResourceType.unknown)

    # --------------------------------------------------------
    # Required Methods
    # --------------------------------------------------------

    def getinfo(self, path, namespaces=None):
        self.check()
        namespaces = namespaces or ()
        sys_path = self.getsyspath(path)
        with convert_os_errors('getinfo', path):
            _stat = os.stat(sys_path)

        info = {
            'basic': {
                'name': basename(path),
                'is_dir': stat.S_ISDIR(_stat.st_mode)
            }
        }
        if 'details' in namespaces:
            info['details'] = self._make_details_from_stat(_stat)
        if 'stat' in namespaces:
            info['stat'] = {
                k: getattr(stat, k)
                for k in dir(stat) if k.startswith('st_')
            }
        if 'access' in namespaces:
            info['access'] = self._make_access_from_stat(_stat)

        return Info(info)

    def listdir(self, path):
        self.check()
        sys_path = self._to_sys_path(path)
        with convert_os_errors('listdir', path, directory=True):
            names = os.listdir(sys_path)
        return names

    def makedir(self, path, permissions=None, recreate=False):
        self.check()
        mode = Permissions.get_mode(permissions)
        self.validatepath(path)
        sys_path = self._to_sys_path(path)
        with convert_os_errors('makedir', path, directory=True):
            try:
                os.mkdir(sys_path, mode)
            except OSError as error:
                if error.errno == errno.ENOENT:
                    raise errors.ResourceNotFound(path)
                elif error.errno == errno.EEXIST and recreate:
                    pass
                else:
                    raise
            return self.opendir(path)

    def openbin(self, path, mode="r", buffering=-1, **options):
        _mode = Mode(mode)
        _mode.validate_bin()
        self.check()
        self.validatepath(path)
        sys_path = self._to_sys_path(path)
        with convert_os_errors('openbin', path):
            if six.PY2 and _mode.exclusive and self.exists(path):
                raise errors.FileExists(path)
            binary_file = io.open(
                sys_path,
                mode=_mode.to_platform_bin(),
                buffering=buffering,
                **options
            )
        return binary_file

    def remove(self, path):
        self.check()
        sys_path = self._to_sys_path(path)
        with convert_os_errors('remove', path):
            try:
                os.remove(sys_path)
            except OSError as error:
                if error.errno == errno.EACCES and sys.platform == "win32":
                    # sometimes windows says this for attempts to remove a dir
                    if os.path.isdir(sys_path):  # pragma: nocover
                        raise errors.FileExpected(path)
                if error.errno == errno.EPERM and sys.platform == "darwin":
                    # sometimes OSX says this for attempts to remove a dir
                    if os.path.isdir(sys_path):  # pragma: nocover
                        raise errors.FileExpected(path)
                raise

    def removedir(self, path):
        self.check()
        _path = abspath(normpath(path))
        if _path == '/':
            raise errors.RemoveRootError()
        sys_path = self._to_sys_path(path)
        with convert_os_errors('removedir', path, directory=True):
            os.rmdir(sys_path)

    # --------------------------------------------------------
    # Optional Methods
    # --------------------------------------------------------

    def getsyspath(self, path):
        sys_path = self._to_sys_path(path)
        return sys_path

    def gettype(self, path):
        self.check()
        sys_path = self._to_sys_path(path)
        with convert_os_errors('gettype', path):
            stat = os.stat(sys_path)
        resource_type = self._get_type_from_stat(stat)
        return resource_type

    def open(self,
             path,
             mode="r",
             buffering=-1,
             encoding=None,
             errors=None,
             newline=None,
             line_buffering=False,
             **options):
        _mode = Mode(mode)
        validate_open_mode(mode)
        self.check()
        self.validatepath(path)
        sys_path = self._to_sys_path(path)
        with convert_os_errors('open', path):
            if six.PY2 and _mode.exclusive and self.exists(path):
                raise FileExists(path)
            _encoding = encoding or 'utf-8'
            return io.open(
                sys_path,
                mode=_mode.to_platform(),
                buffering=buffering,
                encoding=None if _mode.binary else _encoding,
                errors=errors,
                newline=newline,
                **options
            )

    def setinfo(self, path, info):
        self.check()
        sys_path = self._to_sys_path(path)
        if not os.path.exists(sys_path):
            raise errors.ResourceNotFound(path)
        if 'details' in info:
            details = info['details']
            if 'accessed' in details or 'modified' in details:
                accessed = details.get("accessed")
                modified = details.get("modified", accessed)
                accessed = int(modified if accessed is None else accessed)
                modified = int(modified)
                if accessed is not None or modified is not None:
                    with convert_os_errors('setinfo', path):
                        os.utime(sys_path, (accessed, modified))

    def _scandir(self, path, namespaces=None):
        self.check()
        namespaces = namespaces or ()
        _path = abspath(normpath(path))
        sys_path = self._to_sys_path(_path)
        with convert_os_errors('scandir', path, directory=True):
            for dir_entry in scandir(sys_path):
                info = {
                    "basic": {
                        "name": dir_entry.name,
                        "is_dir": dir_entry.is_dir()
                    }
                }
                if 'details' in namespaces:
                    stat_result = dir_entry.stat()
                    info['details'] = \
                        self._make_details_from_stat(stat_result)
                if 'stat' in namespaces:
                    stat_result = dir_entry.stat()
                    info['stat'] = {
                        k: getattr(stat, k)
                        for k in dir(stat) if k.startswith('st_')
                    }
                if 'access' in namespaces:
                    stat_result = dir_entry.stat()
                    info['access'] =\
                        self._make_access_from_stat(stat_result)

                yield Info(info)

    def scandir(self, path, namespaces=None, page=None):
        iter_info = self._scandir(path, namespaces=namespaces)
        if page is not None:
            start, end = page
            iter_info = itertools.islice(iter_info, start, end)
        return iter_info
<|endoftext|>


<commit_before>import unittest
import os
import time
import requests
import shutil

from os import environ
from configparser import ConfigParser
from pprint import pprint
from subprocess import call as Call

from hipmer.hipmerUtils import hipmerUtils

class hipmerTest(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.token = environ.get('KB_AUTH_TOKEN', None)
        config_file = environ.get('KB_DEPLOYMENT_CONFIG', None)
        cls.cfg = {}
        config = ConfigParser()
        config.read(config_file)
        print(config_file)
        for nameval in config.items('hipmer'):
            cls.cfg[nameval[0]] = nameval[1]
        cls.scratch = cls.cfg['scratch']
        cls.test_dir = os.path.dirname(os.path.abspath(__file__))
        cls.data_dir = os.path.join(cls.test_dir, '../test/data/')

    def test_validate(self):
        # run hipmer
        params = {
            'mer_sizes': '21,41,127',
            'workspace_name': 'bogus',
            'output_contigset_name': 'hipmer.contigs',
            'is_meta': {
                'aggressive': 1
            },
			"is_plant": {
                "diploid": "low"
            },
            'usedebug': 1,
            'assembly_size_filter': 500,
            'interleaved': 1,
            'reads': [{
                'ins_avg': 100,
                'ins_dev': 10,
                'is_rev_comped': 0,
                'read_library_name': '24799/3/1'
            }]
        }
        HU = hipmerUtils(self.cfg, self.token)
        result = HU._validate_inputs(params)
        self.assertTrue(result)

        # must be ints
        params['mer_sizes'] = 'asdf,asdfa,asdf'
        with self.assertRaises(ValueError):
            result = HU._validate_inputs(params)

        # can't go over 127 for kmers
        params['mer_sizes'] = '10 31 130'
        with self.assertRaises(ValueError):
            result = HU._validate_inputs(params)

        # can't go over 127 for kmers
        params['mer_sizes'] = '10,31,130'
        with self.assertRaises(ValueError):
            result = HU._validate_inputs(params)

#        # ins_avg and ins_dev must not be 'None' when paired-end
#        params['reads'][0]['ins_dev'] = None
#        result = HU.check_reads(['24799/3/1'], [], params)
#        self.assertFalse(result)

##    def test_generate_command(self):
##        HU = kbase_hipmerUtils(self.cfg, self.token)
##        result = HU.generate_config(params)
##        self.assertTrue(result)

#    def test_fixup(self):
#        tfile = self.scratch + '/t.fq'
#        params = {
#            'reads': [{'ref': '1/2/3'}],
#            'readsfiles': {
#                '1/2/3': {'files': {'fwd': tfile}}
#            }
#        }
#        shutil.copyfile(self.data_dir + 'sra.fq', tfile)
#        HU = hipmerUtils(self.cfg, self.token)
#        HU.fixup_reads(params)
#        self.assertTrue(os.path.exists(tfile+'.orig'))
#
#        os.remove(tfile + '.orig')
#        shutil.copyfile(self.data_dir + 'nosra.fq', tfile)
#        HU.fixup_reads(params)
#        self.assertFalse(os.path.exists(tfile + '.orig'))
#
<commit_msg>Added contig filtering option - updated test parameters<commit_after>import unittest
import os
import time
import requests
import shutil

from os import environ
from configparser import ConfigParser
from pprint import pprint
from subprocess import call as Call

from hipmer.hipmerUtils import hipmerUtils

class hipmerTest(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.token = environ.get('KB_AUTH_TOKEN', None)
        config_file = environ.get('KB_DEPLOYMENT_CONFIG', None)
        cls.cfg = {}
        config = ConfigParser()
        config.read(config_file)
        print(config_file)
        for nameval in config.items('hipmer'):
            cls.cfg[nameval[0]] = nameval[1]
        cls.scratch = cls.cfg['scratch']
        cls.test_dir = os.path.dirname(os.path.abspath(__file__))
        cls.data_dir = os.path.join(cls.test_dir, '../test/data/')

    def test_validate(self):
        # run hipmer
        params = {
            'mer_sizes': '21,41,127',
            'workspace_name': 'bogus',
            'output_contigset_name': 'hipmer.contigs',
            'is_meta': {
                'aggressive': 1
            },
			"is_plant": {
                "diploid": "low"
            },
            'usedebug': 1,
            'assembly_size_filter': 2000,
            'interleaved': 1,
            'reads': [{
                'ins_avg': 100,
                'ins_dev': 10,
                'is_rev_comped': 0,
                'read_library_name': '24799/3/1'
            }]
        }
        HU = hipmerUtils(self.cfg, self.token)
        result = HU._validate_inputs(params)
        self.assertTrue(result)

        # must be ints
        params['mer_sizes'] = 'asdf,asdfa,asdf'
        with self.assertRaises(ValueError):
            result = HU._validate_inputs(params)

        # can't go over 127 for kmers
        params['mer_sizes'] = '10 31 130'
        with self.assertRaises(ValueError):
            result = HU._validate_inputs(params)

        # can't go over 127 for kmers
        params['mer_sizes'] = '10,31,130'
        with self.assertRaises(ValueError):
            result = HU._validate_inputs(params)

#        # ins_avg and ins_dev must not be 'None' when paired-end
#        params['reads'][0]['ins_dev'] = None
#        result = HU.check_reads(['24799/3/1'], [], params)
#        self.assertFalse(result)

##    def test_generate_command(self):
##        HU = kbase_hipmerUtils(self.cfg, self.token)
##        result = HU.generate_config(params)
##        self.assertTrue(result)

#    def test_fixup(self):
#        tfile = self.scratch + '/t.fq'
#        params = {
#            'reads': [{'ref': '1/2/3'}],
#            'readsfiles': {
#                '1/2/3': {'files': {'fwd': tfile}}
#            }
#        }
#        shutil.copyfile(self.data_dir + 'sra.fq', tfile)
#        HU = hipmerUtils(self.cfg, self.token)
#        HU.fixup_reads(params)
#        self.assertTrue(os.path.exists(tfile+'.orig'))
#
#        os.remove(tfile + '.orig')
#        shutil.copyfile(self.data_dir + 'nosra.fq', tfile)
#        HU.fixup_reads(params)
#        self.assertFalse(os.path.exists(tfile + '.orig'))
#
<|endoftext|>


<commit_before>"""
Useful functions for working with PyFilesystem paths.
sdsd
This is broadly similar to the standard ``os.path`` module but works
with paths in the canonical format expected by all FS objects (that is,
separated by forward slashes and with an optional leading slash).

See :ref:`paths` for an explanation of PyFilesystem paths.

"""

from __future__ import print_function
from __future__ import unicode_literals

import re

from .errors import IllegalBackReference

__all__ = [
    "abspath",
    "basename",
    "combine",
    "dirname",
    "forcedir",
    "frombase",
    "isabs",
    "isbase",
    "isdotfile",
    "isparent",
    "issamedir",
    "iswildcard",
    "iteratepath",
    "join",
    "normpath",
    "recursepath",
    "relativefrom",
    "relpath",
    "split",
    "splitext",
]

_requires_normalization = re.compile(
    r'(^|/)\.\.?($|/)|//',
    re.UNICODE
).search

def normpath(path):
    """
    Normalize a path.

    This function simplifies a path by collapsing back-references
    and removing duplicated separators.

    :param str path: Path to normalize.
    :returns: A valid FS path.
    :type: str

    >>> normpath("/foo//bar/frob/../baz")
    '/foo/bar/baz'
    >>> normpath("foo/../../bar")
    Traceback (most recent call last)
        ...
    IllegalBackReference: Too many backrefs in 'foo/../../bar'

    """
    if path in '/':
        return path

    # An early out if there is no need to normalize this path
    if not _requires_normalization(path):
        return path.rstrip('/')

    prefix = '/' if path.startswith('/') else ''
    components = []
    try:
        for component in path.split('/'):
            if component in '..':  # True for '..', '.', and ''
                if component == '..':
                    components.pop()
            else:
                components.append(component)
    except IndexError:
        raise IllegalBackReference(
            "Too many backrefs in '{}'".format(path)
        )
    return prefix + '/'.join(components)

def iteratepath(path):
    """
    Iterate over the individual components of a path.

    >>> iteratepath('/foo/bar/baz')
    ['foo', 'bar', 'baz']

    :param str path: Path to iterate over.
    :returns: A list of path components.
    :rtype: list

    """
    path = relpath(normpath(path))
    if not path:
        return []
    return path.split('/')

def recursepath(path, reverse=False):
    """
    Get intermediate paths from the root to the given path.

    :param str path: A PyFilesystem path
    :param bool reverse: Reverses the order of the paths.
    :returns: A list of paths.
    :rtype: list

    >>> recursepath('a/b/c')
    ['/', '/a', '/a/b', '/a/b/c']

    """
    if path in '/':
        return ['/']

    path = abspath(normpath(path)) + '/'

    paths = ['/']
    find = path.find
    append = paths.append
    pos = 1
    len_path = len(path)

    while pos < len_path:
        pos = find('/', pos)
        append(path[:pos])
        pos += 1

    if reverse:
        return paths[::-1]
    return paths

def isabs(path):
    """
    Check if a path is an absolute path.

    :param str path: A PyFilesytem path.
    :rtype: bool

    """
    # Somewhat trivial, but helps to make code self-documenting
    return path.startswith('/')

def abspath(path):
    """
    Convert the given path to an absolute path.

    Since FS objects have no concept of a *current directory*, this
    simply adds a leading ``/`` character if the path doesn't already
    have one.

    :param str path: A PyFilesytem path.
    :returns: An absolute path.
    :rtype: str

    """
    if not path.startswith('/'):
        return '/' + path
    return path

def relpath(path):
    """
    Convert the given path to a relative path.

    This is the inverse of abspath(), stripping a leading ``'/'`` from
    the path if it is present.

    :param str path: Path to adjust
    :rtype: str

    >>> relpath('/a/b')
    'a/b'

    """
    return path.lstrip('/')

def join(*paths):
    """
    Join any number of paths together.

    :param paths: Paths to join are given in positional arguments.
    :rtype: str

    >>> join('foo', 'bar', 'baz')
    'foo/bar/baz'
    >>> join('foo/bar', '../baz')
    'foo/baz'
    >>> join('foo/bar', '/baz')
    '/baz'

    """
    absolute = False
    relpaths = []
    for p in paths:
        if p:
            if p[0] == '/':
                del relpaths[:]
                absolute = True
            relpaths.append(p)

    path = normpath("/".join(relpaths))
    if absolute:
        path = abspath(path)
    return path

def combine(path1, path2):
    """
    Join two paths together.

    :param str path1: A PyFilesytem path.
    :param str path2: A PyFilesytem path.
    :rtype: str

    This is faster than :func:`~fs.path.join`, but only works when the
    second path is relative, and there are no back references in either
    path.

    >>> combine("foo/bar", "baz")
    'foo/bar/baz'

    """
    if not path1:
        return path2.lstrip()
    return "{}/{}".format(path1.rstrip('/'), path2.lstrip('/'))

def split(path):
    """
    Split a path into (head, tail) pair.

    This function splits a path into a pair (head, tail) where 'tail' is
    the last pathname component and 'head' is all preceding components.

    :param str path: Path to split
    :returns: tuple of ``(head, tail)``
    :rtype: tuple

    >>> split("foo/bar")
    ('foo', 'bar')
    >>> split("foo/bar/baz")
    ('foo/bar', 'baz')
    >>> split("/foo/bar/baz")
    ('/foo/bar', 'baz')

    """
    if '/' not in path:
        return ('', path)
    split = path.rsplit('/', 1)
    return (split[0] or '/', split[1])

def splitext(path):
    """
    Split the extension from the path, and returns the path (up to the
    last '.' and the extension).

    :param path: A path to split
    :returns: tuple of ``(path, extension)``
    :rtype: tuple

    >>> splitext('baz.txt')
    ('baz', '.txt')
    >>> splitext('foo/bar/baz.txt')
    ('foo/bar/baz', '.txt')

    """

    parent_path, pathname = split(path)
    if '.' not in pathname:
        return path, ''
    pathname, ext = pathname.rsplit('.', 1)
    path = join(parent_path, pathname)
    return path, '.' + ext

def isdotfile(path):
    """
    Detect if a path references a dot file, i.e. a resource who's name
    starts with a '.'

    :param path: Path to check.
    :type path: str
    :rtype: bool

    >>> isdotfile('.baz')
    True
    >>> isdotfile('foo/bar/.baz')
    True
    >>> isdotfile('foo/bar.baz')
    False

    """
    return basename(path).startswith('.')

def dirname(path):
    """
    Return the parent directory of a path.

    This is always equivalent to the 'head' component of the value
    returned by ``split(path)``.

    :param str path: A PyFilesytem path.
    :rtype: str

    >>> dirname('foo/bar/baz')
    'foo/bar'
    >>> dirname('/foo/bar')
    '/foo'
    >>> dirname('/foo')
    '/'

    """
    return split(path)[0]

def basename(path):
    """
    Return the basename of the resource referenced by a path.

    This is always equivalent to the 'tail' component of the value
    returned by split(path).

    :param str path: A PyFilesytem path.
    :rtype: str

    >>> basename('foo/bar/baz')
    'baz'
    >>> basename('foo/bar')
    'bar'
    >>> basename('foo/bar/')
    ''

    """
    return split(path)[1]

def issamedir(path1, path2):
    """
    Check if two paths reference a resource in the same directory.

    :param str path1: A PyFilesytem path.
    :param str path2: A PyFilesytem path.
    :rtype: bool

    >>> issamedir("foo/bar/baz.txt", "foo/bar/spam.txt")
    True
    >>> issamedir("foo/bar/baz/txt", "spam/eggs/spam.txt")
    False

    """
    return dirname(normpath(path1)) == dirname(normpath(path2))

def isbase(path1, path2):
    """
    Check if path1 is a base of path2.

    :param str path1: A PyFilesytem path.
    :param str path2: A PyFilesytem path.
    :rtype: bool

    """
    _path1 = forcedir(abspath(path1))
    _path2 = forcedir(abspath(path2))
    return _path1.startswith(_path2)

def isparent(path1, path2):
    """
    Check if ``path1`` is a parent directory of ``path2``.

    :param str path1: A PyFilesytem path.
    :param str path2: A PyFilesytem path.
    :rtype: bool

    >>> isparent("foo/bar", "foo/bar/spam.txt")
    True
    >>> isparent("foo/bar/", "foo/bar")
    True
    >>> isparent("foo/barry", "foo/baz/bar")
    False
    >>> isparent("foo/bar/baz/", "foo/baz/bar")
    False

    """
    bits1 = path1.split("/")
    bits2 = path2.split("/")
    while bits1 and bits1[-1] == "":
        bits1.pop()
    if len(bits1) > len(bits2):
        return False
    for (bit1, bit2) in zip(bits1, bits2):
        if bit1 != bit2:
            return False
    return True

def forcedir(path):
    """
    Ensure the path ends with a trailing forward slash

    :param path: A PyFilesytem path.
    :rtype: bool

    >>> forcedir("foo/bar")
    'foo/bar/'
    >>> forcedir("foo/bar/")
    'foo/bar/'

    """

    if not path.endswith('/'):
        return path + '/'
    return path

def frombase(path1, path2):
    """
    Get the final path of ``path2`` that isn't in ``path1``.

    :param str path1: A PyFilesytem path.
    :param str path2: A PyFilesytem path.
    :rtype: str

    >>> frombase('foo/bar/', 'foo/bar/baz/egg')
    'baz/egg'

    """
    if not isparent(path1, path2):
        raise ValueError("path1 must be a prefix of path2")
    return path2[len(path1):]

def relativefrom(base, path):
    """
    Return a path relative from a given base path, i.e. insert backrefs
    as appropriate to reach the path from the base.

    :param str base: Path to a directory.
    :param atr path: Path you wish to make relative.

    >>> relativefrom("foo/bar", "baz/index.html")
    '../../baz/index.html'

    """
    base = list(iteratepath(base))
    path = list(iteratepath(path))

    common = 0
    for component_a, component_b in zip(base, path):
        if component_a != component_b:
            break
        common += 1

    return '/'.join(['..'] * (len(base) - common) + path[common:])

_WILD_CHARS = frozenset('*?[]!{}')

def iswildcard(path):
    """
    Check if a path ends with a wildcard.

    :param int path: An FS path.
    :rtype: bool

    >>> iswildcard('foo/bar/baz.*')
    True
    >>> iswildcard('foo/bar')
    False

    """
    assert path is not None
    return not _WILD_CHARS.isdisjoint(path)
<commit_msg>Remove nonsense from documentation (#70)<commit_after>"""
Useful functions for working with PyFilesystem paths.
This is broadly similar to the standard ``os.path`` module but works
with paths in the canonical format expected by all FS objects (that is,
separated by forward slashes and with an optional leading slash).

See :ref:`paths` for an explanation of PyFilesystem paths.

"""

from __future__ import print_function
from __future__ import unicode_literals

import re

from .errors import IllegalBackReference

__all__ = [
    "abspath",
    "basename",
    "combine",
    "dirname",
    "forcedir",
    "frombase",
    "isabs",
    "isbase",
    "isdotfile",
    "isparent",
    "issamedir",
    "iswildcard",
    "iteratepath",
    "join",
    "normpath",
    "recursepath",
    "relativefrom",
    "relpath",
    "split",
    "splitext",
]

_requires_normalization = re.compile(
    r'(^|/)\.\.?($|/)|//',
    re.UNICODE
).search

def normpath(path):
    """
    Normalize a path.

    This function simplifies a path by collapsing back-references
    and removing duplicated separators.

    :param str path: Path to normalize.
    :returns: A valid FS path.
    :type: str

    >>> normpath("/foo//bar/frob/../baz")
    '/foo/bar/baz'
    >>> normpath("foo/../../bar")
    Traceback (most recent call last)
        ...
    IllegalBackReference: Too many backrefs in 'foo/../../bar'

    """
    if path in '/':
        return path

    # An early out if there is no need to normalize this path
    if not _requires_normalization(path):
        return path.rstrip('/')

    prefix = '/' if path.startswith('/') else ''
    components = []
    try:
        for component in path.split('/'):
            if component in '..':  # True for '..', '.', and ''
                if component == '..':
                    components.pop()
            else:
                components.append(component)
    except IndexError:
        raise IllegalBackReference(
            "Too many backrefs in '{}'".format(path)
        )
    return prefix + '/'.join(components)

def iteratepath(path):
    """
    Iterate over the individual components of a path.

    >>> iteratepath('/foo/bar/baz')
    ['foo', 'bar', 'baz']

    :param str path: Path to iterate over.
    :returns: A list of path components.
    :rtype: list

    """
    path = relpath(normpath(path))
    if not path:
        return []
    return path.split('/')

def recursepath(path, reverse=False):
    """
    Get intermediate paths from the root to the given path.

    :param str path: A PyFilesystem path
    :param bool reverse: Reverses the order of the paths.
    :returns: A list of paths.
    :rtype: list

    >>> recursepath('a/b/c')
    ['/', '/a', '/a/b', '/a/b/c']

    """
    if path in '/':
        return ['/']

    path = abspath(normpath(path)) + '/'

    paths = ['/']
    find = path.find
    append = paths.append
    pos = 1
    len_path = len(path)

    while pos < len_path:
        pos = find('/', pos)
        append(path[:pos])
        pos += 1

    if reverse:
        return paths[::-1]
    return paths

def isabs(path):
    """
    Check if a path is an absolute path.

    :param str path: A PyFilesytem path.
    :rtype: bool

    """
    # Somewhat trivial, but helps to make code self-documenting
    return path.startswith('/')

def abspath(path):
    """
    Convert the given path to an absolute path.

    Since FS objects have no concept of a *current directory*, this
    simply adds a leading ``/`` character if the path doesn't already
    have one.

    :param str path: A PyFilesytem path.
    :returns: An absolute path.
    :rtype: str

    """
    if not path.startswith('/'):
        return '/' + path
    return path

def relpath(path):
    """
    Convert the given path to a relative path.

    This is the inverse of abspath(), stripping a leading ``'/'`` from
    the path if it is present.

    :param str path: Path to adjust
    :rtype: str

    >>> relpath('/a/b')
    'a/b'

    """
    return path.lstrip('/')

def join(*paths):
    """
    Join any number of paths together.

    :param paths: Paths to join are given in positional arguments.
    :rtype: str

    >>> join('foo', 'bar', 'baz')
    'foo/bar/baz'
    >>> join('foo/bar', '../baz')
    'foo/baz'
    >>> join('foo/bar', '/baz')
    '/baz'

    """
    absolute = False
    relpaths = []
    for p in paths:
        if p:
            if p[0] == '/':
                del relpaths[:]
                absolute = True
            relpaths.append(p)

    path = normpath("/".join(relpaths))
    if absolute:
        path = abspath(path)
    return path

def combine(path1, path2):
    """
    Join two paths together.

    :param str path1: A PyFilesytem path.
    :param str path2: A PyFilesytem path.
    :rtype: str

    This is faster than :func:`~fs.path.join`, but only works when the
    second path is relative, and there are no back references in either
    path.

    >>> combine("foo/bar", "baz")
    'foo/bar/baz'

    """
    if not path1:
        return path2.lstrip()
    return "{}/{}".format(path1.rstrip('/'), path2.lstrip('/'))

def split(path):
    """
    Split a path into (head, tail) pair.

    This function splits a path into a pair (head, tail) where 'tail' is
    the last pathname component and 'head' is all preceding components.

    :param str path: Path to split
    :returns: tuple of ``(head, tail)``
    :rtype: tuple

    >>> split("foo/bar")
    ('foo', 'bar')
    >>> split("foo/bar/baz")
    ('foo/bar', 'baz')
    >>> split("/foo/bar/baz")
    ('/foo/bar', 'baz')

    """
    if '/' not in path:
        return ('', path)
    split = path.rsplit('/', 1)
    return (split[0] or '/', split[1])

def splitext(path):
    """
    Split the extension from the path, and returns the path (up to the
    last '.' and the extension).

    :param path: A path to split
    :returns: tuple of ``(path, extension)``
    :rtype: tuple

    >>> splitext('baz.txt')
    ('baz', '.txt')
    >>> splitext('foo/bar/baz.txt')
    ('foo/bar/baz', '.txt')

    """

    parent_path, pathname = split(path)
    if '.' not in pathname:
        return path, ''
    pathname, ext = pathname.rsplit('.', 1)
    path = join(parent_path, pathname)
    return path, '.' + ext

def isdotfile(path):
    """
    Detect if a path references a dot file, i.e. a resource who's name
    starts with a '.'

    :param path: Path to check.
    :type path: str
    :rtype: bool

    >>> isdotfile('.baz')
    True
    >>> isdotfile('foo/bar/.baz')
    True
    >>> isdotfile('foo/bar.baz')
    False

    """
    return basename(path).startswith('.')

def dirname(path):
    """
    Return the parent directory of a path.

    This is always equivalent to the 'head' component of the value
    returned by ``split(path)``.

    :param str path: A PyFilesytem path.
    :rtype: str

    >>> dirname('foo/bar/baz')
    'foo/bar'
    >>> dirname('/foo/bar')
    '/foo'
    >>> dirname('/foo')
    '/'

    """
    return split(path)[0]

def basename(path):
    """
    Return the basename of the resource referenced by a path.

    This is always equivalent to the 'tail' component of the value
    returned by split(path).

    :param str path: A PyFilesytem path.
    :rtype: str

    >>> basename('foo/bar/baz')
    'baz'
    >>> basename('foo/bar')
    'bar'
    >>> basename('foo/bar/')
    ''

    """
    return split(path)[1]

def issamedir(path1, path2):
    """
    Check if two paths reference a resource in the same directory.

    :param str path1: A PyFilesytem path.
    :param str path2: A PyFilesytem path.
    :rtype: bool

    >>> issamedir("foo/bar/baz.txt", "foo/bar/spam.txt")
    True
    >>> issamedir("foo/bar/baz/txt", "spam/eggs/spam.txt")
    False

    """
    return dirname(normpath(path1)) == dirname(normpath(path2))

def isbase(path1, path2):
    """
    Check if path1 is a base of path2.

    :param str path1: A PyFilesytem path.
    :param str path2: A PyFilesytem path.
    :rtype: bool

    """
    _path1 = forcedir(abspath(path1))
    _path2 = forcedir(abspath(path2))
    return _path1.startswith(_path2)

def isparent(path1, path2):
    """
    Check if ``path1`` is a parent directory of ``path2``.

    :param str path1: A PyFilesytem path.
    :param str path2: A PyFilesytem path.
    :rtype: bool

    >>> isparent("foo/bar", "foo/bar/spam.txt")
    True
    >>> isparent("foo/bar/", "foo/bar")
    True
    >>> isparent("foo/barry", "foo/baz/bar")
    False
    >>> isparent("foo/bar/baz/", "foo/baz/bar")
    False

    """
    bits1 = path1.split("/")
    bits2 = path2.split("/")
    while bits1 and bits1[-1] == "":
        bits1.pop()
    if len(bits1) > len(bits2):
        return False
    for (bit1, bit2) in zip(bits1, bits2):
        if bit1 != bit2:
            return False
    return True

def forcedir(path):
    """
    Ensure the path ends with a trailing forward slash

    :param path: A PyFilesytem path.
    :rtype: bool

    >>> forcedir("foo/bar")
    'foo/bar/'
    >>> forcedir("foo/bar/")
    'foo/bar/'

    """

    if not path.endswith('/'):
        return path + '/'
    return path

def frombase(path1, path2):
    """
    Get the final path of ``path2`` that isn't in ``path1``.

    :param str path1: A PyFilesytem path.
    :param str path2: A PyFilesytem path.
    :rtype: str

    >>> frombase('foo/bar/', 'foo/bar/baz/egg')
    'baz/egg'

    """
    if not isparent(path1, path2):
        raise ValueError("path1 must be a prefix of path2")
    return path2[len(path1):]

def relativefrom(base, path):
    """
    Return a path relative from a given base path, i.e. insert backrefs
    as appropriate to reach the path from the base.

    :param str base: Path to a directory.
    :param atr path: Path you wish to make relative.

    >>> relativefrom("foo/bar", "baz/index.html")
    '../../baz/index.html'

    """
    base = list(iteratepath(base))
    path = list(iteratepath(path))

    common = 0
    for component_a, component_b in zip(base, path):
        if component_a != component_b:
            break
        common += 1

    return '/'.join(['..'] * (len(base) - common) + path[common:])

_WILD_CHARS = frozenset('*?[]!{}')

def iswildcard(path):
    """
    Check if a path ends with a wildcard.

    :param int path: An FS path.
    :rtype: bool

    >>> iswildcard('foo/bar/baz.*')
    True
    >>> iswildcard('foo/bar')
    False

    """
    assert path is not None
    return not _WILD_CHARS.isdisjoint(path)
<|endoftext|>


<commit_before>#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: Apache 2.0 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>

from __future__ import absolute_import, division, print_function, unicode_literals

import os
import re
import unittest

from lxml.etree import _Comment
from html5_parser import parse

from . import MATHML, SVG, XHTML, XLINK, XML, TestCase

self_path = os.path.abspath(__file__)
base = os.path.dirname(self_path)
html5lib_tests_path = os.path.join(base, 'html5lib-tests')

class TestData(object):

    def __init__(self, filename):
        with open(filename, 'rb') as f:
            self.lines = f.read().decode('utf-8').splitlines()

    def __iter__(self):
        data = {}
        key = None
        for line in self.lines:
            heading = self.is_section_heading(line)
            if heading:
                if data and heading == 'data':
                    yield self.normalize(data)
                    data = {}
                key = heading
                data[key] = ''
            elif key is not None:
                data[key] += line + '\n'
        if data:
            yield self.normalize(data)

    def is_section_heading(self, line):
        """If the current heading is a test section heading return the heading,
        otherwise return False"""
        if line.startswith("#"):
            return line[1:].strip()
        else:
            return False

    def normalize(self, data):
        return {k: v.rstrip('\n') for k, v in data.items()}

def serialize_construction_output(root):
    tree = root.getroottree()
    lines = []
    if tree.docinfo.doctype:
        lines.append('| ' + tree.docinfo.doctype)

    NAMESPACE_PREFIXES = {XHTML: '', SVG: 'svg ', MATHML: 'math ', XLINK: 'xlink ', XML: 'xml '}

    def add(level, *a):
        lines.append('|' + ' ' * level + ''.join(a))

    def serialize_tag(name, level):
        ns = 'None '
        if name.startswith('{'):
            ns, name = name[1:].rpartition('}')[::2]
            ns = NAMESPACE_PREFIXES.get(ns, ns)
        add(level, '<', ns, name, '>')

    def serialize_attr(name, val, level):
        ns = ''
        if name.startswith('{'):
            ns, name = name[1:].rpartition('}')[::2]
            ns = NAMESPACE_PREFIXES.get(ns, ns)
        elif name.startswith('xlink_') or name.startswith('xml_'):
            name = name.replace('_', ':', 1)
        level += 2
        add(level, ns, name, '=', '"', val, '"')

    def serialize_text(text, level):
        level += 2
        add(level, '"', text, '"')

    def serialize_comment(node, level):
        add(level, '<!-- ', node.text, ' -->')

    def serialize_node(node, level=1):
        serialize_tag(node.tag, level)
        for attr in sorted(node.keys()):
            serialize_attr(attr, node.get(attr), level)
        if node.text:
            serialize_text(node.text, level)
        for child in node:
            if isinstance(child, _Comment):
                serialize_comment(child, level + 2)
            else:
                serialize_node(child, level + 2)
            if child.tail:
                serialize_text(child.tail, level)

    serialize_node(root)
    return '\n'.join(lines)

class ConstructionTests(TestCase):

    def implementation(self, inner_html, html, expected, errors):
        html = inner_html or html
        noscript = re.search(r'^\| +<noscript>$', expected, flags=re.MULTILINE)
        if noscript is not None:
            raise unittest.SkipTest('<noscript> is always parsed with scripting off by gumbo')

        if inner_html:
            raise unittest.SkipTest('TODO: Implement fragment parsing')
        else:
            root = parse(html, namespace_elements=True)

        output = serialize_construction_output(root)

        # html5lib doesn't yet support the template tag, but it appears in the
        # tests with the expectation that the template contents will be under the
        # word 'contents', so we need to reformat that string a bit.
        # expected = reformatTemplateContents(expected)

        error_msg = '\n'.join(['\n\nInput:', html, '\nExpected:', expected, '\nReceived:', output])
        self.ae(expected, output, error_msg + '\n')
        # TODO: Check error messages, when there's full error support.

    @classmethod
    def add_single(cls, test_name, num, test):

        def test_func(
            self,
            inner_html=test.get('document-fragment'),
            html=test.get('data'),
            expected=test.get('document'),
            errors=test.get('errors', '').split('\n')
        ):
            return self.implementation(inner_html, html, expected, errors)

        test_func.__name__ = str('test_%s_%d' % (test_name, num))
        setattr(cls, test_func.__name__, test_func)

def html5lib_construction_test_files():
    if os.path.exists(html5lib_tests_path):
        base = os.path.join(html5lib_tests_path, 'tree-construction')
        for x in os.listdir(base):
            if x.endswith('.dat'):
                yield os.path.join(base, x)

def find_tests():
    for ct in html5lib_construction_test_files():
        test_name = os.path.basename(ct).rpartition('.')[0]
        for i, test in enumerate(TestData(ct)):
            ConstructionTests.add_single(test_name, i + 1, test)
    suite = unittest.defaultTestLoader.loadTestsFromTestCase(ConstructionTests)
    return suite
<commit_msg>Fix outputting of root level comments in the html5 test adapter<commit_after>#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: Apache 2.0 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>

from __future__ import absolute_import, division, print_function, unicode_literals

import os
import re
import unittest

from lxml.etree import _Comment
from html5_parser import parse

from . import MATHML, SVG, XHTML, XLINK, XML, TestCase

self_path = os.path.abspath(__file__)
base = os.path.dirname(self_path)
html5lib_tests_path = os.path.join(base, 'html5lib-tests')

class TestData(object):

    def __init__(self, filename):
        with open(filename, 'rb') as f:
            self.lines = f.read().decode('utf-8').splitlines()

    def __iter__(self):
        data = {}
        key = None
        for line in self.lines:
            heading = self.is_section_heading(line)
            if heading:
                if data and heading == 'data':
                    yield self.normalize(data)
                    data = {}
                key = heading
                data[key] = ''
            elif key is not None:
                data[key] += line + '\n'
        if data:
            yield self.normalize(data)

    def is_section_heading(self, line):
        """If the current heading is a test section heading return the heading,
        otherwise return False"""
        if line.startswith("#"):
            return line[1:].strip()
        else:
            return False

    def normalize(self, data):
        return {k: v.rstrip('\n') for k, v in data.items()}

def serialize_construction_output(root):
    tree = root.getroottree()
    lines = []
    if tree.docinfo.doctype:
        lines.append('| ' + tree.docinfo.doctype)

    NAMESPACE_PREFIXES = {XHTML: '', SVG: 'svg ', MATHML: 'math ', XLINK: 'xlink ', XML: 'xml '}

    def add(level, *a):
        lines.append('|' + ' ' * level + ''.join(a))

    def serialize_tag(name, level):
        ns = 'None '
        if name.startswith('{'):
            ns, name = name[1:].rpartition('}')[::2]
            ns = NAMESPACE_PREFIXES.get(ns, ns)
        add(level, '<', ns, name, '>')

    def serialize_attr(name, val, level):
        ns = ''
        if name.startswith('{'):
            ns, name = name[1:].rpartition('}')[::2]
            ns = NAMESPACE_PREFIXES.get(ns, ns)
        elif name.startswith('xlink_') or name.startswith('xml_'):
            name = name.replace('_', ':', 1)
        level += 2
        add(level, ns, name, '=', '"', val, '"')

    def serialize_text(text, level):
        level += 2
        add(level, '"', text, '"')

    def serialize_comment(node, level=1):
        add(level, '<!-- ', node.text, ' -->')

    def serialize_node(node, level=1):
        serialize_tag(node.tag, level)
        for attr in sorted(node.keys()):
            serialize_attr(attr, node.get(attr), level)
        if node.text:
            serialize_text(node.text, level)
        for child in node:
            if isinstance(child, _Comment):
                serialize_comment(child, level + 2)
            else:
                serialize_node(child, level + 2)
            if child.tail:
                serialize_text(child.tail, level)

    for c in root.itersiblings(preceding=True):
        serialize_comment(c)
    serialize_node(root)
    for c in root.itersiblings():
        serialize_comment(c)
    return '\n'.join(lines)

class ConstructionTests(TestCase):

    def implementation(self, inner_html, html, expected, errors):
        html = inner_html or html
        noscript = re.search(r'^\| +<noscript>$', expected, flags=re.MULTILINE)
        if noscript is not None:
            raise unittest.SkipTest('<noscript> is always parsed with scripting off by gumbo')

        if inner_html:
            raise unittest.SkipTest('TODO: Implement fragment parsing')
        else:
            root = parse(html, namespace_elements=True)

        output = serialize_construction_output(root)

        # html5lib doesn't yet support the template tag, but it appears in the
        # tests with the expectation that the template contents will be under the
        # word 'contents', so we need to reformat that string a bit.
        # expected = reformatTemplateContents(expected)

        error_msg = '\n'.join(['\n\nInput:', html, '\nExpected:', expected, '\nReceived:', output])
        self.ae(expected, output, error_msg + '\n')
        # TODO: Check error messages, when there's full error support.

    @classmethod
    def add_single(cls, test_name, num, test):

        def test_func(
            self,
            inner_html=test.get('document-fragment'),
            html=test.get('data'),
            expected=test.get('document'),
            errors=test.get('errors', '').split('\n')
        ):
            return self.implementation(inner_html, html, expected, errors)

        test_func.__name__ = str('test_%s_%d' % (test_name, num))
        setattr(cls, test_func.__name__, test_func)

def html5lib_construction_test_files():
    if os.path.exists(html5lib_tests_path):
        base = os.path.join(html5lib_tests_path, 'tree-construction')
        for x in os.listdir(base):
            if x.endswith('.dat'):
                yield os.path.join(base, x)

def find_tests():
    for ct in html5lib_construction_test_files():
        test_name = os.path.basename(ct).rpartition('.')[0]
        for i, test in enumerate(TestData(ct)):
            ConstructionTests.add_single(test_name, i + 1, test)
    suite = unittest.defaultTestLoader.loadTestsFromTestCase(ConstructionTests)
    return suite
<|endoftext|>


<commit_before>#!/usr/bin/python
import os, sys, socket, random, time
from test_common import *

# "I am a string" -> ["I a", "m a s", "trin", "g"]
def rand_split(string, nsub_strings):
    cutoffs = random.sample(range(len(string)), nsub_strings);
    cutoffs.sort();
    cutoffs.insert(0, 0)
    cutoffs.append(len(string))
    strings = []
    for (start, end) in zip(cutoffs[0:len(cutoffs) - 1], cutoffs[1:len(cutoffs)]):
        strings.append(string[start:end])

    return strings

meaningful_words = ['set', 'get', 'delete', 'gets', 'add', 'replace', 'append', 'prepend', 'cas', 'incr', 'decr', 'stat', 'rethinkdbctl', 'noreply']

def word():
    return random.choice(meaningful_words)

delims = ['\t', ' ', '\n', '\r']

def delim():
    return random.choice(delims)

def number():
    return str(random.randint(0, 999999999999))

def garbage():
    chars=['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','1','2','3','4','5','6','7','8','9','0','!','@','#','$','%','^','&','*','(',')','_','+',',','.','/',';','[',']','<','>',':','{','}','?','\r\n','\r','\n','\t',' ']
    len = random.randint(1, 1000)
    res = ''
    for i in range(len):
        res += random.choice(chars)
    return res

def funny():
    return "Yo dawg"

def test_function(opts, port):

    log = open('fuzz_out', 'w')

    start_time = time.time()

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(("localhost", port))

    while (time.time() - start_time < opts["duration"]):
        str = ''
        for i in range(20):
            choice = random.random()
            if choice < .5:
                str += word()
            elif choice < .75:
                str += number()
            elif choice < .85:
                str += garbage()
            elif choice < .99:
                str += delim()
            else:
                str += funny()

        for substr in rand_split(str, random.randint(10, 40)):
            s.send(substr)
            log.write(substr)

        s.settimeout(0)
        try:
            s.recv(100000)
        except:
            pass

    s.close()

if __name__ == "__main__":
    op = make_option_parser()
    op["duration"] = IntFlag("--duration", 1000)
    auto_server_test_main(test_function, op.parse(sys.argv), timeout = 120)
<commit_msg>Hopefully this fixes the fuzz test.<commit_after>#!/usr/bin/python
import os, sys, socket, random, time
from test_common import *

# "I am a string" -> ["I a", "m a s", "trin", "g"]
def rand_split(string, nsub_strings):
    cutoffs = random.sample(range(len(string)), nsub_strings);
    cutoffs.sort();
    cutoffs.insert(0, 0)
    cutoffs.append(len(string))
    strings = []
    for (start, end) in zip(cutoffs[0:len(cutoffs) - 1], cutoffs[1:len(cutoffs)]):
        strings.append(string[start:end])

    return strings

meaningful_words = ['set', 'get', 'delete', 'gets', 'add', 'replace', 'append', 'prepend', 'cas', 'incr', 'decr', 'stat', 'rethinkdbctl', 'noreply']

def word():
    return random.choice(meaningful_words)

delims = ['\t', ' ', '\n', '\r']

def delim():
    return random.choice(delims)

def number():
    return str(random.randint(0, 999999999999))

def garbage():
    chars=['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','1','2','3','4','5','6','7','8','9','0','!','@','#','$','%','^','&','*','(',')','_','+',',','.','/',';','[',']','<','>',':','{','}','?','\r\n','\r','\n','\t',' ']
    len = random.randint(1, 1000)
    res = ''
    for i in range(len):
        res += random.choice(chars)
    return res

def funny():
    return "Yo dawg"

def test_function(opts, port):

    sent_log = open('fuzz_sent', 'w')
    recv_log = open('fuzz_recv', 'w')

    start_time = time.time()

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(("localhost", port))

    time.sleep(2)

    while (time.time() - start_time < opts["duration"]):
        time.sleep(.01)
        str = ''
        for i in range(20):
            choice = random.random()
            if choice < .5:
                str += word()
            elif choice < .75:
                str += number()
            elif choice < .85:
                str += garbage()
            elif choice < .99:
                str += delim()
            else:
                str += funny()

        for substr in rand_split(str, random.randint(10, 40)):
            s.send(substr)
            sent_log.write(substr)

        s.settimeout(0)
        try:
            server_str = s.recv(100000)
            recv_log.write(server_str)
        except:
            pass

    s.send('quit\r\n')

    s.close()

if __name__ == "__main__":
    op = make_option_parser()
    op["duration"] = IntFlag("--duration", 1000)
    auto_server_test_main(test_function, op.parse(sys.argv), timeout = 120)
<|endoftext|>


<commit_before>#!/usr/bin/env python
# DungeonDisplay - the View for our dungeon generator
#
# Handles rendering of DungeonMap and its associated Regions and Connections.

import logging
import pygame
import math

from .map import DungeonMap, Region, Connection, Decoration
from .geometry import rotate, to_string

log = logging.getLogger(__name__)

class DungeonDisplay:
    """Class to handle visual rendering of the map"""

    def __init__(self, dungeon_map, (w, h)=(None, None)):
        assert isinstance(dungeon_map, DungeonMap)
        # Warning! If you call pygame.display.set_mode() with a (w, h) larger
        # than the window can actually be constructed (due to menu bars,
        # window title bars, dock/taskbar, etc.), pygame will create a smaller
        # window and quietly rescale everything on-the-fly for you.
        # We don't want that!
        # So we make our initial window quite a bit smaller than the display
        # size so that we can reasonably expect it's a valid window.
        if w is None:
            w = pygame.display.Info().current_w - 200
        if h is None:
            h = pygame.display.Info().current_h - 200
        self.surface = pygame.display.set_mode((w, h), pygame.RESIZABLE)
        self.dungeon_map = dungeon_map
        log.debug("Initialized {0}".format(self))

    def __str__(self):
        return ("<DungeonDisplay: surface {0}, map {1}>"
                .format(self.surface, self.dungeon_map))

    def resize_display(self, (w, h)):
        """Handle a window resize event by adjusting our display size"""
        self.surface = pygame.display.set_mode((w, h), pygame.RESIZABLE)

    def update_bounds(self, (display_w, display_h)=(None, None)):
        """Update the display position and scale prior to drawing the map"""
        log.debug("Recalculating map display bounds")
        # Get the extent of the dungeon
        (x0, y0, x1, y1) = self.dungeon_map.bounds

        # Get the extent of the surface
        if display_w is None:
            (display_w, display_h) = self.surface.get_size()
        log.debug("Base parameters are {0}, {1}"
                  .format(self.dungeon_map.bounds,
                          self.surface.get_size()))

        # Provide 10' of buffer around the edges of the map
        (x0, y0, x1, y1) = (x0 - 10, y0 - 10, x1 + 10, y1 + 10)

        # Figure out how much to scale to make the map fit the display
        map_w = (x1 - x0)
        map_h = (y1 - y0)
        xscale = display_w / map_w
        yscale = display_h / map_h
        # self.display_scale is the number of pixels per foot.
        # We constrain it such that 5' is always a whole number of pixels
        self.display_scale = math.floor(min(xscale, yscale) * 5) / 5
        if self.display_scale < 1:
            self.display_scale = 1
        log.debug("Display scale ({0}, {1}) -> {2}"
                  .format(xscale, yscale, self.display_scale))

        # Center the dungeon in the display as needed
        # But fudge it a bit to ensure that (x0, y1) is a grid intersection
        delta = (display_h / self.display_scale) - map_h
        y0 = math.floor((y0 - delta/2) / 10) * 10
        y1 = math.ceil((y1 + delta/2) / 10) * 10
        delta = (display_w / self.display_scale) - map_w
        x0 = math.floor((x0 - delta/2) / 10) * 10
        x1 = math.ceil((x1 + delta/2) / 10) * 10

        self.bounds = (x0, y0, x1, y1)

        log.debug("Display bounds are now {0}, scale is {1}"
                  .format(self.bounds, self.display_scale))

    def map_to_screen(self, coords):
        """Convert (x, y) or [(x0, y0), (x1, y1), ...] to the corresponding
        onscreen coordinates"""

        (x0, y0, x1, y1) = self.bounds

        # Note that the screen uses left-handed coordinates while the map
        # uses right-handed coordinates, so we flip the y axis when converting

        # Handle single point
        if hasattr(coords, "x") and hasattr(coords, "y"):
            return self.map_to_screen((coords.x, coords.y))
        if type(coords) is tuple:
            (x, y) = coords
            return ((x - x0) * self.display_scale,
                    (y1 - y) * self.display_scale)
        # Handle list of points
        output_list = []
        for (x, y) in coords:
            output_list.append(((x - x0) * self.display_scale,
                                (y1 - y) * self.display_scale))
        log.debug("map {0} corresponds to screen {1}"
                  .format(to_string(coords), to_string(output_list)))
        return output_list

    def screen_to_map(self, coords):
        """Convert screen coordinates to the corresponding map coordinates"""
        (x0, y0, x1, y1) = self.bounds

        (x, y) = coords

        # Note that the screen uses left-handed coordinates while the map
        # uses right-handed coordinates, so we flip the y axis when converting
        return ((x / self.display_scale) + x0,
                -(y / self.display_scale) + y1)

    def draw_background(self, verbosity=0):
        """Draw the parts of the dungeon that underlay the grid"""
        log.debug("Drawing background")

        log.debug("Drawing 'rock' background")
        pygame.draw.rect(self.surface, (127, 127, 127), self.surface.get_rect())

        log.debug("Drawing Region contents")
        for region in self.dungeon_map.regions:
            coords = self.map_to_screen(region.coords)
            if verbosity > 0:
                # Color-code regions for convenience
                if region.kind == Region.ROOM:
                    color = (255, 255, 240)
                elif region.kind == Region.CHAMBER:
                    color = (255, 240, 255)
                elif region.kind == Region.PASSAGE:
                    color = (240, 255, 255)
                else:
                    raise LookupError("Unknown Region kind '{0}'"
                                      .format(region.kind))
            else:
                color = (255, 255, 255)
            pygame.draw.polygon(self.surface, color, coords)

        if verbosity == 0:
            return
        log.debug("Drawing Connection contents")
        for conn in self.dungeon_map.connections:
            coords = self.map_to_screen(conn.get_poly_coords())
            if (conn.kind == Connection.DOOR or
                conn.kind == Connection.SECRET or
                conn.kind == Connection.ONEWAY):
                if conn.is_incomplete():
                    color = (127, 140, 127)
                else:
                    color = (240, 255, 240)
            elif conn.kind == Connection.ARCH:
                if conn.is_incomplete():
                    color = (127, 127, 140)
                else:
                    color = (240, 240, 255)
            elif conn.kind == Connection.OPEN:
                if conn.is_incomplete():
                    color = (140, 127, 127)
                else:
                    color = (255, 240, 240)
            else:
                continue
            pygame.draw.polygon(self.surface, color, coords)

    def draw_grid(self, verbosity=0):
        """Draw the 10' grid over the dungeon background"""
        log.debug("Drawing grid")
        (x0, y0, x1, y1) = self.bounds
        color = (191, 191, 191)

        (w, h) = self.surface.get_size()

        i = 10 * ((x0 + 9) // 10)
        while i < x1:
            (x, ignore) = self.map_to_screen((i, 0))
            pygame.draw.line(self.surface, color, (x, 0), (x, h), 1)
            i += 10

        j = 10 * ((y0 + 9) // 10)
        while j < y1:
            (ignore, y) = self.map_to_screen((0, j))
            pygame.draw.line(self.surface, color, (0, y), (w, y), 1)
            j += 10

    def draw_foreground(self, verbosity=0):
        """Draw everything that displays over the dungeon grid"""
        log.debug("Drawing foreground")

        if verbosity > 0:
            # Mark the origin point
            pygame.draw.polygon(self.surface, (255, 0, 0),
                                self.map_to_screen([(-3, -3), (-3, 3),
                                                    (3, 3), (3, -3)]))

        for region in self.dungeon_map.regions:
            color = (0, 0, 0) if not region.tentative else (0, 255, 0)
            coords_list = region.get_wall_coords()
            for coords in coords_list:
                coords = self.map_to_screen(coords)
                pygame.draw.lines(self.surface, color, False, coords, 2)

        for conn in self.dungeon_map.connections:
            centroid = self.map_to_screen((conn.line.centroid.x,
                                           conn.line.centroid.y))
            if verbosity > 0:
                # Draw the directional vectors of this connection for debugging
                endpoint1 = self.map_to_screen((conn.line.centroid.x +
                                                7*conn.direction[0],
                                                conn.line.centroid.y +
                                                7*conn.direction[1]))
                pygame.draw.line(self.surface, (255, 0, 0),
                                 centroid, endpoint1, 3)

            color = (0, 0, 0) if not conn.tentative else (0, 255, 0)
            if verbosity > 0:
                door_color = (240, 255, 240)
            else:
                door_color = (255, 255, 255)
            coords = self.map_to_screen(conn.get_poly_coords())
            for line in conn.draw_lines:
                if line.is_ring:
                    pygame.draw.polygon(self.surface, door_color,
                                        self.map_to_screen(line.coords))
            for line in conn.draw_lines:
                pygame.draw.lines(self.surface, color, False,
                                  self.map_to_screen(line.coords), 2)

        for dec in self.dungeon_map.decorations:
            if dec.kind == Decoration.STAIRS:
                pygame.draw.polygon(self.surface, (100, 100, 100),
                                    self.map_to_screen(dec.coords))
            else:
                raise LookupError("Don't know how to draw foreground for {0}"
                                  .format(dec.kind))

    def draw(self, verbosity=0):
        """Render the entire map to the surface"""

        # Calculate overall scale and position of the map
        self.update_bounds()
        # Draw the dungeon background (everything behind the grid)
        self.draw_background(verbosity)
        # Draw the grid
        self.draw_grid(verbosity)
        # Draw the dungeon foreground (everything in front of the grid)
        self.draw_foreground(verbosity)

        pygame.display.flip()
<commit_msg>Simplified and improved map-to-screen calculation. Fixes #55<commit_after>#!/usr/bin/env python
# DungeonDisplay - the View for our dungeon generator
#
# Handles rendering of DungeonMap and its associated Regions and Connections.

import logging
import pygame
import math

from .map import DungeonMap, Region, Connection, Decoration
from .geometry import rotate, to_string

log = logging.getLogger(__name__)

class DungeonDisplay:
    """Class to handle visual rendering of the map"""

    def __init__(self, dungeon_map, (w, h)=(None, None)):
        assert isinstance(dungeon_map, DungeonMap)
        # Warning! If you call pygame.display.set_mode() with a (w, h) larger
        # than the window can actually be constructed (due to menu bars,
        # window title bars, dock/taskbar, etc.), pygame will create a smaller
        # window and quietly rescale everything on-the-fly for you.
        # We don't want that!
        # So we make our initial window quite a bit smaller than the display
        # size so that we can reasonably expect it's a valid window.
        if w is None:
            w = pygame.display.Info().current_w - 200
        if h is None:
            h = pygame.display.Info().current_h - 200
        self.surface = pygame.display.set_mode((w, h), pygame.RESIZABLE)
        self.dungeon_map = dungeon_map
        log.debug("Initialized {0}".format(self))

    def __str__(self):
        return ("<DungeonDisplay: surface {0}, map {1}>"
                .format(self.surface, self.dungeon_map))

    def resize_display(self, (w, h)):
        """Handle a window resize event by adjusting our display size"""
        self.surface = pygame.display.set_mode((w, h), pygame.RESIZABLE)

    def update_bounds(self, (display_w, display_h)=(None, None)):
        """Update the display position and scale prior to drawing the map"""
        log.debug("Recalculating map display bounds")
        # Get the extent of the dungeon
        (x0, y0, x1, y1) = self.dungeon_map.bounds

        # Get the extent of the surface
        if display_w is None:
            (display_w, display_h) = self.surface.get_size()
        log.debug("Base parameters are {0}, {1}"
                  .format(self.dungeon_map.bounds,
                          self.surface.get_size()))

        # Provide 10' of buffer around the edges of the map
        (x0, y0, x1, y1) = (x0 - 10, y0 - 10, x1 + 10, y1 + 10)
        log.debug("Buffered bounds are {0}".format((x0, y0, x1, y1)))

        # Figure out how much to scale to make the map fit the display
        map_w = (x1 - x0)
        map_h = (y1 - y0)
        xscale = display_w / map_w
        yscale = display_h / map_h
        # self.display_scale is the number of pixels per foot.
        # We constrain it such that 5' is always a whole number of pixels
        self.display_scale = math.floor(min(xscale, yscale) * 5) / 5
        if self.display_scale < 1:
            self.display_scale = 1
        log.debug("Display scale ({0}, {1}) -> {2}"
                  .format(xscale, yscale, self.display_scale))

        # Center the dungeon in the display as needed, but fudge it a bit
        # to ensure that the origin is a grid intersection.
        # The display uses left-handed coordinates while the map uses
        # right-handed coordinates, so the origin for the display (its
        # top left corner) is the x *minimum* and y *maximum* of the map.
        delta = (display_h / self.display_scale) - map_h
        y = math.floor((y1 + delta/2) / 10) * 10
        delta = (display_w / self.display_scale) - map_w
        x = math.ceil((x0 - delta/2) / 10) * 10

        self.origin = (x, y)

        log.debug("Map display origin is now {0}, scale is {1}"
                  .format(self.origin, self.display_scale))

    def map_to_screen(self, coords):
        """Convert (x, y) or [(x0, y0), (x1, y1), ...] to the corresponding
        onscreen coordinates"""

        (x0, y0) = self.origin

        # Note that the screen uses left-handed coordinates while the map
        # uses right-handed coordinates, so we flip the y axis when converting

        # Handle single point
        if hasattr(coords, "x") and hasattr(coords, "y"):
            return self.map_to_screen((coords.x, coords.y))
        if type(coords) is tuple:
            (x, y) = coords
            return ((x - x0) * self.display_scale,
                    (y0 - y) * self.display_scale)
        # Handle list of points
        output_list = []
        for (x, y) in coords:
            output_list.append(((x - x0) * self.display_scale,
                                (y0 - y) * self.display_scale))
        log.debug("map {0} corresponds to screen {1}"
                  .format(to_string(coords), to_string(output_list)))
        return output_list

    def screen_to_map(self, coords):
        """Convert screen coordinates to the corresponding map coordinates"""
        (x0, y0) = self.origin

        (x, y) = coords

        # Note that the screen uses left-handed coordinates while the map
        # uses right-handed coordinates, so we flip the y axis when converting
        map_coords = ((x / self.display_scale) + x0,
                      -(y / self.display_scale) + y0)
        log.debug("screen coords {0} --> map coords {1}"
                  .format(coords, map_coords))
        return map_coords

    def draw_background(self, verbosity=0):
        """Draw the parts of the dungeon that underlay the grid"""
        log.debug("Drawing background")

        log.debug("Drawing 'rock' background")
        pygame.draw.rect(self.surface, (127, 127, 127), self.surface.get_rect())

        log.debug("Drawing Region contents")
        for region in self.dungeon_map.regions:
            coords = self.map_to_screen(region.coords)
            if verbosity > 0:
                # Color-code regions for convenience
                if region.kind == Region.ROOM:
                    color = (255, 255, 240)
                elif region.kind == Region.CHAMBER:
                    color = (255, 240, 255)
                elif region.kind == Region.PASSAGE:
                    color = (240, 255, 255)
                else:
                    raise LookupError("Unknown Region kind '{0}'"
                                      .format(region.kind))
            else:
                color = (255, 255, 255)
            pygame.draw.polygon(self.surface, color, coords)

        if verbosity == 0:
            return
        log.debug("Drawing Connection contents")
        for conn in self.dungeon_map.connections:
            coords = self.map_to_screen(conn.get_poly_coords())
            if (conn.kind == Connection.DOOR or
                conn.kind == Connection.SECRET or
                conn.kind == Connection.ONEWAY):
                if conn.is_incomplete():
                    color = (127, 140, 127)
                else:
                    color = (240, 255, 240)
            elif conn.kind == Connection.ARCH:
                if conn.is_incomplete():
                    color = (127, 127, 140)
                else:
                    color = (240, 240, 255)
            elif conn.kind == Connection.OPEN:
                if conn.is_incomplete():
                    color = (140, 127, 127)
                else:
                    color = (255, 240, 240)
            else:
                continue
            pygame.draw.polygon(self.surface, color, coords)

    def draw_grid(self, verbosity=0):
        """Draw the 10' grid over the dungeon background"""
        log.debug("Drawing grid")
        (x0, y0) = self.origin
        color = (191, 191, 191)

        (w, h) = self.surface.get_size()

        i = x0
        while True:
            (x, ignore) = self.map_to_screen((i, 0))
            if x > w:
                break
            pygame.draw.line(self.surface, color, (x, 0), (x, h), 1)
            i += 10

        j = y0
        while True:
            (ignore, y) = self.map_to_screen((0, j))
            if y > h:
                break
            pygame.draw.line(self.surface, color, (0, y), (w, y), 1)
            j -= 10

    def draw_foreground(self, verbosity=0):
        """Draw everything that displays over the dungeon grid"""
        log.debug("Drawing foreground")

        if verbosity > 0:
            # Mark the origin point
            pygame.draw.polygon(self.surface, (255, 0, 0),
                                self.map_to_screen([(-3, -3), (-3, 3),
                                                    (3, 3), (3, -3)]))

        for region in self.dungeon_map.regions:
            color = (0, 0, 0) if not region.tentative else (0, 255, 0)
            coords_list = region.get_wall_coords()
            for coords in coords_list:
                coords = self.map_to_screen(coords)
                pygame.draw.lines(self.surface, color, False, coords, 2)

        for conn in self.dungeon_map.connections:
            centroid = self.map_to_screen((conn.line.centroid.x,
                                           conn.line.centroid.y))
            if verbosity > 0:
                # Draw the directional vectors of this connection for debugging
                endpoint1 = self.map_to_screen((conn.line.centroid.x +
                                                7*conn.direction[0],
                                                conn.line.centroid.y +
                                                7*conn.direction[1]))
                pygame.draw.line(self.surface, (255, 0, 0),
                                 centroid, endpoint1, 3)

            color = (0, 0, 0) if not conn.tentative else (0, 255, 0)
            if verbosity > 0:
                door_color = (240, 255, 240)
            else:
                door_color = (255, 255, 255)
            coords = self.map_to_screen(conn.get_poly_coords())
            for line in conn.draw_lines:
                if line.is_ring:
                    pygame.draw.polygon(self.surface, door_color,
                                        self.map_to_screen(line.coords))
            for line in conn.draw_lines:
                pygame.draw.lines(self.surface, color, False,
                                  self.map_to_screen(line.coords), 2)

        for dec in self.dungeon_map.decorations:
            if dec.kind == Decoration.STAIRS:
                pygame.draw.polygon(self.surface, (100, 100, 100),
                                    self.map_to_screen(dec.coords))
            else:
                raise LookupError("Don't know how to draw foreground for {0}"
                                  .format(dec.kind))

    def draw(self, verbosity=0):
        """Render the entire map to the surface"""

        # Calculate overall scale and position of the map
        self.update_bounds()
        # Draw the dungeon background (everything behind the grid)
        self.draw_background(verbosity)
        # Draw the grid
        self.draw_grid(verbosity)
        # Draw the dungeon foreground (everything in front of the grid)
        self.draw_foreground(verbosity)

        pygame.display.flip()
<|endoftext|>