<commit_before># Copyright (c) Aaron Gallagher <_@habnab.it>
# See COPYING for details.

import struct

from parsley import makeProtocol
from twisted.internet import protocol, defer, interfaces
from twisted.python import failure
from zope.interface import implements

import txsocksx.constants as c, txsocksx.errors as e
from txsocksx import grammar

def socks_host(host):
    return chr(c.ATYP_DOMAINNAME) + chr(len(host)) + host

class SOCKS5ClientTransport(object):
    def __init__(self, wrappedClient):
        self.wrappedClient = wrappedClient
        self.transport = self.wrappedClient.transport

    def __getattr__(self, attr):
        return getattr(self.transport, attr)

class SOCKS5Sender(object):
    def __init__(self, transport):
        self.transport = transport

    def sendAuthMethods(self, methods):
        self.transport.write(
            struct.pack('!BB', c.VER_SOCKS5, len(methods)) + ''.join(methods))

    def sendLogin(self, username, password):
        self.transport.write(
            '\x01'
            + chr(len(username)) + username
            + chr(len(password)) + password)

    def sendRequest(self, command, host, port):
        data = struct.pack('!BBB', c.VER_SOCKS5, command, c.RSV)
        port = struct.pack('!H', port)
        self.transport.write(data + socks_host(host) + port)

class SOCKS5Receiver(object):
    implements(interfaces.ITransport)
    otherProtocol = None

    def __init__(self, sender, parser):
        self.sender = sender
        self.factory = parser.factory
        parser.setNextRule('SOCKS5ClientState_initial')

    def connectionMade(self):
        methods = self.methods = []
        if self.factory.anonymousAuth:
            methods.append(c.AUTH_ANONYMOUS)
        if self.factory.loginAuth:
            methods.append(c.AUTH_LOGIN)
        self.sender.sendAuthMethods(methods)

    def authSelected(self, method):
        if method not in self.methods:
            raise e.MethodsNotAcceptedError('no method proprosed was accepted',
                                            self.methods, method)
        if method == c.AUTH_ANONYMOUS:
            return self._sendRequest()
        elif method == c.AUTH_LOGIN:
            self.sender.sendLogin(*self.factory.loginAuth)
            return 'SOCKS5ClientState_readLoginResponse'

    def loginResponse(self, success):
        if not success:
            raise e.LoginAuthenticationFailed(
                'username/password combination was rejected')
        return self._sendRequest()

    def _sendRequest(self):
        self.sender.sendRequest(
            c.CMD_CONNECT, self.factory.host, self.factory.port)
        return 'SOCKS5ClientState_readResponse'

    def serverResponse(self, status, address, port):
        if status != c.SOCKS5_GRANTED:
            raise e.ConnectionError('connection rejected by SOCKS server',
                                    status,
                                    e.socks5ErrorMap.get(status, status))
        self.factory.proxyConnectionEstablished(self)
        return 'SOCKSState_readData'

    def proxyEstablished(self, other):
        self.otherProtocol = other
        other.makeConnection(SOCKS5ClientTransport(self.sender))

    def dataReceived(self, data):
        self.otherProtocol.dataReceived(data)

    def connectionLost(self, reason):
        if self.otherProtocol:
            self.otherProtocol.connectionLost(reason)
        else:
            self.factory.proxyConnectionFailed(reason)

SOCKS5Client = makeProtocol(
    grammar.grammarSource, SOCKS5Sender, SOCKS5Receiver, grammar.bindings)

class SOCKS5ClientFactory(protocol.ClientFactory):
    protocol = SOCKS5Client

    def __init__(self, host, port, proxiedFactory, anonymousAuth, loginAuth):
        if not (anonymousAuth or loginAuth):
            raise ValueError('neither anonymous nor login auth was specified')
        self.host = host
        self.port = port
        self.proxiedFactory = proxiedFactory
        self.anonymousAuth = anonymousAuth
        self.loginAuth = loginAuth
        self.deferred = defer.Deferred()

    def proxyConnectionFailed(self, reason):
        self.deferred.errback(reason)

    def clientConnectionFailed(self, connector, reason):
        self.proxyConnectionFailed(reason)

    def proxyConnectionEstablished(self, proxyProtocol):
        proto = self.proxiedFactory.buildProtocol(
            proxyProtocol.sender.transport.getPeer())
        # XXX: handle the case of `proto is None`
        proxyProtocol.proxyEstablished(proto)
        self.deferred.callback(proto)

class SOCKS5ClientEndpoint(object):
    implements(interfaces.IStreamClientEndpoint)

    def __init__(self, host, port, proxyEndpoint, anonymousAuth=True,
                 loginAuth=None):
        self.host = host
        self.port = port
        self.proxyEndpoint = proxyEndpoint
        self.anonymousAuth = anonymousAuth
        self.loginAuth = loginAuth

    def connect(self, fac):
        proxyFac = SOCKS5ClientFactory(
            self.host, self.port, fac, self.anonymousAuth, self.loginAuth)
        self.proxyEndpoint.connect(proxyFac)
        # XXX: maybe use the deferred returned here? need to more different
        # ways/times a connection can fail before connectionMade is called.
        return proxyFac.deferred
<commit_msg>Update for parsley API updates.<commit_after># Copyright (c) Aaron Gallagher <_@habnab.it>
# See COPYING for details.

import struct

from parsley import makeProtocol
from twisted.internet import protocol, defer, interfaces
from twisted.python import failure
from zope.interface import implements

import txsocksx.constants as c, txsocksx.errors as e
from txsocksx import grammar

def socks_host(host):
    return chr(c.ATYP_DOMAINNAME) + chr(len(host)) + host

class SOCKS5ClientTransport(object):
    def __init__(self, wrappedClient):
        self.wrappedClient = wrappedClient
        self.transport = self.wrappedClient.transport

    def __getattr__(self, attr):
        return getattr(self.transport, attr)

class SOCKS5Sender(object):
    def __init__(self, transport):
        self.transport = transport

    def sendAuthMethods(self, methods):
        self.transport.write(
            struct.pack('!BB', c.VER_SOCKS5, len(methods)) + ''.join(methods))

    def sendLogin(self, username, password):
        self.transport.write(
            '\x01'
            + chr(len(username)) + username
            + chr(len(password)) + password)

    def sendRequest(self, command, host, port):
        data = struct.pack('!BBB', c.VER_SOCKS5, command, c.RSV)
        port = struct.pack('!H', port)
        self.transport.write(data + socks_host(host) + port)

class SOCKS5Receiver(object):
    implements(interfaces.ITransport)
    otherProtocol = None
    currentRule = 'SOCKS5ClientState_initial'

    def __init__(self, sender):
        self.sender = sender

    def prepareParsing(self, parser):
        self.factory = parser.factory
        methods = self.methods = []
        if self.factory.anonymousAuth:
            methods.append(c.AUTH_ANONYMOUS)
        if self.factory.loginAuth:
            methods.append(c.AUTH_LOGIN)
        self.sender.sendAuthMethods(methods)

    def authSelected(self, method):
        if method not in self.methods:
            raise e.MethodsNotAcceptedError('no method proprosed was accepted',
                                            self.methods, method)
        if method == c.AUTH_ANONYMOUS:
            return self._sendRequest()
        elif method == c.AUTH_LOGIN:
            self.sender.sendLogin(*self.factory.loginAuth)
            self.currentRule = 'SOCKS5ClientState_readLoginResponse'

    def loginResponse(self, success):
        if not success:
            raise e.LoginAuthenticationFailed(
                'username/password combination was rejected')
        return self._sendRequest()

    def _sendRequest(self):
        self.sender.sendRequest(
            c.CMD_CONNECT, self.factory.host, self.factory.port)
        self.currentRule = 'SOCKS5ClientState_readResponse'

    def serverResponse(self, status, address, port):
        if status != c.SOCKS5_GRANTED:
            raise e.ConnectionError('connection rejected by SOCKS server',
                                    status,
                                    e.socks5ErrorMap.get(status, status))
        self.factory.proxyConnectionEstablished(self)
        self.currentRule = 'SOCKSState_readData'

    def proxyEstablished(self, other):
        self.otherProtocol = other
        other.makeConnection(SOCKS5ClientTransport(self.sender))

    def dataReceived(self, data):
        self.otherProtocol.dataReceived(data)

    def finishParsing(self, reason):
        if self.otherProtocol:
            self.otherProtocol.connectionLost(reason)
        else:
            self.factory.proxyConnectionFailed(reason)

SOCKS5Client = makeProtocol(
    grammar.grammarSource, SOCKS5Sender, SOCKS5Receiver, grammar.bindings)

class SOCKS5ClientFactory(protocol.ClientFactory):
    protocol = SOCKS5Client

    def __init__(self, host, port, proxiedFactory, anonymousAuth, loginAuth):
        if not (anonymousAuth or loginAuth):
            raise ValueError('neither anonymous nor login auth was specified')
        self.host = host
        self.port = port
        self.proxiedFactory = proxiedFactory
        self.anonymousAuth = anonymousAuth
        self.loginAuth = loginAuth
        self.deferred = defer.Deferred()

    def proxyConnectionFailed(self, reason):
        self.deferred.errback(reason)

    def clientConnectionFailed(self, connector, reason):
        self.proxyConnectionFailed(reason)

    def proxyConnectionEstablished(self, proxyProtocol):
        proto = self.proxiedFactory.buildProtocol(
            proxyProtocol.sender.transport.getPeer())
        # XXX: handle the case of `proto is None`
        proxyProtocol.proxyEstablished(proto)
        self.deferred.callback(proto)

class SOCKS5ClientEndpoint(object):
    implements(interfaces.IStreamClientEndpoint)

    def __init__(self, host, port, proxyEndpoint, anonymousAuth=True,
                 loginAuth=None):
        self.host = host
        self.port = port
        self.proxyEndpoint = proxyEndpoint
        self.anonymousAuth = anonymousAuth
        self.loginAuth = loginAuth

    def connect(self, fac):
        proxyFac = SOCKS5ClientFactory(
            self.host, self.port, fac, self.anonymousAuth, self.loginAuth)
        self.proxyEndpoint.connect(proxyFac)
        # XXX: maybe use the deferred returned here? need to more different
        # ways/times a connection can fail before connectionMade is called.
        return proxyFac.deferred
<|endoftext|>


<commit_before>"""Platform for the Aladdin Connect cover component."""
from __future__ import annotations

import logging
from typing import Any, Final

from aladdin_connect import AladdinConnectClient
import voluptuous as vol

from homeassistant.components.cover import (
    DEVICE_CLASS_GARAGE,
    PLATFORM_SCHEMA as BASE_PLATFORM_SCHEMA,
    CoverEntity,
)
from homeassistant.const import (
    CONF_PASSWORD,
    CONF_USERNAME,
    STATE_CLOSED,
    STATE_CLOSING,
    STATE_OPENING,
)
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

from .const import NOTIFICATION_ID, NOTIFICATION_TITLE, STATES_MAP, SUPPORTED_FEATURES
from .model import DoorDevice

_LOGGER: Final = logging.getLogger(__name__)

PLATFORM_SCHEMA: Final = BASE_PLATFORM_SCHEMA.extend(
    {vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string}
)

def setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the Aladdin Connect platform."""

    username: str = config[CONF_USERNAME]
    password: str = config[CONF_PASSWORD]
    acc = AladdinConnectClient(username, password)

    try:
        if not acc.login():
            raise ValueError("Username or Password is incorrect")
        add_entities(AladdinDevice(acc, door) for door in acc.get_doors())
    except (TypeError, KeyError, NameError, ValueError) as ex:
        _LOGGER.error("%s", ex)
        hass.components.persistent_notification.create(
            "Error: {ex}
You will need to restart hass after fixing.",
            title=NOTIFICATION_TITLE,
            notification_id=NOTIFICATION_ID,
        )

class AladdinDevice(CoverEntity):
    """Representation of Aladdin Connect cover."""

    _attr_device_class = DEVICE_CLASS_GARAGE
    _attr_supported_features = SUPPORTED_FEATURES

    def __init__(self, acc: AladdinConnectClient, device: DoorDevice) -> None:
        """Initialize the cover."""
        self._acc = acc
        self._device_id = device["device_id"]
        self._number = device["door_number"]
        self._attr_name = device["name"]
        self._attr_unique_id = f"{self._device_id}-{self._number}"

    def close_cover(self, **kwargs: Any) -> None:
        """Issue close command to cover."""
        self._acc.close_door(self._device_id, self._number)

    def open_cover(self, **kwargs: Any) -> None:
        """Issue open command to cover."""
        self._acc.open_door(self._device_id, self._number)

    def update(self) -> None:
        """Update status of cover."""
        status = STATES_MAP.get(
            self._acc.get_door_status(self._device_id, self._number)
        )
        self._attr_is_opening = status == STATE_OPENING
        self._attr_is_closing = status == STATE_CLOSING
        self._attr_is_closed = None if status is None else status == STATE_CLOSED
<commit_msg>Fix update entity prior to adding (#54015)<commit_after>"""Platform for the Aladdin Connect cover component."""
from __future__ import annotations

import logging
from typing import Any, Final

from aladdin_connect import AladdinConnectClient
import voluptuous as vol

from homeassistant.components.cover import (
    DEVICE_CLASS_GARAGE,
    PLATFORM_SCHEMA as BASE_PLATFORM_SCHEMA,
    CoverEntity,
)
from homeassistant.const import (
    CONF_PASSWORD,
    CONF_USERNAME,
    STATE_CLOSED,
    STATE_CLOSING,
    STATE_OPENING,
)
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

from .const import NOTIFICATION_ID, NOTIFICATION_TITLE, STATES_MAP, SUPPORTED_FEATURES
from .model import DoorDevice

_LOGGER: Final = logging.getLogger(__name__)

PLATFORM_SCHEMA: Final = BASE_PLATFORM_SCHEMA.extend(
    {vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string}
)

def setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the Aladdin Connect platform."""

    username: str = config[CONF_USERNAME]
    password: str = config[CONF_PASSWORD]
    acc = AladdinConnectClient(username, password)

    try:
        if not acc.login():
            raise ValueError("Username or Password is incorrect")
        add_entities(
            (AladdinDevice(acc, door) for door in acc.get_doors()),
            update_before_add = True
        )
    except (TypeError, KeyError, NameError, ValueError) as ex:
        _LOGGER.error("%s", ex)
        hass.components.persistent_notification.create(
            "Error: {ex}
You will need to restart hass after fixing.",
            title=NOTIFICATION_TITLE,
            notification_id=NOTIFICATION_ID,
        )

class AladdinDevice(CoverEntity):
    """Representation of Aladdin Connect cover."""

    _attr_device_class = DEVICE_CLASS_GARAGE
    _attr_supported_features = SUPPORTED_FEATURES

    def __init__(self, acc: AladdinConnectClient, device: DoorDevice) -> None:
        """Initialize the cover."""
        self._acc = acc
        self._device_id = device["device_id"]
        self._number = device["door_number"]
        self._attr_name = device["name"]
        self._attr_unique_id = f"{self._device_id}-{self._number}"

    def close_cover(self, **kwargs: Any) -> None:
        """Issue close command to cover."""
        self._acc.close_door(self._device_id, self._number)

    def open_cover(self, **kwargs: Any) -> None:
        """Issue open command to cover."""
        self._acc.open_door(self._device_id, self._number)

    def update(self) -> None:
        """Update status of cover."""
        status = STATES_MAP.get(
            self._acc.get_door_status(self._device_id, self._number)
        )
        self._attr_is_opening = status == STATE_OPENING
        self._attr_is_closing = status == STATE_CLOSING
        self._attr_is_closed = None if status is None else status == STATE_CLOSED
<|endoftext|>


<commit_before># Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw
# Contact: email-sig@python.org

"""email package exception classes."""

class MessageError(Exception):
    """Base class for errors in the email package."""

class MessageParseError(MessageError):
    """Base class for message parsing errors."""

class HeaderParseError(MessageParseError):
    """Error while parsing headers."""

class BoundaryError(MessageParseError):
    """Couldn't find terminating boundary."""

class MultipartConversionError(MessageError, TypeError):
    """Conversion to a multipart is prohibited."""

class CharsetError(MessageError):
    """An illegal charset was given."""

# These are parsing defects which the parser was able to work around.
class MessageDefect(ValueError):
    """Base class for a message defect."""

    def __init__(self, line=None):
        self.line = line

class NoBoundaryInMultipartDefect(MessageDefect):
    """A message claimed to be a multipart but had no boundary parameter."""

class StartBoundaryNotFoundDefect(MessageDefect):
    """The claimed start boundary was never found."""

class CloseBoundaryNotFoundDefect(MessageDefect):
    """A start boundary was found, but not the corresponding close boundary."""

class FirstHeaderLineIsContinuationDefect(MessageDefect):
    """A message had a continuation line as its first header line."""

class MisplacedEnvelopeHeaderDefect(MessageDefect):
    """A 'Unix-from' header was found in the middle of a header block."""

class MissingHeaderBodySeparatorDefect(MessageDefect):
    """Found line with no leading whitespace and no colon before blank line."""
# XXX: backward compatibility, just in case (it was never emitted).
MalformedHeaderDefect = MissingHeaderBodySeparatorDefect

class MultipartInvariantViolationDefect(MessageDefect):
    """A message claimed to be a multipart but no subparts were found."""

class InvalidMultipartContentTransferEncodingDefect(MessageDefect):
    """An invalid content transfer encoding was set on the multipart itself."""

class UndecodableBytesDefect(MessageDefect):
    """Header contained bytes that could not be decoded"""

class InvalidBase64PaddingDefect(MessageDefect):
    """base64 encoded sequence had an incorrect length"""

class InvalidBase64CharactersDefect(MessageDefect):
    """base64 encoded sequence had characters not in base64 alphabet"""

# These errors are specific to header parsing.

class HeaderDefect(MessageDefect):
    """Base class for a header defect."""

class InvalidHeaderDefect(HeaderDefect):
    """Header is not valid, message gives details."""

class HeaderMissingRequiredValue(HeaderDefect):
    """A header that must have a value had none"""

class NonPrintableDefect(HeaderDefect):
    """ASCII characters outside the ascii-printable range found"""

    def __init__(self, non_printables):
        super().__init__(non_printables)
        self.non_printables = non_printables

    def __str__(self):
        return ("the following ASCII non-printables found in header: "
            "{}".format(self.non_printables))

class ObsoleteHeaderDefect(HeaderDefect):
    """Header uses syntax declared obsolete by RFC 5322"""

class NonASCIILocalPartDefect(HeaderDefect):
    """local_part contains non-ASCII characters"""
    # This defect only occurs during unicode parsing, not when
    # parsing messages decoded from binary.
<commit_msg>Now that Defects are Exception subclasses, call super.<commit_after># Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw
# Contact: email-sig@python.org

"""email package exception classes."""

class MessageError(Exception):
    """Base class for errors in the email package."""

class MessageParseError(MessageError):
    """Base class for message parsing errors."""

class HeaderParseError(MessageParseError):
    """Error while parsing headers."""

class BoundaryError(MessageParseError):
    """Couldn't find terminating boundary."""

class MultipartConversionError(MessageError, TypeError):
    """Conversion to a multipart is prohibited."""

class CharsetError(MessageError):
    """An illegal charset was given."""

# These are parsing defects which the parser was able to work around.
class MessageDefect(ValueError):
    """Base class for a message defect."""

    def __init__(self, line=None):
        if line is not None:
            super().__init__(line)
        self.line = line

class NoBoundaryInMultipartDefect(MessageDefect):
    """A message claimed to be a multipart but had no boundary parameter."""

class StartBoundaryNotFoundDefect(MessageDefect):
    """The claimed start boundary was never found."""

class CloseBoundaryNotFoundDefect(MessageDefect):
    """A start boundary was found, but not the corresponding close boundary."""

class FirstHeaderLineIsContinuationDefect(MessageDefect):
    """A message had a continuation line as its first header line."""

class MisplacedEnvelopeHeaderDefect(MessageDefect):
    """A 'Unix-from' header was found in the middle of a header block."""

class MissingHeaderBodySeparatorDefect(MessageDefect):
    """Found line with no leading whitespace and no colon before blank line."""
# XXX: backward compatibility, just in case (it was never emitted).
MalformedHeaderDefect = MissingHeaderBodySeparatorDefect

class MultipartInvariantViolationDefect(MessageDefect):
    """A message claimed to be a multipart but no subparts were found."""

class InvalidMultipartContentTransferEncodingDefect(MessageDefect):
    """An invalid content transfer encoding was set on the multipart itself."""

class UndecodableBytesDefect(MessageDefect):
    """Header contained bytes that could not be decoded"""

class InvalidBase64PaddingDefect(MessageDefect):
    """base64 encoded sequence had an incorrect length"""

class InvalidBase64CharactersDefect(MessageDefect):
    """base64 encoded sequence had characters not in base64 alphabet"""

# These errors are specific to header parsing.

class HeaderDefect(MessageDefect):
    """Base class for a header defect."""

    def __init__(self, *args, **kw):
        super().__init__(*args, **kw)

class InvalidHeaderDefect(HeaderDefect):
    """Header is not valid, message gives details."""

class HeaderMissingRequiredValue(HeaderDefect):
    """A header that must have a value had none"""

class NonPrintableDefect(HeaderDefect):
    """ASCII characters outside the ascii-printable range found"""

    def __init__(self, non_printables):
        super().__init__(non_printables)
        self.non_printables = non_printables

    def __str__(self):
        return ("the following ASCII non-printables found in header: "
            "{}".format(self.non_printables))

class ObsoleteHeaderDefect(HeaderDefect):
    """Header uses syntax declared obsolete by RFC 5322"""

class NonASCIILocalPartDefect(HeaderDefect):
    """local_part contains non-ASCII characters"""
    # This defect only occurs during unicode parsing, not when
    # parsing messages decoded from binary.
<|endoftext|>


<commit_before>import sys, os, _tkinter
ver = str(_tkinter.TCL_VERSION)
for t in "tcl", "tk":
    v = os.path.join(sys.prefix, "tcl", t+ver)
    if os.path.exists(os.path.join(v, "tclIndex")):
        os.environ[t.upper() + "_LIBRARY"] = v
<commit_msg>Suggestion from SF patch #470433 to avoid clobbering TCL_LIBRARY et al. if already set.  Also adds TIX_LIBRARY (just in case). (Note that this is entirely Windows specific.)<commit_after>import sys, os, _tkinter

ver = str(_tkinter.TCL_VERSION)
for t in "tcl", "tk", "tix":
    key = t.upper() + "_LIBRARY"
    try:
        v = os.environ[key]
    except KeyError:
        v = os.path.join(sys.prefix, "tcl", t+ver)
        if os.path.exists(os.path.join(v, "tclIndex")):
            os.environ[key] = v
<|endoftext|>


<commit_before>from lingcod.features.forms import FeatureForm
from lingcod.bookmarks.models import Bookmark
from django import forms

class BookmarkForm(FeatureForm):
    name = forms.CharField(label='Bookmark Name')
    latitude = forms.FloatField(widget=forms.HiddenInput())
    longitude = forms.FloatField(widget=forms.HiddenInput())
    altitude = forms.FloatField(widget=forms.HiddenInput())
    heading = forms.FloatField(widget=forms.HiddenInput())
    tilt = forms.FloatField(widget=forms.HiddenInput())
    roll = forms.FloatField(widget=forms.HiddenInput())
    altitudeMode = forms.FloatField(widget=forms.HiddenInput())
    publicstate = forms.CharField(widget=forms.HiddenInput())
    ip = forms.CharField(widget=forms.HiddenInput(), required=False)
    class Meta(FeatureForm.Meta):
        model = Bookmark
<commit_msg>Specify default IP in form<commit_after>from lingcod.features.forms import FeatureForm
from lingcod.bookmarks.models import Bookmark
from django import forms

class BookmarkForm(FeatureForm):
    name = forms.CharField(label='Bookmark Name')
    latitude = forms.FloatField(widget=forms.HiddenInput())
    longitude = forms.FloatField(widget=forms.HiddenInput())
    altitude = forms.FloatField(widget=forms.HiddenInput())
    heading = forms.FloatField(widget=forms.HiddenInput())
    tilt = forms.FloatField(widget=forms.HiddenInput())
    roll = forms.FloatField(widget=forms.HiddenInput())
    altitudeMode = forms.FloatField(widget=forms.HiddenInput())
    publicstate = forms.CharField(widget=forms.HiddenInput())
    ip = forms.CharField(widget=forms.HiddenInput(), initial="0.0.0.0", required=False)
    class Meta(FeatureForm.Meta):
        model = Bookmark
<|endoftext|>


<commit_before>"""
definition of plugin system

plugins are supposed to be run when image is built and we need to extract some information
"""
import copy
import importlib
import logging
import os
import sys
import traceback
import imp

import dock.plugins
from dock.util import join_img_name_tag

MODULE_EXTENSIONS = ('.py', '.pyc', '.pyo')
logger = logging.getLogger(__name__)

def get_plugin_conf(build_json, plugin_type, plugin_name):
    """
    return dict with configuration of a plugin from provided build json

    :param plugin_type: str, type of plugin (prebuild_plugins, postbuild_plugins, ...)
    :param plugin_name: str, unique name of the plugin
    :return: dict
    """
    logger.debug("getting plugin conf for '%s' with type '%s'",
                 plugin_name, plugin_type)
    plugins_of_a_type = build_json.get(plugin_type, None)
    if plugins_of_a_type is None:
        logger.warning("there are no plugins with type '%s'",
                       plugin_type)
        return
    plugin_conf = [x for x in plugins_of_a_type if x['name'] == plugin_name]
    plugins_num = len(plugin_conf)
    if plugins_num == 1:
        return plugin_conf[0]
    elif plugins_num <= 0:
        logger.warning("there is no configuration for plugin '%s'",
                       plugin_name)
        return
    else:
        logger.error("there is no configuration for plugin '%s'",
                     plugin_name)
        raise RuntimeError("plugin '%s' was specified multiple (%d) times, can't pick one",
                           plugin_name, plugins_num)

class PluginFailedException(Exception):
    """ There was an error during plugin execution """

class Plugin(object):
    """ abstract plugin class """

    # unique plugin identification
    # output of this plugin can be found in results specified with this key,
    # same thing goes for input: use this key for providing input for this plugin
    key = None
    # by default, if plugin fails (raises exc), execution continues
    can_fail = True

    def __init__(self, *args, **kwargs):
        """
        constructor
        """
        self.log = logging.getLogger("dock.plugins." + self.key)
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        return "%s" % self.key

    def __repr__(self):
        return "Plugin(key='%s')" % self.key

    def run(self):
        """
        each plugin has to implement this method -- it is used to run the plugin actually

        response from a build plugin is kept and used in json result response like this:

          results[plugin.key] = plugin.run()

        input plugins should emit build json with this method
        """
        raise NotImplemented()

class BuildPlugin(Plugin):
    """
    abstract plugin class: base for build plugins, it is
    flavored with DockerTasker and BuildWorkflow instances
    """

    def __init__(self, tasker, workflow, *args, **kwargs):
        """
        constructor

        :param tasker: DockerTasker instance
        :param workflow: DockerBuildWorkflow instance
        :param args: arguments from user input
        :param kwargs: keyword arguments from user input
        """
        self.tasker = tasker
        self.workflow = workflow
        super(BuildPlugin, self).__init__(*args, **kwargs)

class PluginsRunner(object):

    def __init__(self, plugin_class_name, plugins_conf, *args, **kwargs):
        """
        constructor

        :param plugin_class_name: str, name of plugin class to filter (e.g. 'PreBuildPlugin')
        :param plugins_conf: dict, configuration for plugins
        """
        self.plugins_results = getattr(self, "plugins_results", {})
        self.plugins_conf = plugins_conf or []
        self.plugin_files = kwargs.get("plugin_files", [])
        self.plugin_classes = self.load_plugins(plugin_class_name)

    def load_plugins(self, plugin_class_name):
        """
        load all available plugins
        """
        # imp.findmodule('dock') doesn't work
        plugins_dir = os.path.join(os.path.dirname(__file__), 'plugins')
        logger.debug("loading plugins from dir '%s'", plugins_dir)
        files = [os.path.join(plugins_dir, f) \
                 for f in os.listdir(plugins_dir) \
                 if f.endswith(".py")]
        if self.plugin_files:
            logger.debug("loading additional plugins from files '%s'", self.plugin_files)
            files += self.plugin_files
        plugin_class = globals()[plugin_class_name]
        plugin_classes = {}
        for f in files:
            logger.debug("load file '%s'", f)
            module_name = os.path.basename(f).rsplit('.', 1)[0]
            try:
                f_module = imp.load_source("dock.plugins.%s" % module_name, f)
            except (IOError, OSError, ImportError) as ex:
                logger.warning("can't load module '%s': %s", f, repr(ex))
                continue
            for name in dir(f_module):
                binding = getattr(f_module, name, None)
                try:
                    # if you try to compare binding and PostBuildPlugin, python won't match them if you call
                    # this script directly b/c:
                    # ! <class 'plugins.plugin_rpmqa.PostBuildRPMqaPlugin'> <= <class '__main__.PostBuildPlugin'>
                    # but
                    # <class 'plugins.plugin_rpmqa.PostBuildRPMqaPlugin'> <= <class 'dock.plugin.PostBuildPlugin'>
                    is_sub = issubclass(binding, plugin_class)
                except TypeError:
                    is_sub = False
                if binding and is_sub and plugin_class.__name__ != binding.__name__:
                    plugin_classes[binding.key] = binding
        return plugin_classes

    def create_instance_from_plugin(self, plugin_class, plugin_conf):
        """
        create instance from plugin using the plugin class and configuration passed to for it

        input plugins and build plugins initialize differently

        :param plugin_class: plugin class
        :param plugin_conf: dict, configuration for plugin
        :return:
        """
        plugin_instance = plugin_class(**plugin_conf)
        return plugin_instance

    def run(self):
        """
        run all requested plugins
        """
        failed_msgs = []
        for plugin_request in self.plugins_conf:
            try:
                plugin_name = plugin_request['name']
            except (TypeError, KeyError):
                logger.error("Invalid plugin request, no key 'name': %s", plugin_request)
                continue
            try:
                plugin_conf = plugin_request.get("args", {})
            except AttributeError:
                logger.error("Invalid plugin request, no key 'args': %s", plugin_request)
                continue
            try:
                plugin_class = self.plugin_classes[plugin_name]
            except KeyError:
                logger.error("No such plugin: '%s', did you set the correct plugin type?", plugin_name)
                continue
            try:
                plugin_can_fail = plugin_request['can_fail']
            except (TypeError, KeyError):
                plugin_can_fail = getattr(plugin_class, "can_fail", True)

            logger.debug("running plugin '%s'", plugin_name)

            plugin_instance = self.create_instance_from_plugin(plugin_class, plugin_conf)

            try:
                plugin_response = plugin_instance.run()
            except Exception as ex:
                msg = "Plugin '%s' raised an exception: '%s'" % (plugin_instance.key, repr(ex))
                logger.warning(msg)
                logger.debug(traceback.format_exc())
                if not plugin_can_fail:
                    failed_msgs.append(msg)
                plugin_response = msg

            self.plugins_results[plugin_instance.key] = plugin_response
        if len(failed_msgs) == 1:
            raise PluginFailedException(failed_msgs[0])
        elif len(failed_msgs) > 1:
            raise PluginFailedException("Multiple plugins raised an exception: " + str(failed_msgs))
        return self.plugins_results

class BuildPluginsRunner(PluginsRunner):
    def __init__(self, dt, workflow, plugin_class_name, plugins_conf, *args, **kwargs):
        """
        constructor

        :param dt: DockerTasker instance
        :param workflow: DockerBuildWorkflow instance
        :param plugin_class_name: str, name of plugin class to filter (e.g. 'PreBuildPlugin')
        :param plugins_conf: dict, configuration for plugins
        """
        self.dt = dt
        self.workflow = workflow
        super(BuildPluginsRunner, self).__init__(plugin_class_name, plugins_conf, *args, **kwargs)

    def _translate_special_values(self, obj_to_translate):
        """
        you may want to write plugins for values which are not known before build:
        e.g. id of built image, base image name,... this method will therefore
        translate some reserved values to the runtime values
        """
        translation_dict = {
            'BUILT_IMAGE_ID': self.workflow.builder.image_id,
            'BUILD_DOCKERFILE_PATH' : self.workflow.builder.git_dockerfile_path,
            'BUILD_GIT_PATH' :  self.workflow.builder.git_path,
            'BASE_IMAGE': join_img_name_tag(self.workflow.builder.base_image_name,
                                            self.workflow.builder.base_tag)
        }
        if isinstance(obj_to_translate, dict):
            # Recurse into dicts
            translated_dict = copy.deepcopy(obj_to_translate)
            for key, value in obj_to_translate.items():
                translated_dict[key] = self._translate_special_values (value)

            return translated_dict
        elif isinstance(obj_to_translate, list):
            # Iterate over lists
            return [self._translate_special_values(elem)
                    for elem in obj_to_translate]
        else:
            return translation_dict.get(obj_to_translate, obj_to_translate)

    def create_instance_from_plugin(self, plugin_class, plugin_conf):
        translated_conf = self._translate_special_values(plugin_conf)
        logger.info("running plugin instance with args: '%s'", translated_conf)
        plugin_instance = plugin_class(self.dt, self.workflow, **translated_conf)
        return plugin_instance

class PreBuildPlugin(BuildPlugin):
    pass

class PreBuildPluginsRunner(BuildPluginsRunner):

    def __init__(self, dt, workflow, plugins_conf, *args, **kwargs):
        self.plugins_results = workflow.prebuild_results
        super(PreBuildPluginsRunner, self).__init__(dt, workflow, 'PreBuildPlugin', plugins_conf, *args, **kwargs)

class PrePublishPlugin(BuildPlugin):
    pass

class PrePublishPluginsRunner(BuildPluginsRunner):

    def __init__(self, dt, workflow, plugins_conf, *args, **kwargs):
        self.plugins_results = workflow.postbuild_results
        super(PrePublishPluginsRunner, self).__init__(dt, workflow, 'PrePublishPlugin', plugins_conf, *args, **kwargs)

class PostBuildPlugin(BuildPlugin):
    pass

class PostBuildPluginsRunner(BuildPluginsRunner):

    def __init__(self, dt, workflow, plugins_conf, *args, **kwargs):
        self.plugins_results = workflow.postbuild_results
        super(PostBuildPluginsRunner, self).__init__(dt, workflow, 'PostBuildPlugin', plugins_conf, *args, **kwargs)

class InputPlugin(Plugin):

    def __init__(self, substitutions=None, **kwargs):
        """
        constructor
        """
        # call parent constructor
        super(InputPlugin, self).__init__(**kwargs)
        self.substitutions = substitutions

    def substitute_configuration(self, build_json):
        """
        replace values of provided build json according to self.substitutions

        path to values can be specified in two ways:

         * single key value for root arguments, e.g. 'image'
         * plugin configuration: you following convention:

             plugin_type.plugin_name.argument_name

           hence

             prebuild_plugins.koji.target

        :param build_json: dict, build json
        :return: dict, substituted build json
        """
        # key: image, git_uri, prebuildplugins.koji.target, ...
        for key, value in self.substitutions.items():
            key_fragments = key.split(".")
            if len(key_fragments) == 1:
                logger.info("changing value '%s': '%s' -> '%s'",
                            key, build_json[key], value)
                build_json[key] = value
            else:
                try:
                    plugin_type, plugin_name, arg_name = key_fragments
                except ValueError:
                    logger.error("invalid absolute path: it requires exactly three parts: "
                                 "plugin type, plugin name, argument name separated be dot")
                    raise ValueError("invalid absolute path to plugin, it should be "
                                     "plugin_type.plugin_name.argument_name")
                else:
                    plugin_conf = get_plugin_conf(build_json, plugin_type, plugin_name)
                    if plugin_conf is None:
                        logger.warning("no plugin conf found, skipping...")
                    else:
                        logger.info("changing value '%s' of plugin '%s': '%s' -> '%s'",
                                    arg_name, plugin_name, plugin_conf['args'][arg_name], value)
                        plugin_conf['args'][arg_name] = value
        return build_json

class InputPluginsRunner(PluginsRunner):

    def __init__(self, plugins_conf, *args, **kwargs):
        self.plugins_results = {}
        super(InputPluginsRunner, self).__init__('InputPlugin', plugins_conf, *args, **kwargs)
<commit_msg>log when plugin fails and it's not an error<commit_after>"""
definition of plugin system

plugins are supposed to be run when image is built and we need to extract some information
"""
import copy
import importlib
import logging
import os
import sys
import traceback
import imp

import dock.plugins
from dock.util import join_img_name_tag

MODULE_EXTENSIONS = ('.py', '.pyc', '.pyo')
logger = logging.getLogger(__name__)

def get_plugin_conf(build_json, plugin_type, plugin_name):
    """
    return dict with configuration of a plugin from provided build json

    :param plugin_type: str, type of plugin (prebuild_plugins, postbuild_plugins, ...)
    :param plugin_name: str, unique name of the plugin
    :return: dict
    """
    logger.debug("getting plugin conf for '%s' with type '%s'",
                 plugin_name, plugin_type)
    plugins_of_a_type = build_json.get(plugin_type, None)
    if plugins_of_a_type is None:
        logger.warning("there are no plugins with type '%s'",
                       plugin_type)
        return
    plugin_conf = [x for x in plugins_of_a_type if x['name'] == plugin_name]
    plugins_num = len(plugin_conf)
    if plugins_num == 1:
        return plugin_conf[0]
    elif plugins_num <= 0:
        logger.warning("there is no configuration for plugin '%s'",
                       plugin_name)
        return
    else:
        logger.error("there is no configuration for plugin '%s'",
                     plugin_name)
        raise RuntimeError("plugin '%s' was specified multiple (%d) times, can't pick one",
                           plugin_name, plugins_num)

class PluginFailedException(Exception):
    """ There was an error during plugin execution """

class Plugin(object):
    """ abstract plugin class """

    # unique plugin identification
    # output of this plugin can be found in results specified with this key,
    # same thing goes for input: use this key for providing input for this plugin
    key = None
    # by default, if plugin fails (raises exc), execution continues
    can_fail = True

    def __init__(self, *args, **kwargs):
        """
        constructor
        """
        self.log = logging.getLogger("dock.plugins." + self.key)
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        return "%s" % self.key

    def __repr__(self):
        return "Plugin(key='%s')" % self.key

    def run(self):
        """
        each plugin has to implement this method -- it is used to run the plugin actually

        response from a build plugin is kept and used in json result response like this:

          results[plugin.key] = plugin.run()

        input plugins should emit build json with this method
        """
        raise NotImplemented()

class BuildPlugin(Plugin):
    """
    abstract plugin class: base for build plugins, it is
    flavored with DockerTasker and BuildWorkflow instances
    """

    def __init__(self, tasker, workflow, *args, **kwargs):
        """
        constructor

        :param tasker: DockerTasker instance
        :param workflow: DockerBuildWorkflow instance
        :param args: arguments from user input
        :param kwargs: keyword arguments from user input
        """
        self.tasker = tasker
        self.workflow = workflow
        super(BuildPlugin, self).__init__(*args, **kwargs)

class PluginsRunner(object):

    def __init__(self, plugin_class_name, plugins_conf, *args, **kwargs):
        """
        constructor

        :param plugin_class_name: str, name of plugin class to filter (e.g. 'PreBuildPlugin')
        :param plugins_conf: dict, configuration for plugins
        """
        self.plugins_results = getattr(self, "plugins_results", {})
        self.plugins_conf = plugins_conf or []
        self.plugin_files = kwargs.get("plugin_files", [])
        self.plugin_classes = self.load_plugins(plugin_class_name)

    def load_plugins(self, plugin_class_name):
        """
        load all available plugins
        """
        # imp.findmodule('dock') doesn't work
        plugins_dir = os.path.join(os.path.dirname(__file__), 'plugins')
        logger.debug("loading plugins from dir '%s'", plugins_dir)
        files = [os.path.join(plugins_dir, f) \
                 for f in os.listdir(plugins_dir) \
                 if f.endswith(".py")]
        if self.plugin_files:
            logger.debug("loading additional plugins from files '%s'", self.plugin_files)
            files += self.plugin_files
        plugin_class = globals()[plugin_class_name]
        plugin_classes = {}
        for f in files:
            logger.debug("load file '%s'", f)
            module_name = os.path.basename(f).rsplit('.', 1)[0]
            try:
                f_module = imp.load_source("dock.plugins.%s" % module_name, f)
            except (IOError, OSError, ImportError) as ex:
                logger.warning("can't load module '%s': %s", f, repr(ex))
                continue
            for name in dir(f_module):
                binding = getattr(f_module, name, None)
                try:
                    # if you try to compare binding and PostBuildPlugin, python won't match them if you call
                    # this script directly b/c:
                    # ! <class 'plugins.plugin_rpmqa.PostBuildRPMqaPlugin'> <= <class '__main__.PostBuildPlugin'>
                    # but
                    # <class 'plugins.plugin_rpmqa.PostBuildRPMqaPlugin'> <= <class 'dock.plugin.PostBuildPlugin'>
                    is_sub = issubclass(binding, plugin_class)
                except TypeError:
                    is_sub = False
                if binding and is_sub and plugin_class.__name__ != binding.__name__:
                    plugin_classes[binding.key] = binding
        return plugin_classes

    def create_instance_from_plugin(self, plugin_class, plugin_conf):
        """
        create instance from plugin using the plugin class and configuration passed to for it

        input plugins and build plugins initialize differently

        :param plugin_class: plugin class
        :param plugin_conf: dict, configuration for plugin
        :return:
        """
        plugin_instance = plugin_class(**plugin_conf)
        return plugin_instance

    def run(self):
        """
        run all requested plugins
        """
        failed_msgs = []
        for plugin_request in self.plugins_conf:
            try:
                plugin_name = plugin_request['name']
            except (TypeError, KeyError):
                logger.error("Invalid plugin request, no key 'name': %s", plugin_request)
                continue
            try:
                plugin_conf = plugin_request.get("args", {})
            except AttributeError:
                logger.error("Invalid plugin request, no key 'args': %s", plugin_request)
                continue
            try:
                plugin_class = self.plugin_classes[plugin_name]
            except KeyError:
                logger.error("No such plugin: '%s', did you set the correct plugin type?", plugin_name)
                continue
            try:
                plugin_can_fail = plugin_request['can_fail']
            except (TypeError, KeyError):
                plugin_can_fail = getattr(plugin_class, "can_fail", True)

            logger.debug("running plugin '%s'", plugin_name)

            plugin_instance = self.create_instance_from_plugin(plugin_class, plugin_conf)

            try:
                plugin_response = plugin_instance.run()
            except Exception as ex:
                msg = "Plugin '%s' raised an exception: '%s'" % (plugin_instance.key, repr(ex))
                logger.warning(msg)
                logger.debug(traceback.format_exc())
                if not plugin_can_fail:
                    failed_msgs.append(msg)
                else:
                    logger.info("Error is not fatal. Continuing...")
                plugin_response = msg

            self.plugins_results[plugin_instance.key] = plugin_response
        if len(failed_msgs) == 1:
            raise PluginFailedException(failed_msgs[0])
        elif len(failed_msgs) > 1:
            raise PluginFailedException("Multiple plugins raised an exception: " + str(failed_msgs))
        return self.plugins_results

class BuildPluginsRunner(PluginsRunner):
    def __init__(self, dt, workflow, plugin_class_name, plugins_conf, *args, **kwargs):
        """
        constructor

        :param dt: DockerTasker instance
        :param workflow: DockerBuildWorkflow instance
        :param plugin_class_name: str, name of plugin class to filter (e.g. 'PreBuildPlugin')
        :param plugins_conf: dict, configuration for plugins
        """
        self.dt = dt
        self.workflow = workflow
        super(BuildPluginsRunner, self).__init__(plugin_class_name, plugins_conf, *args, **kwargs)

    def _translate_special_values(self, obj_to_translate):
        """
        you may want to write plugins for values which are not known before build:
        e.g. id of built image, base image name,... this method will therefore
        translate some reserved values to the runtime values
        """
        translation_dict = {
            'BUILT_IMAGE_ID': self.workflow.builder.image_id,
            'BUILD_DOCKERFILE_PATH' : self.workflow.builder.git_dockerfile_path,
            'BUILD_GIT_PATH' :  self.workflow.builder.git_path,
            'BASE_IMAGE': join_img_name_tag(self.workflow.builder.base_image_name,
                                            self.workflow.builder.base_tag)
        }
        if isinstance(obj_to_translate, dict):
            # Recurse into dicts
            translated_dict = copy.deepcopy(obj_to_translate)
            for key, value in obj_to_translate.items():
                translated_dict[key] = self._translate_special_values (value)

            return translated_dict
        elif isinstance(obj_to_translate, list):
            # Iterate over lists
            return [self._translate_special_values(elem)
                    for elem in obj_to_translate]
        else:
            return translation_dict.get(obj_to_translate, obj_to_translate)

    def create_instance_from_plugin(self, plugin_class, plugin_conf):
        translated_conf = self._translate_special_values(plugin_conf)
        logger.info("running plugin instance with args: '%s'", translated_conf)
        plugin_instance = plugin_class(self.dt, self.workflow, **translated_conf)
        return plugin_instance

class PreBuildPlugin(BuildPlugin):
    pass

class PreBuildPluginsRunner(BuildPluginsRunner):

    def __init__(self, dt, workflow, plugins_conf, *args, **kwargs):
        self.plugins_results = workflow.prebuild_results
        super(PreBuildPluginsRunner, self).__init__(dt, workflow, 'PreBuildPlugin', plugins_conf, *args, **kwargs)

class PrePublishPlugin(BuildPlugin):
    pass

class PrePublishPluginsRunner(BuildPluginsRunner):

    def __init__(self, dt, workflow, plugins_conf, *args, **kwargs):
        self.plugins_results = workflow.postbuild_results
        super(PrePublishPluginsRunner, self).__init__(dt, workflow, 'PrePublishPlugin', plugins_conf, *args, **kwargs)

class PostBuildPlugin(BuildPlugin):
    pass

class PostBuildPluginsRunner(BuildPluginsRunner):

    def __init__(self, dt, workflow, plugins_conf, *args, **kwargs):
        self.plugins_results = workflow.postbuild_results
        super(PostBuildPluginsRunner, self).__init__(dt, workflow, 'PostBuildPlugin', plugins_conf, *args, **kwargs)

class InputPlugin(Plugin):

    def __init__(self, substitutions=None, **kwargs):
        """
        constructor
        """
        # call parent constructor
        super(InputPlugin, self).__init__(**kwargs)
        self.substitutions = substitutions

    def substitute_configuration(self, build_json):
        """
        replace values of provided build json according to self.substitutions

        path to values can be specified in two ways:

         * single key value for root arguments, e.g. 'image'
         * plugin configuration: you following convention:

             plugin_type.plugin_name.argument_name

           hence

             prebuild_plugins.koji.target

        :param build_json: dict, build json
        :return: dict, substituted build json
        """
        # key: image, git_uri, prebuildplugins.koji.target, ...
        for key, value in self.substitutions.items():
            key_fragments = key.split(".")
            if len(key_fragments) == 1:
                logger.info("changing value '%s': '%s' -> '%s'",
                            key, build_json[key], value)
                build_json[key] = value
            else:
                try:
                    plugin_type, plugin_name, arg_name = key_fragments
                except ValueError:
                    logger.error("invalid absolute path: it requires exactly three parts: "
                                 "plugin type, plugin name, argument name separated be dot")
                    raise ValueError("invalid absolute path to plugin, it should be "
                                     "plugin_type.plugin_name.argument_name")
                else:
                    plugin_conf = get_plugin_conf(build_json, plugin_type, plugin_name)
                    if plugin_conf is None:
                        logger.warning("no plugin conf found, skipping...")
                    else:
                        logger.info("changing value '%s' of plugin '%s': '%s' -> '%s'",
                                    arg_name, plugin_name, plugin_conf['args'][arg_name], value)
                        plugin_conf['args'][arg_name] = value
        return build_json

class InputPluginsRunner(PluginsRunner):

    def __init__(self, plugins_conf, *args, **kwargs):
        self.plugins_results = {}
        super(InputPluginsRunner, self).__init__('InputPlugin', plugins_conf, *args, **kwargs)
<|endoftext|>


<commit_before>import re
import os.path

from django.core.urlresolvers import reverse
from django.conf import settings
from django.db import models
from django.utils.translation import ugettext_lazy as _, ugettext

from guardian.shortcuts import assign, get_objects_for_user
from taggit.managers import TaggableManager

from projects.models import Project
from projects import constants
from .constants import BUILD_STATE, BUILD_TYPES, VERSION_TYPES

class VersionManager(models.Manager):
    def _filter_queryset(self, user, project, privacy_level, only_active):
        if isinstance(privacy_level, basestring):
            privacy_level = (privacy_level,)
        queryset = Version.objects.filter(privacy_level__in=privacy_level)
        # Remove this so we can use public() for all active public projects
        #if not user and not project:
            #return queryset
        if user and user.is_authenticated():
            # Add in possible user-specific views
            user_queryset = get_objects_for_user(user, 'builds.view_version')
            queryset = user_queryset | queryset
        elif user:
            # Hack around get_objects_for_user not supporting global perms
            global_access = user.has_perm('builds.view_version')
            if global_access:
                queryset = Version.objects.all()
        if project:
            # Filter by project if requested
            queryset = queryset.filter(project=project)
        if only_active:
            queryset = queryset.filter(active=True)
        return queryset

    def active(self, user=None, project=None, *args, **kwargs):
        queryset = self._filter_queryset(
            user,
            project,
            privacy_level=(constants.PUBLIC, constants.PROTECTED,
                           constants.PRIVATE),
            only_active=True,
        )
        return queryset.filter(*args, **kwargs)

    def public(self, user=None, project=None, only_active=True, *args,
               **kwargs):
        queryset = self._filter_queryset(
            user,
            project,
            privacy_level=(constants.PUBLIC),
            only_active=only_active
        )
        return queryset.filter(*args, **kwargs)

    def protected(self, user=None, project=None, only_active=True, *args,
                  **kwargs):
        queryset = self._filter_queryset(
            user,
            project,
            privacy_level=(constants.PUBLIC, constants.PROTECTED),
            only_active=only_active
        )
        return queryset.filter(*args, **kwargs)

    def private(self, user=None, project=None, only_active=True, *args,
                **kwargs):
        queryset = self._filter_queryset(
            user,
            project,
            privacy_level=(constants.PRIVATE),
            only_active=only_active
        )
        return queryset.filter(*args, **kwargs)

class Version(models.Model):
    project = models.ForeignKey(Project, verbose_name=_('Project'),
                                related_name='versions')
    type = models.CharField(
        _('Type'), max_length=20,
        choices=VERSION_TYPES, default='unknown',
    )
    # used by the vcs backend
    identifier = models.CharField(_('Identifier'), max_length=255)

    verbose_name = models.CharField(_('Verbose Name'), max_length=255)
    slug = models.CharField(_('Slug'), max_length=255)

    supported = models.BooleanField(_('Supported'), default=True)
    active = models.BooleanField(_('Active'), default=False)
    built = models.BooleanField(_('Built'), default=False)
    uploaded = models.BooleanField(_('Uploaded'), default=False)
    privacy_level = models.CharField(
        _('Privacy Level'), max_length=20, choices=constants.PRIVACY_CHOICES,
        default='public', help_text=_("Level of privacy for this Version.")
    )
    tags = TaggableManager(blank=True)
    objects = VersionManager()

    class Meta:
        unique_together = [('project', 'slug')]
        ordering = ['-verbose_name']
        permissions = (
            # Translators: Permission around whether a user can view the
            #              version
            ('view_version', _('View Version')),
        )

    def __unicode__(self):
        return ugettext(u"Version %(version)s of %(project)s (%(pk)s)" % {
            'version': self.verbose_name,
            'project': self.project,
            'pk': self.pk
        })

    def get_absolute_url(self):
        if not self.built and not self.uploaded:
            return ''
        return self.project.get_docs_url(version_slug=self.slug)

    def save(self, *args, **kwargs):
        """
        Add permissions to the Version for all owners on save.
        """
        obj = super(Version, self).save(*args, **kwargs)
        for owner in self.project.users.all():
            assign('view_version', owner, self)
        self.project.sync_supported_versions()
        return obj

    @property
    def remote_slug(self):
        if self.slug == 'latest':
            if self.project.default_branch:
                return self.project.default_branch
            else:
                return self.project.vcs_repo().fallback_branch
        else:
            return self.slug

    def get_subdomain_url(self):
        use_subdomain = getattr(settings, 'USE_SUBDOMAIN', False)
        if use_subdomain:
            return "/%s/%s/" % (
                self.project.language,
                self.slug,
            )
        else:
            return reverse('docs_detail', kwargs={
                'project_slug': self.project.slug,
                'lang_slug': self.project.language,
                'version_slug': self.slug,
                'filename': ''
            })

    def get_subproject_url(self):
        return "/projects/%s/%s/%s/" % (
            self.project.slug,
            self.project.language,
            self.slug,
        )

    def get_downloads(self, pretty=False):
        project = self.project
        data = {}
        if pretty:
            if project.has_pdf(self.slug):
                data['PDF'] = project.get_pdf_url(self.slug)
            if project.has_htmlzip(self.slug):
                data['HTML'] = project.get_htmlzip_url(self.slug)
            if project.has_epub(self.slug):
                data['Epub'] = project.get_epub_url(self.slug)
        else:
            if project.has_pdf(self.slug):
                data['pdf_url'] = project.get_pdf_url(self.slug)
            if project.has_htmlzip(self.slug):
                data['htmlzip_url'] = project.get_htmlzip_url(self.slug)
            if project.has_epub(self.slug):
                data['epub_url'] = project.get_epub_url(self.slug)
            #if project.has_manpage(self.slug):
                #data['manpage_url'] = project.get_manpage_url(self.slug)
            if project.has_dash(self.slug):
                data['dash_url'] = project.get_dash_url(self.slug)
                data['dash_feed_url'] = project.get_dash_feed_url(self.slug)
        return data

    def get_conf_py_path(self):
        # Hack this for now.
        return "/docs/"
        conf_py_path = self.project.conf_file(self.slug)
        conf_py_path = conf_py_path.replace(
            self.project.checkout_path(self.slug), '')
        return conf_py_path.replace('conf.py', '')

    def get_build_path(self):
        '''Return version build path if path exists, otherwise `None`'''
        path = self.project.checkout_path(version=self.slug)
        if os.path.exists(path):
            return path
        return None

    def get_github_url(self, docroot, filename, source_suffix):
        GITHUB_REGEXS = [
            re.compile('github.com/(.+)/(.+)(?:\.git){1}'),
            re.compile('github.com/(.+)/(.+)'),
            re.compile('github.com:(.+)/(.+).git'),
        ]
        GITHUB_URL = 'https://github.com/{user}/{repo}/blob/{version}{docroot}{path}{source_suffix}'

        repo_url = self.project.repo
        if 'github' not in repo_url:
            return ''
        if not docroot:
            return ''

        for regex in GITHUB_REGEXS:
            match = regex.search(repo_url)
            if match:
                user, repo = match.groups()
                break
        else:
            return ''
        repo = repo.rstrip('/')

        return GITHUB_URL.format(
            user=user,
            repo=repo,
            version=self.remote_slug,
            docroot=docroot,
            path=filename,
            source_suffix=source_suffix,
            )

    def get_bitbucket_url(self, docroot, filename, source_suffix):
        BB_REGEXS = [
            re.compile('bitbucket.org/(.+)/(.+).git'),
            re.compile('bitbucket.org/(.+)/(.+)/'),
            re.compile('bitbucket.org/(.+)/(.+)'),
        ]
        BB_URL = 'https://bitbucket.org/{user}/{repo}/src/{version}{docroot}{path}{source_suffix}'

        repo_url = self.project.repo
        if 'bitbucket' not in repo_url:
            return ''
        if not docroot:
            return ''

        for regex in BB_REGEXS:
            match = regex.search(repo_url)
            if match:
                user, repo = match.groups()
                break
        else:
            return ''
        repo = repo.rstrip('/')

        return BB_URL.format(
            user=user,
            repo=repo,
            version=self.remote_slug,
            docroot=docroot,
            path=filename,
            source_suffix=source_suffix,
            )

class VersionAlias(models.Model):
    project = models.ForeignKey(Project, verbose_name=_('Project'),
                                related_name='aliases')
    from_slug = models.CharField(_('From slug'), max_length=255, default='')
    to_slug = models.CharField(_('To slug'), max_length=255, default='',
                               blank=True)
    largest = models.BooleanField(_('Largest'), default=False)

    def __unicode__(self):
        return ugettext(u"Alias for %(project)s: %(from)s -> %(to)s" % {
            'project': self.project,
            'form': self.from_slug,
            'to': self.to_slug,
        })

class Build(models.Model):
    project = models.ForeignKey(Project, verbose_name=_('Project'),
                                related_name='builds')
    version = models.ForeignKey(Version, verbose_name=_('Version'), null=True,
                                related_name='builds')
    type = models.CharField(_('Type'), max_length=55, choices=BUILD_TYPES,
                            default='html')
    state = models.CharField(_('State'), max_length=55, choices=BUILD_STATE,
                             default='finished')
    date = models.DateTimeField(_('Date'), auto_now_add=True)
    success = models.BooleanField(_('Success'))

    setup = models.TextField(_('Setup'), null=True, blank=True)
    setup_error = models.TextField(_('Setup error'), null=True, blank=True)
    output = models.TextField(_('Output'), default='', blank=True)
    error = models.TextField(_('Error'), default='', blank=True)
    exit_code = models.IntegerField(_('Exit code'), max_length=3, null=True,
                                    blank=True)

    class Meta:
        ordering = ['-date']
        get_latest_by = 'date'

    def __unicode__(self):
        return ugettext(u"Build %(project)s for %(usernames)s (%(pk)s)" % {
            'project': self.project,
            'usernames': ' '.join(self.project.users.all()
                                  .values_list('username', flat=True)),
            'pk': self.pk,
        })

    @models.permalink
    def get_absolute_url(self):
        return ('builds_detail', [self.project.slug, self.pk])
<commit_msg>Default to rst<commit_after>import re
import os.path

from django.core.urlresolvers import reverse
from django.conf import settings
from django.db import models
from django.utils.translation import ugettext_lazy as _, ugettext

from guardian.shortcuts import assign, get_objects_for_user
from taggit.managers import TaggableManager

from projects.models import Project
from projects import constants
from .constants import BUILD_STATE, BUILD_TYPES, VERSION_TYPES

class VersionManager(models.Manager):
    def _filter_queryset(self, user, project, privacy_level, only_active):
        if isinstance(privacy_level, basestring):
            privacy_level = (privacy_level,)
        queryset = Version.objects.filter(privacy_level__in=privacy_level)
        # Remove this so we can use public() for all active public projects
        #if not user and not project:
            #return queryset
        if user and user.is_authenticated():
            # Add in possible user-specific views
            user_queryset = get_objects_for_user(user, 'builds.view_version')
            queryset = user_queryset | queryset
        elif user:
            # Hack around get_objects_for_user not supporting global perms
            global_access = user.has_perm('builds.view_version')
            if global_access:
                queryset = Version.objects.all()
        if project:
            # Filter by project if requested
            queryset = queryset.filter(project=project)
        if only_active:
            queryset = queryset.filter(active=True)
        return queryset

    def active(self, user=None, project=None, *args, **kwargs):
        queryset = self._filter_queryset(
            user,
            project,
            privacy_level=(constants.PUBLIC, constants.PROTECTED,
                           constants.PRIVATE),
            only_active=True,
        )
        return queryset.filter(*args, **kwargs)

    def public(self, user=None, project=None, only_active=True, *args,
               **kwargs):
        queryset = self._filter_queryset(
            user,
            project,
            privacy_level=(constants.PUBLIC),
            only_active=only_active
        )
        return queryset.filter(*args, **kwargs)

    def protected(self, user=None, project=None, only_active=True, *args,
                  **kwargs):
        queryset = self._filter_queryset(
            user,
            project,
            privacy_level=(constants.PUBLIC, constants.PROTECTED),
            only_active=only_active
        )
        return queryset.filter(*args, **kwargs)

    def private(self, user=None, project=None, only_active=True, *args,
                **kwargs):
        queryset = self._filter_queryset(
            user,
            project,
            privacy_level=(constants.PRIVATE),
            only_active=only_active
        )
        return queryset.filter(*args, **kwargs)

class Version(models.Model):
    project = models.ForeignKey(Project, verbose_name=_('Project'),
                                related_name='versions')
    type = models.CharField(
        _('Type'), max_length=20,
        choices=VERSION_TYPES, default='unknown',
    )
    # used by the vcs backend
    identifier = models.CharField(_('Identifier'), max_length=255)

    verbose_name = models.CharField(_('Verbose Name'), max_length=255)
    slug = models.CharField(_('Slug'), max_length=255)

    supported = models.BooleanField(_('Supported'), default=True)
    active = models.BooleanField(_('Active'), default=False)
    built = models.BooleanField(_('Built'), default=False)
    uploaded = models.BooleanField(_('Uploaded'), default=False)
    privacy_level = models.CharField(
        _('Privacy Level'), max_length=20, choices=constants.PRIVACY_CHOICES,
        default='public', help_text=_("Level of privacy for this Version.")
    )
    tags = TaggableManager(blank=True)
    objects = VersionManager()

    class Meta:
        unique_together = [('project', 'slug')]
        ordering = ['-verbose_name']
        permissions = (
            # Translators: Permission around whether a user can view the
            #              version
            ('view_version', _('View Version')),
        )

    def __unicode__(self):
        return ugettext(u"Version %(version)s of %(project)s (%(pk)s)" % {
            'version': self.verbose_name,
            'project': self.project,
            'pk': self.pk
        })

    def get_absolute_url(self):
        if not self.built and not self.uploaded:
            return ''
        return self.project.get_docs_url(version_slug=self.slug)

    def save(self, *args, **kwargs):
        """
        Add permissions to the Version for all owners on save.
        """
        obj = super(Version, self).save(*args, **kwargs)
        for owner in self.project.users.all():
            assign('view_version', owner, self)
        self.project.sync_supported_versions()
        return obj

    @property
    def remote_slug(self):
        if self.slug == 'latest':
            if self.project.default_branch:
                return self.project.default_branch
            else:
                return self.project.vcs_repo().fallback_branch
        else:
            return self.slug

    def get_subdomain_url(self):
        use_subdomain = getattr(settings, 'USE_SUBDOMAIN', False)
        if use_subdomain:
            return "/%s/%s/" % (
                self.project.language,
                self.slug,
            )
        else:
            return reverse('docs_detail', kwargs={
                'project_slug': self.project.slug,
                'lang_slug': self.project.language,
                'version_slug': self.slug,
                'filename': ''
            })

    def get_subproject_url(self):
        return "/projects/%s/%s/%s/" % (
            self.project.slug,
            self.project.language,
            self.slug,
        )

    def get_downloads(self, pretty=False):
        project = self.project
        data = {}
        if pretty:
            if project.has_pdf(self.slug):
                data['PDF'] = project.get_pdf_url(self.slug)
            if project.has_htmlzip(self.slug):
                data['HTML'] = project.get_htmlzip_url(self.slug)
            if project.has_epub(self.slug):
                data['Epub'] = project.get_epub_url(self.slug)
        else:
            if project.has_pdf(self.slug):
                data['pdf_url'] = project.get_pdf_url(self.slug)
            if project.has_htmlzip(self.slug):
                data['htmlzip_url'] = project.get_htmlzip_url(self.slug)
            if project.has_epub(self.slug):
                data['epub_url'] = project.get_epub_url(self.slug)
            #if project.has_manpage(self.slug):
                #data['manpage_url'] = project.get_manpage_url(self.slug)
            if project.has_dash(self.slug):
                data['dash_url'] = project.get_dash_url(self.slug)
                data['dash_feed_url'] = project.get_dash_feed_url(self.slug)
        return data

    def get_conf_py_path(self):
        # Hack this for now.
        return "/docs/"
        conf_py_path = self.project.conf_file(self.slug)
        conf_py_path = conf_py_path.replace(
            self.project.checkout_path(self.slug), '')
        return conf_py_path.replace('conf.py', '')

    def get_build_path(self):
        '''Return version build path if path exists, otherwise `None`'''
        path = self.project.checkout_path(version=self.slug)
        if os.path.exists(path):
            return path
        return None

    def get_github_url(self, docroot, filename, source_suffix='.rst'):
        GITHUB_REGEXS = [
            re.compile('github.com/(.+)/(.+)(?:\.git){1}'),
            re.compile('github.com/(.+)/(.+)'),
            re.compile('github.com:(.+)/(.+).git'),
        ]
        GITHUB_URL = 'https://github.com/{user}/{repo}/blob/{version}{docroot}{path}{source_suffix}'

        repo_url = self.project.repo
        if 'github' not in repo_url:
            return ''
        if not docroot:
            return ''

        for regex in GITHUB_REGEXS:
            match = regex.search(repo_url)
            if match:
                user, repo = match.groups()
                break
        else:
            return ''
        repo = repo.rstrip('/')

        return GITHUB_URL.format(
            user=user,
            repo=repo,
            version=self.remote_slug,
            docroot=docroot,
            path=filename,
            source_suffix=source_suffix,
            )

    def get_bitbucket_url(self, docroot, filename, source_suffix='.rst'):
        BB_REGEXS = [
            re.compile('bitbucket.org/(.+)/(.+).git'),
            re.compile('bitbucket.org/(.+)/(.+)/'),
            re.compile('bitbucket.org/(.+)/(.+)'),
        ]
        BB_URL = 'https://bitbucket.org/{user}/{repo}/src/{version}{docroot}{path}{source_suffix}'

        repo_url = self.project.repo
        if 'bitbucket' not in repo_url:
            return ''
        if not docroot:
            return ''

        for regex in BB_REGEXS:
            match = regex.search(repo_url)
            if match:
                user, repo = match.groups()
                break
        else:
            return ''
        repo = repo.rstrip('/')

        return BB_URL.format(
            user=user,
            repo=repo,
            version=self.remote_slug,
            docroot=docroot,
            path=filename,
            source_suffix=source_suffix,
            )

class VersionAlias(models.Model):
    project = models.ForeignKey(Project, verbose_name=_('Project'),
                                related_name='aliases')
    from_slug = models.CharField(_('From slug'), max_length=255, default='')
    to_slug = models.CharField(_('To slug'), max_length=255, default='',
                               blank=True)
    largest = models.BooleanField(_('Largest'), default=False)

    def __unicode__(self):
        return ugettext(u"Alias for %(project)s: %(from)s -> %(to)s" % {
            'project': self.project,
            'form': self.from_slug,
            'to': self.to_slug,
        })

class Build(models.Model):
    project = models.ForeignKey(Project, verbose_name=_('Project'),
                                related_name='builds')
    version = models.ForeignKey(Version, verbose_name=_('Version'), null=True,
                                related_name='builds')
    type = models.CharField(_('Type'), max_length=55, choices=BUILD_TYPES,
                            default='html')
    state = models.CharField(_('State'), max_length=55, choices=BUILD_STATE,
                             default='finished')
    date = models.DateTimeField(_('Date'), auto_now_add=True)
    success = models.BooleanField(_('Success'))

    setup = models.TextField(_('Setup'), null=True, blank=True)
    setup_error = models.TextField(_('Setup error'), null=True, blank=True)
    output = models.TextField(_('Output'), default='', blank=True)
    error = models.TextField(_('Error'), default='', blank=True)
    exit_code = models.IntegerField(_('Exit code'), max_length=3, null=True,
                                    blank=True)

    class Meta:
        ordering = ['-date']
        get_latest_by = 'date'

    def __unicode__(self):
        return ugettext(u"Build %(project)s for %(usernames)s (%(pk)s)" % {
            'project': self.project,
            'usernames': ' '.join(self.project.users.all()
                                  .values_list('username', flat=True)),
            'pk': self.pk,
        })

    @models.permalink
    def get_absolute_url(self):
        return ('builds_detail', [self.project.slug, self.pk])
<|endoftext|>


<commit_before>"""URL resolver for documentation"""

import re

from django.conf import settings

from readthedocs.projects.constants import PRIVATE, PUBLIC
from readthedocs.core.utils.extend import SettingsOverrideObject

class ResolverBase(object):

    """
    Read the Docs URL Resolver.

    Url Types:

    - Subdomain
    - CNAME

    Path Types:

    - Subproject
    - Single Version
    - Normal

    All possible URL's::

        Subdomain or CNAME:

        # Default
        /<lang>/<version>/<filename>
        # Single Version
        /<filename>
        # Subproject Default
        /projects/<subproject_slug>/<lang>/<version>/<filename>
        # Subproject Single Version
        /projects/<subproject_slug>/<filename>

        Development serving:

        # Default
        /docs/<project_slug>/<lang>/<version>/<filename>
        # Single Version
        /docs/<project_slug>/<filename>
        # Subproject Default
        /docs/<project_slug>/projects/<subproject_slug>/<lang>/<version>/<filename>
        # Subproject Single Version
        /docs/<project_slug>/projects/<subproject_slug>/<filename>
    """

    def base_resolve_path(self, project_slug, filename, version_slug=None,
                          language=None, private=False, single_version=None,
                          subproject_slug=None, subdomain=None, cname=None):
        """Resolve a with nothing smart, just filling in the blanks"""
        # Only support `/docs/project' URLs outside our normal environment. Normally
        # the path should always have a subdomain or CNAME domain
        if subdomain or cname or (self._use_subdomain()):
            url = '/'
        else:
            url = '/docs/{project_slug}/'

        if subproject_slug:
            url += 'projects/{subproject_slug}/'

        if single_version:
            url += '{filename}'
        else:
            url += '{language}/{version_slug}/{filename}'

        return url.format(
            project_slug=project_slug, filename=filename,
            version_slug=version_slug, language=language,
            single_version=single_version, subproject_slug=subproject_slug,
        )

    def resolve_path(self, project, filename='', version_slug=None,
                     language=None, single_version=None, subdomain=None,
                     cname=None, private=None):
        """Resolve a URL with a subset of fields defined"""
        relation = project.superprojects.first()
        cname = cname or project.domains.filter(canonical=True).first()
        main_language_project = project.main_language_project

        version_slug = version_slug or project.get_default_version()
        language = language or project.language

        if private is None:
            private = self._get_private(project, version_slug)

        filename = self._fix_filename(project, filename)

        if main_language_project:
            project_slug = main_language_project.slug
            language = project.language
            subproject_slug = None
        elif relation:
            project_slug = relation.parent.slug
            subproject_slug = relation.alias
            cname = relation.parent.domains.filter(canonical=True).first()
        else:
            project_slug = project.slug
            subproject_slug = None

        if project.single_version or single_version:
            single_version = True
        else:
            single_version = False

        return self.base_resolve_path(
            project_slug=project_slug,
            filename=filename,
            version_slug=version_slug,
            language=language,
            single_version=single_version,
            subproject_slug=subproject_slug,
            cname=cname,
            private=private,
            subdomain=subdomain,
        )

    def resolve_domain(self, project, private=None):
        canonical_project = self._get_canonical_project(project)
        domain = canonical_project.domains.filter(canonical=True).first()
        if domain:
            return domain.domain
        elif self._use_subdomain():
            return self._get_project_subdomain(canonical_project)
        else:
            return getattr(settings, 'PRODUCTION_DOMAIN')

    def resolve(self, project, protocol='http', filename='', private=None,
                **kwargs):
        if private is None:
            version_slug = kwargs.get('version_slug')
            if version_slug is None:
                version_slug = project.get_default_version()
            private = self._get_private(project, version_slug)

        return '{protocol}://{domain}{path}'.format(
            protocol=protocol,
            domain=self.resolve_domain(project, private=private),
            path=self.resolve_path(project, filename=filename, private=private,
                                   **kwargs),
        )

    def _get_canonical_project(self, project):
        """Get canonical project in the case of subproject or translations

        :type project: Project
        :rtype: Project
        """
        main_language_project = project.main_language_project
        relation = project.superprojects.first()
        if main_language_project:
            return main_language_project
        elif relation:
            return relation.parent
        else:
            return project

    def _get_project_subdomain(self, project):
        """Determine canonical project domain as subdomain"""
        public_domain = getattr(settings, 'PUBLIC_DOMAIN', None)
        if self._use_subdomain():
            project = self._get_canonical_project(project)
            subdomain_slug = project.slug.replace('_', '-')
            return "%s.%s" % (subdomain_slug, public_domain)

    def _get_private(self, project, version_slug):
        from readthedocs.builds.models import Version
        try:
            version = project.versions.get(slug=version_slug)
            private = version.privacy_level == PRIVATE
        except Version.DoesNotExist:
            private = getattr(settings, 'DEFAULT_PRIVACY_LEVEL', PUBLIC) == PRIVATE
        return private

    def _fix_filename(self, project, filename):
        """
        Force filenames that might be HTML file paths into proper URL's

        This basically means stripping / and .html endings and then re-adding them properly.
        """
        # Bail out on non-html files
        if '.' in filename and '.html' not in filename:
            return filename
        filename = filename.lstrip('/')
        filename = re.sub(r'(^|/)index(?:.html)?$', '\\1', filename)
        if filename:
            if filename.endswith('/') or filename.endswith('.html'):
                path = filename
            elif project.documentation_type == "sphinx_singlehtml":
                path = "index.html#document-" + filename
            elif project.documentation_type in ["sphinx_htmldir", "mkdocs"]:
                path = filename + "/"
            elif '#' in filename:
                # do nothing if the filename contains URL fragments
                path = filename
            else:
                path = filename + ".html"
        else:
            path = ""
        return path

    def _use_subdomain(self):
        """Make decision about whether to use a subdomain to serve docs"""
        use_subdomain = getattr(settings, 'USE_SUBDOMAIN', False)
        public_domain = getattr(settings, 'PUBLIC_DOMAIN', None)
        return use_subdomain and public_domain is not None

class Resolver(SettingsOverrideObject):

    _default_class = ResolverBase
    _override_setting = 'RESOLVER_CLASS'

resolver = Resolver()
resolve_path = resolver.resolve_path
resolve_domain = resolver.resolve_domain
resolve = resolver.resolve
<commit_msg>Fix unicode handling in resolver<commit_after>"""URL resolver for documentation"""

import re

from django.conf import settings

from readthedocs.projects.constants import PRIVATE, PUBLIC
from readthedocs.core.utils.extend import SettingsOverrideObject

class ResolverBase(object):

    """
    Read the Docs URL Resolver.

    Url Types:

    - Subdomain
    - CNAME

    Path Types:

    - Subproject
    - Single Version
    - Normal

    All possible URL's::

        Subdomain or CNAME:

        # Default
        /<lang>/<version>/<filename>
        # Single Version
        /<filename>
        # Subproject Default
        /projects/<subproject_slug>/<lang>/<version>/<filename>
        # Subproject Single Version
        /projects/<subproject_slug>/<filename>

        Development serving:

        # Default
        /docs/<project_slug>/<lang>/<version>/<filename>
        # Single Version
        /docs/<project_slug>/<filename>
        # Subproject Default
        /docs/<project_slug>/projects/<subproject_slug>/<lang>/<version>/<filename>
        # Subproject Single Version
        /docs/<project_slug>/projects/<subproject_slug>/<filename>
    """

    def base_resolve_path(self, project_slug, filename, version_slug=None,
                          language=None, private=False, single_version=None,
                          subproject_slug=None, subdomain=None, cname=None):
        """Resolve a with nothing smart, just filling in the blanks"""
        # Only support `/docs/project' URLs outside our normal environment. Normally
        # the path should always have a subdomain or CNAME domain
        if subdomain or cname or (self._use_subdomain()):
            url = u'/'
        else:
            url = u'/docs/{project_slug}/'

        if subproject_slug:
            url += u'projects/{subproject_slug}/'

        if single_version:
            url += u'{filename}'
        else:
            url += u'{language}/{version_slug}/{filename}'

        return url.format(
            project_slug=project_slug, filename=filename,
            version_slug=version_slug, language=language,
            single_version=single_version, subproject_slug=subproject_slug,
        )

    def resolve_path(self, project, filename='', version_slug=None,
                     language=None, single_version=None, subdomain=None,
                     cname=None, private=None):
        """Resolve a URL with a subset of fields defined"""
        relation = project.superprojects.first()
        cname = cname or project.domains.filter(canonical=True).first()
        main_language_project = project.main_language_project

        version_slug = version_slug or project.get_default_version()
        language = language or project.language

        if private is None:
            private = self._get_private(project, version_slug)

        filename = self._fix_filename(project, filename)

        if main_language_project:
            project_slug = main_language_project.slug
            language = project.language
            subproject_slug = None
        elif relation:
            project_slug = relation.parent.slug
            subproject_slug = relation.alias
            cname = relation.parent.domains.filter(canonical=True).first()
        else:
            project_slug = project.slug
            subproject_slug = None

        if project.single_version or single_version:
            single_version = True
        else:
            single_version = False

        return self.base_resolve_path(
            project_slug=project_slug,
            filename=filename,
            version_slug=version_slug,
            language=language,
            single_version=single_version,
            subproject_slug=subproject_slug,
            cname=cname,
            private=private,
            subdomain=subdomain,
        )

    def resolve_domain(self, project, private=None):
        canonical_project = self._get_canonical_project(project)
        domain = canonical_project.domains.filter(canonical=True).first()
        if domain:
            return domain.domain
        elif self._use_subdomain():
            return self._get_project_subdomain(canonical_project)
        else:
            return getattr(settings, 'PRODUCTION_DOMAIN')

    def resolve(self, project, protocol='http', filename='', private=None,
                **kwargs):
        if private is None:
            version_slug = kwargs.get('version_slug')
            if version_slug is None:
                version_slug = project.get_default_version()
            private = self._get_private(project, version_slug)

        return '{protocol}://{domain}{path}'.format(
            protocol=protocol,
            domain=self.resolve_domain(project, private=private),
            path=self.resolve_path(project, filename=filename, private=private,
                                   **kwargs),
        )

    def _get_canonical_project(self, project):
        """Get canonical project in the case of subproject or translations

        :type project: Project
        :rtype: Project
        """
        main_language_project = project.main_language_project
        relation = project.superprojects.first()
        if main_language_project:
            return main_language_project
        elif relation:
            return relation.parent
        else:
            return project

    def _get_project_subdomain(self, project):
        """Determine canonical project domain as subdomain"""
        public_domain = getattr(settings, 'PUBLIC_DOMAIN', None)
        if self._use_subdomain():
            project = self._get_canonical_project(project)
            subdomain_slug = project.slug.replace('_', '-')
            return "%s.%s" % (subdomain_slug, public_domain)

    def _get_private(self, project, version_slug):
        from readthedocs.builds.models import Version
        try:
            version = project.versions.get(slug=version_slug)
            private = version.privacy_level == PRIVATE
        except Version.DoesNotExist:
            private = getattr(settings, 'DEFAULT_PRIVACY_LEVEL', PUBLIC) == PRIVATE
        return private

    def _fix_filename(self, project, filename):
        """
        Force filenames that might be HTML file paths into proper URL's

        This basically means stripping / and .html endings and then re-adding them properly.
        """
        # Bail out on non-html files
        if '.' in filename and '.html' not in filename:
            return filename
        filename = filename.lstrip('/')
        filename = re.sub(r'(^|/)index(?:.html)?$', '\\1', filename)
        if filename:
            if filename.endswith('/') or filename.endswith('.html'):
                path = filename
            elif project.documentation_type == "sphinx_singlehtml":
                path = "index.html#document-" + filename
            elif project.documentation_type in ["sphinx_htmldir", "mkdocs"]:
                path = filename + "/"
            elif '#' in filename:
                # do nothing if the filename contains URL fragments
                path = filename
            else:
                path = filename + ".html"
        else:
            path = ""
        return path

    def _use_subdomain(self):
        """Make decision about whether to use a subdomain to serve docs"""
        use_subdomain = getattr(settings, 'USE_SUBDOMAIN', False)
        public_domain = getattr(settings, 'PUBLIC_DOMAIN', None)
        return use_subdomain and public_domain is not None

class Resolver(SettingsOverrideObject):

    _default_class = ResolverBase
    _override_setting = 'RESOLVER_CLASS'

resolver = Resolver()
resolve_path = resolver.resolve_path
resolve_domain = resolver.resolve_domain
resolve = resolver.resolve
<|endoftext|>


<commit_before>"""
Read the Docs URL Resolver.

Url Types:

- Subproject
- Subdomain
- CNAME
- Single Version
- Normal

All possible URL's::

    Subdomain or CNAME:

    /<lang>/<version>/<filename> # Default
    /<filename> # Single Version
    /projects/<subproject_slug>/<lang>/<version>/ # Subproject

    Normal Serving:

    /docs/<project_slug>/<lang>/<version>/<filename> # Default
    /docs/<project_slug>/<filename> # Single Version
    /docs/<project_slug>/projects/<subproject_slug>/<lang>/<version>/ # Subproject
"""

from django.conf import settings

def base_resolve(project_slug, filename, version_slug=None, language=None,
                 single_version=None, subproject_slug=None,  subdomain=None, cname=None):
    """ Resolve a with nothing smart, just filling in the blanks."""
    filename = filename.lstrip('/')

    if subdomain or cname:
        url = '/'
    else:
        url = '/docs/{project_slug}/'

    if subproject_slug:
        url += 'projects/{subproject_slug}/'

    if single_version:
        url += '{filename}'
    else:
        url += '{language}/{version_slug}/{filename}'

    return url.format(
        project_slug=project_slug, filename=filename,
        version_slug=version_slug, language=language,
        single_version=single_version, subproject_slug=subproject_slug,
    )

def resolve(project, filename='', version_slug=None, language=None,
            single_version=None, subdomain=None, cname=None):
    """ Resolve a URL with a subset of fields defined."""
    subdomain = getattr(settings, 'USE_SUBDOMAIN', False)
    relation = project.superprojects.first()
    cname = cname or project.domains.first()

    version_slug = version_slug or project.get_default_version()
    language = language or project.language

    if relation:
        project_slug = relation.parent.slug
        subproject_slug = relation.child.slug
    else:
        project_slug = project.slug
        subproject_slug = None

    if project.single_version or single_version:
        single_version = True
    else:
        single_version = False

    return base_resolve(project_slug=project_slug, filename=filename,
                        version_slug=version_slug, language=language,
                        single_version=single_version, subproject_slug=subproject_slug,
                        subdomain=subdomain, cname=cname)

def smart_resolve(project, filename=''):
    """ Resolve a URL with all fields automatically filled in from the project."""
    subdomain = getattr(settings, 'USE_SUBDOMAIN', False)
    relation = project.superprojects.first()
    cname = project.domains.first()

    version_slug = project.get_default_version()
    language = project.language

    if relation:
        project_slug = relation.parent.slug
        subproject_slug = relation.child.slug
    else:
        project_slug = project.slug
        subproject_slug = None

    if project.single_version:
        single_version = True
    else:
        single_version = False

    return base_resolve(project_slug=project_slug, filename=filename,
                        version_slug=version_slug, language=language,
                        single_version=single_version, subproject_slug=subproject_slug,
                        subdomain=subdomain, cname=cname)
<commit_msg>Missed a spot<commit_after>"""
Read the Docs URL Resolver.

Url Types:

- Subproject
- Subdomain
- CNAME
- Single Version
- Normal

All possible URL's::

    Subdomain or CNAME:

    /<lang>/<version>/<filename> # Default
    /<filename> # Single Version
    /projects/<subproject_slug>/<lang>/<version>/<filename> # Subproject
    /projects/<subproject_slug>/<filename> # Subproject Single Version

    Normal Serving:

    /docs/<project_slug>/<lang>/<version>/<filename> # Default
    /docs/<project_slug>/<filename> # Single Version
    /docs/<project_slug>/projects/<subproject_slug>/<lang>/<version>/<filename> # Subproject
    /docs/<project_slug>/projects/<subproject_slug>/<filename> # Subproject Single Version
"""

from django.conf import settings

def base_resolve(project_slug, filename, version_slug=None, language=None,
                 single_version=None, subproject_slug=None,  subdomain=None, cname=None):
    """ Resolve a with nothing smart, just filling in the blanks."""
    filename = filename.lstrip('/')

    if subdomain or cname:
        url = '/'
    else:
        url = '/docs/{project_slug}/'

    if subproject_slug:
        url += 'projects/{subproject_slug}/'

    if single_version:
        url += '{filename}'
    else:
        url += '{language}/{version_slug}/{filename}'

    return url.format(
        project_slug=project_slug, filename=filename,
        version_slug=version_slug, language=language,
        single_version=single_version, subproject_slug=subproject_slug,
    )

def resolve(project, filename='', version_slug=None, language=None,
            single_version=None, subdomain=None, cname=None):
    """ Resolve a URL with a subset of fields defined."""
    subdomain = getattr(settings, 'USE_SUBDOMAIN', False)
    relation = project.superprojects.first()
    cname = cname or project.domains.first()

    version_slug = version_slug or project.get_default_version()
    language = language or project.language

    if relation:
        project_slug = relation.parent.slug
        subproject_slug = relation.child.slug
    else:
        project_slug = project.slug
        subproject_slug = None

    if project.single_version or single_version:
        single_version = True
    else:
        single_version = False

    return base_resolve(project_slug=project_slug, filename=filename,
                        version_slug=version_slug, language=language,
                        single_version=single_version, subproject_slug=subproject_slug,
                        subdomain=subdomain, cname=cname)

def smart_resolve(project, filename=''):
    """ Resolve a URL with all fields automatically filled in from the project."""
    subdomain = getattr(settings, 'USE_SUBDOMAIN', False)
    relation = project.superprojects.first()
    cname = project.domains.first()

    version_slug = project.get_default_version()
    language = project.language

    if relation:
        project_slug = relation.parent.slug
        subproject_slug = relation.child.slug
    else:
        project_slug = project.slug
        subproject_slug = None

    if project.single_version:
        single_version = True
    else:
        single_version = False

    return base_resolve(project_slug=project_slug, filename=filename,
                        version_slug=version_slug, language=language,
                        single_version=single_version, subproject_slug=subproject_slug,
                        subdomain=subdomain, cname=cname)
<|endoftext|>