<commit_before>"""
    weasyprint.layout.columns
    -------------------------

    Layout for columns.

    :copyright: Copyright 2011-2019 Simon Sapin and contributors, see AUTHORS.
    :license: BSD, see LICENSE for details.

"""

from math import floor

from .absolute import absolute_layout
from .percentages import resolve_percentages

def columns_layout(context, box, max_position_y, skip_stack, containing_block,
                   page_is_empty, absolute_boxes, fixed_boxes,
                   adjoining_margins):
    """Lay out a multi-column ``box``."""
    # Avoid circular imports
    from .blocks import (
        block_box_layout, block_level_layout, block_level_width,
        collapse_margin)

    # Implementation of the multi-column pseudo-algorithm:
    # https://www.w3.org/TR/css3-multicol/#pseudo-algorithm
    width = None
    style = box.style
    original_max_position_y = max_position_y

    if box.style['position'] == 'relative':
        # New containing block, use a new absolute list
        absolute_boxes = []

    box = box.copy_with_children(box.children)

    height = box.style['height']
    if height != 'auto' and height.unit != '%':
        assert height.unit == 'px'
        known_height = True
        max_position_y = min(
            max_position_y, box.content_box_y() + height.value)
    else:
        known_height = False

    # TODO: the available width can be unknown if the containing block needs
    # the size of this block to know its own size.
    block_level_width(box, containing_block)
    available_width = box.width
    if style['column_width'] == 'auto' and style['column_count'] != 'auto':
        count = style['column_count']
        width = max(
            0, available_width - (count - 1) * style['column_gap']) / count
    elif (style['column_width'] != 'auto' and
            style['column_count'] == 'auto'):
        count = max(1, int(floor(
            (available_width + style['column_gap']) /
            (style['column_width'] + style['column_gap']))))
        width = (
            (available_width + style['column_gap']) / count -
            style['column_gap'])
    else:
        count = min(style['column_count'], int(floor(
            (available_width + style['column_gap']) /
            (style['column_width'] + style['column_gap']))))
        width = (
            (available_width + style['column_gap']) / count -
            style['column_gap'])

    def create_column_box(children):
        column_box = box.anonymous_from(box, children=children)
        resolve_percentages(column_box, containing_block)
        column_box.is_column = True
        column_box.width = width
        column_box.position_x = box.content_box_x()
        column_box.position_y = box.content_box_y()
        return column_box

    # Handle column-span property.
    # We want to get the following structure:
    # columns_and_blocks = [
    #     [column_child_1, column_child_2],
    #     spanning_block,
    #     …
    # ]
    columns_and_blocks = []
    column_children = []
    for child in box.children:
        if child.style['column_span'] == 'all':
            if column_children:
                columns_and_blocks.append(column_children)
            columns_and_blocks.append(child.copy())
            column_children = []
            continue
        column_children.append(child.copy())
    if column_children:
        columns_and_blocks.append(column_children)

    if not box.children:
        next_page = {'break': 'any', 'page': None}
        skip_stack = None

    # Balance.
    #
    # The current algorithm starts from the ideal height (the total height
    # divided by the number of columns). We then iterate until the last column
    # is not the highest one. At the end of each loop, we add the minimal
    # height needed to make one direct child at the top of one column go to the
    # end of the previous column.
    #
    # We rely on a real rendering for each loop, and with a stupid algorithm
    # like this it can last minutes…

    adjoining_margins = []
    current_position_y = box.content_box_y()
    new_children = []
    for column_children_or_block in columns_and_blocks:
        if not isinstance(column_children_or_block, list):
            # We get a spanning block, we display it like other blocks.
            block = column_children_or_block
            resolve_percentages(block, containing_block)
            block.position_x = box.content_box_x()
            block.position_y = current_position_y
            new_child, _, _, adjoining_margins, _ = block_level_layout(
                context, block, original_max_position_y, skip_stack,
                containing_block, page_is_empty, absolute_boxes, fixed_boxes,
                adjoining_margins)
            new_children.append(new_child)
            current_position_y = (
                new_child.border_height() + new_child.border_box_y())
            adjoining_margins.append(new_child.margin_bottom)
            continue

        excluded_shapes = context.excluded_shapes[:]

        # We have a list of children that we have to balance between columns.
        column_children = column_children_or_block

        # Find the total height of the content
        current_position_y += collapse_margin(adjoining_margins)
        adjoining_margins = []
        column_box = create_column_box(column_children)
        new_child, _, _, _, _ = block_box_layout(
            context, column_box, float('inf'), skip_stack, containing_block,
            page_is_empty, [], [], [])
        height = new_child.margin_height()
        if style['column_fill'] == 'balance':
            height /= count

        # Try to render columns until the content fits, increase the column
        # height step by step.
        column_skip_stack = skip_stack
        lost_space = float('inf')
        while True:
            # Remove extra excluded shapes introduced during previous loop
            new_excluded_shapes = (
                len(context.excluded_shapes) - len(excluded_shapes))
            for i in range(new_excluded_shapes):
                context.excluded_shapes.pop()

            for i in range(count):
                # Render the column
                new_box, resume_at, next_page, _, _ = block_box_layout(
                    context, column_box, box.content_box_y() + height,
                    column_skip_stack, containing_block, page_is_empty,
                    [], [], [])
                if new_box is None:
                    # We didn't render anything. Give up and use the max
                    # content height.
                    height *= count
                    continue
                column_skip_stack = resume_at

                in_flow_children = [
                    child for child in new_box.children
                    if child.is_in_normal_flow()]

                if in_flow_children:
                    # Get the empty space at the bottom of the column box
                    empty_space = height - (
                        in_flow_children[-1].position_y - box.content_box_y() +
                        in_flow_children[-1].margin_height())

                    # Get the minimum size needed to render the next box
                    next_box, _, _, _, _ = block_box_layout(
                        context, column_box, box.content_box_y(),
                        column_skip_stack, containing_block, True, [], [], [])
                    assert next_box.children[-1].is_in_normal_flow()
                    next_box_size = next_box.children[-1].margin_height()
                else:
                    empty_space = next_box_size = 0

                # Append the size needed to render the next box in this
                # column.
                #
                # The next box size may be smaller than the empty space, for
                # example when the next box can't be separated from its own
                # next box. In this case we don't try to find the real value
                # and let the workaround below fix this for us.
                #
                # We also want to avoid very small values that may have been
                # introduced by rounding errors. As the workaround below at
                # least adds 1 pixel for each loop, we can ignore lost spaces
                # lower than 1px.
                if next_box_size - empty_space > 1:
                    lost_space = min(lost_space, next_box_size - empty_space)

                # Stop if we already rendered the whole content
                if resume_at is None:
                    break

            if column_skip_stack is None:
                # We rendered the whole content, stop
                break
            else:
                if lost_space == float('inf'):
                    # We didn't find the extra size needed to render a child in
                    # the previous column, increase height by the minimal
                    # value.
                    height += 1
                else:
                    # Increase the columns heights and render them once again
                    height += lost_space
                column_skip_stack = skip_stack

        # TODO: check box.style['max']-height
        max_position_y = min(max_position_y, box.content_box_y() + height)

        # Replace the current box children with columns
        i = 0
        max_column_height = 0
        columns = []
        while True:
            if i == count - 1:
                max_position_y = original_max_position_y
            column_box = create_column_box(column_children)
            column_box.position_y = current_position_y
            if style['direction'] == 'rtl':
                column_box.position_x += (
                    box.width - (i + 1) * width - i * style['column_gap'])
            else:
                column_box.position_x += i * (width + style['column_gap'])
            new_child, column_skip_stack, column_next_page, _, _ = (
                block_box_layout(
                    context, column_box, max_position_y, skip_stack,
                    containing_block, page_is_empty, absolute_boxes,
                    fixed_boxes, None))
            if new_child is None:
                break
            next_page = column_next_page
            skip_stack = column_skip_stack
            columns.append(new_child)
            max_column_height = max(
                max_column_height, new_child.margin_height())
            if skip_stack is None:
                break
            i += 1
            if i == count and not known_height:
                # [If] a declaration that constrains the column height
                # (e.g., using height or max-height). In this case,
                # additional column boxes are created in the inline
                # direction.
                break

        current_position_y += max_column_height
        for column in columns:
            column.height = max_column_height
            new_children.append(column)

    if box.children and not new_children:
        # The box has children but none can be drawn, let's skip the whole box
        return None, (0, None), {'break': 'any', 'page': None}, [], False

    # Set the height of box and the columns
    box.children = new_children
    current_position_y += collapse_margin(adjoining_margins)
    if box.height == 'auto':
        box.height = current_position_y - box.position_y
        height_difference = 0
    else:
        height_difference = box.height - (current_position_y - box.position_y)
    if box.min_height != 'auto' and box.min_height > box.height:
        height_difference += box.min_height - box.height
        box.height = box.min_height
    for child in new_children[::-1]:
        if child.is_column:
            child.height += height_difference
        else:
            break

    if box.style['position'] == 'relative':
        # New containing block, resolve the layout of the absolute descendants
        for absolute_box in absolute_boxes:
            absolute_layout(context, absolute_box, box, fixed_boxes)

    return box, skip_stack, next_page, [], False
<commit_msg>Fix next in flow child detection during column layout<commit_after>"""
    weasyprint.layout.columns
    -------------------------

    Layout for columns.

    :copyright: Copyright 2011-2019 Simon Sapin and contributors, see AUTHORS.
    :license: BSD, see LICENSE for details.

"""

from math import floor

from .absolute import absolute_layout
from .percentages import resolve_percentages

def columns_layout(context, box, max_position_y, skip_stack, containing_block,
                   page_is_empty, absolute_boxes, fixed_boxes,
                   adjoining_margins):
    """Lay out a multi-column ``box``."""
    # Avoid circular imports
    from .blocks import (
        block_box_layout, block_level_layout, block_level_width,
        collapse_margin)

    # Implementation of the multi-column pseudo-algorithm:
    # https://www.w3.org/TR/css3-multicol/#pseudo-algorithm
    width = None
    style = box.style
    original_max_position_y = max_position_y

    if box.style['position'] == 'relative':
        # New containing block, use a new absolute list
        absolute_boxes = []

    box = box.copy_with_children(box.children)

    height = box.style['height']
    if height != 'auto' and height.unit != '%':
        assert height.unit == 'px'
        known_height = True
        max_position_y = min(
            max_position_y, box.content_box_y() + height.value)
    else:
        known_height = False

    # TODO: the available width can be unknown if the containing block needs
    # the size of this block to know its own size.
    block_level_width(box, containing_block)
    available_width = box.width
    if style['column_width'] == 'auto' and style['column_count'] != 'auto':
        count = style['column_count']
        width = max(
            0, available_width - (count - 1) * style['column_gap']) / count
    elif (style['column_width'] != 'auto' and
            style['column_count'] == 'auto'):
        count = max(1, int(floor(
            (available_width + style['column_gap']) /
            (style['column_width'] + style['column_gap']))))
        width = (
            (available_width + style['column_gap']) / count -
            style['column_gap'])
    else:
        count = min(style['column_count'], int(floor(
            (available_width + style['column_gap']) /
            (style['column_width'] + style['column_gap']))))
        width = (
            (available_width + style['column_gap']) / count -
            style['column_gap'])

    def create_column_box(children):
        column_box = box.anonymous_from(box, children=children)
        resolve_percentages(column_box, containing_block)
        column_box.is_column = True
        column_box.width = width
        column_box.position_x = box.content_box_x()
        column_box.position_y = box.content_box_y()
        return column_box

    # Handle column-span property.
    # We want to get the following structure:
    # columns_and_blocks = [
    #     [column_child_1, column_child_2],
    #     spanning_block,
    #     …
    # ]
    columns_and_blocks = []
    column_children = []
    for child in box.children:
        if child.style['column_span'] == 'all':
            if column_children:
                columns_and_blocks.append(column_children)
            columns_and_blocks.append(child.copy())
            column_children = []
            continue
        column_children.append(child.copy())
    if column_children:
        columns_and_blocks.append(column_children)

    if not box.children:
        next_page = {'break': 'any', 'page': None}
        skip_stack = None

    # Balance.
    #
    # The current algorithm starts from the ideal height (the total height
    # divided by the number of columns). We then iterate until the last column
    # is not the highest one. At the end of each loop, we add the minimal
    # height needed to make one direct child at the top of one column go to the
    # end of the previous column.
    #
    # We rely on a real rendering for each loop, and with a stupid algorithm
    # like this it can last minutes…

    adjoining_margins = []
    current_position_y = box.content_box_y()
    new_children = []
    for column_children_or_block in columns_and_blocks:
        if not isinstance(column_children_or_block, list):
            # We get a spanning block, we display it like other blocks.
            block = column_children_or_block
            resolve_percentages(block, containing_block)
            block.position_x = box.content_box_x()
            block.position_y = current_position_y
            new_child, _, _, adjoining_margins, _ = block_level_layout(
                context, block, original_max_position_y, skip_stack,
                containing_block, page_is_empty, absolute_boxes, fixed_boxes,
                adjoining_margins)
            new_children.append(new_child)
            current_position_y = (
                new_child.border_height() + new_child.border_box_y())
            adjoining_margins.append(new_child.margin_bottom)
            continue

        excluded_shapes = context.excluded_shapes[:]

        # We have a list of children that we have to balance between columns.
        column_children = column_children_or_block

        # Find the total height of the content
        current_position_y += collapse_margin(adjoining_margins)
        adjoining_margins = []
        column_box = create_column_box(column_children)
        new_child, _, _, _, _ = block_box_layout(
            context, column_box, float('inf'), skip_stack, containing_block,
            page_is_empty, [], [], [])
        height = new_child.margin_height()
        if style['column_fill'] == 'balance':
            height /= count

        # Try to render columns until the content fits, increase the column
        # height step by step.
        column_skip_stack = skip_stack
        lost_space = float('inf')
        while True:
            # Remove extra excluded shapes introduced during previous loop
            new_excluded_shapes = (
                len(context.excluded_shapes) - len(excluded_shapes))
            for i in range(new_excluded_shapes):
                context.excluded_shapes.pop()

            for i in range(count):
                # Render the column
                new_box, resume_at, next_page, _, _ = block_box_layout(
                    context, column_box, box.content_box_y() + height,
                    column_skip_stack, containing_block, page_is_empty,
                    [], [], [])
                if new_box is None:
                    # We didn't render anything. Give up and use the max
                    # content height.
                    height *= count
                    continue
                column_skip_stack = resume_at

                in_flow_children = [
                    child for child in new_box.children
                    if child.is_in_normal_flow()]

                if in_flow_children:
                    # Get the empty space at the bottom of the column box
                    empty_space = height - (
                        in_flow_children[-1].position_y - box.content_box_y() +
                        in_flow_children[-1].margin_height())

                    # Get the minimum size needed to render the next box
                    next_box, _, _, _, _ = block_box_layout(
                        context, column_box, box.content_box_y(),
                        column_skip_stack, containing_block, True, [], [], [])
                    for child in next_box.children:
                        if child.is_in_normal_flow():
                            next_box_size = child.margin_height()
                            break
                else:
                    empty_space = next_box_size = 0

                # Append the size needed to render the next box in this
                # column.
                #
                # The next box size may be smaller than the empty space, for
                # example when the next box can't be separated from its own
                # next box. In this case we don't try to find the real value
                # and let the workaround below fix this for us.
                #
                # We also want to avoid very small values that may have been
                # introduced by rounding errors. As the workaround below at
                # least adds 1 pixel for each loop, we can ignore lost spaces
                # lower than 1px.
                if next_box_size - empty_space > 1:
                    lost_space = min(lost_space, next_box_size - empty_space)

                # Stop if we already rendered the whole content
                if resume_at is None:
                    break

            if column_skip_stack is None:
                # We rendered the whole content, stop
                break
            else:
                if lost_space == float('inf'):
                    # We didn't find the extra size needed to render a child in
                    # the previous column, increase height by the minimal
                    # value.
                    height += 1
                else:
                    # Increase the columns heights and render them once again
                    height += lost_space
                column_skip_stack = skip_stack

        # TODO: check box.style['max']-height
        max_position_y = min(max_position_y, box.content_box_y() + height)

        # Replace the current box children with columns
        i = 0
        max_column_height = 0
        columns = []
        while True:
            if i == count - 1:
                max_position_y = original_max_position_y
            column_box = create_column_box(column_children)
            column_box.position_y = current_position_y
            if style['direction'] == 'rtl':
                column_box.position_x += (
                    box.width - (i + 1) * width - i * style['column_gap'])
            else:
                column_box.position_x += i * (width + style['column_gap'])
            new_child, column_skip_stack, column_next_page, _, _ = (
                block_box_layout(
                    context, column_box, max_position_y, skip_stack,
                    containing_block, page_is_empty, absolute_boxes,
                    fixed_boxes, None))
            if new_child is None:
                break
            next_page = column_next_page
            skip_stack = column_skip_stack
            columns.append(new_child)
            max_column_height = max(
                max_column_height, new_child.margin_height())
            if skip_stack is None:
                break
            i += 1
            if i == count and not known_height:
                # [If] a declaration that constrains the column height
                # (e.g., using height or max-height). In this case,
                # additional column boxes are created in the inline
                # direction.
                break

        current_position_y += max_column_height
        for column in columns:
            column.height = max_column_height
            new_children.append(column)

    if box.children and not new_children:
        # The box has children but none can be drawn, let's skip the whole box
        return None, (0, None), {'break': 'any', 'page': None}, [], False

    # Set the height of box and the columns
    box.children = new_children
    current_position_y += collapse_margin(adjoining_margins)
    if box.height == 'auto':
        box.height = current_position_y - box.position_y
        height_difference = 0
    else:
        height_difference = box.height - (current_position_y - box.position_y)
    if box.min_height != 'auto' and box.min_height > box.height:
        height_difference += box.min_height - box.height
        box.height = box.min_height
    for child in new_children[::-1]:
        if child.is_column:
            child.height += height_difference
        else:
            break

    if box.style['position'] == 'relative':
        # New containing block, resolve the layout of the absolute descendants
        for absolute_box in absolute_boxes:
            absolute_layout(context, absolute_box, box, fixed_boxes)

    return box, skip_stack, next_page, [], False
<|endoftext|>


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

"""Implementing angular momentum generators."""
from openfermion.ops import FermionOperator, number_operator

def up_index(index):
    """Function to return up-orbital index given a spatial orbital index.

    Args:
        index (Int): spatial orbital index
    """
    return 2 * index

def down_index(index):
    """Function to return down-orbital index given a spatial orbital index.

    Args:
        index (Int): spatial orbital index
    """
    return 2 * index + 1

def sz_operator(n_spatial_orbitals, up_map=up_index, down_map=down_index):
    """Return the sz operator.

    SZ = 0.5 * \sum_{i = 1}^{n} n_{i, \alpha} - n_{i, \beta}

    Args:
        n_spatial_orbitals: number of spatial orbitals (n_qubits // 2).
        up_map: function mapping a spatial index to a spin-orbital index.
                Default is the canonical spin-up corresponds to even
                spin-orbitals and spin-down corresponds to odd spin-orbitals
        down_map: function mapping spatial index to spin-orbital index.
                  Default is canonical spin-up corresponds to even
                  spin-orbitals and spin-down corresponds to odd
                  spin-orbitals.

    Returns:
        operator (FermionOperator): corresponding to the sz operator over
        n_spatial_orbitals.

    Warnings:
        Default assumes a number occupation vector representation with even
        spin-less fermions corresponding to spin-up (alpha) and odd spin-less
        fermions corresponding to spin-down (beta).
    """
    if not isinstance(n_spatial_orbitals, int):
        raise TypeError("n_orbitals must be specified as an integer")

    operator = FermionOperator()
    n_spinless_orbitals = 2 * n_spatial_orbitals
    for ni in range(n_spatial_orbitals):
        operator += number_operator(n_spinless_orbitals, up_map(ni), 0.5) + \
                    number_operator(n_spinless_orbitals, down_map(ni), -0.5)

    return operator

def s_plus_operator(n_spatial_orbitals, up_map=up_index, down_map=down_index):
    """Return the s+ operator.

    S+ = \sum_{i=1}^{n} a_{i, \alpha}^{\dagger}a_{i, \beta}

     Args:
        n_spatial_orbitals: number of spatial orbitals (n_qubits + 1 // 2).
        up_map: function mapping a spatial index to a spin-orbital index.
                Default is the canonical spin-up corresponds to even
                spin-orbitals and spin-down corresponds to odd spin-orbitals
        down_map: function mapping spatial index to spin-orbital index.
                  Default is canonical spin-up corresponds to even
                  spin-orbitals and spin-down corresponds to odd
                  spin-orbitals.

    Returns:
        operator (FermionOperator): corresponding to the s+ operator over
        n_spatial_orbitals.

    Warnings:
        Default assumes a number occupation vector representation with even
        spin-less fermions corresponding to spin-up (alpha) and odd spin-less
        fermions corresponding to spin-down (beta).
    """
    if not isinstance(n_spatial_orbitals, int):
        raise TypeError("n_orbitals must be specified as an integer")

    operator = FermionOperator()
    for ni in range(n_spatial_orbitals):
        operator += FermionOperator(((up_map(ni), 1), (down_map(ni), 0)))

    return operator

def s_minus_operator(n_spatial_orbitals, up_map=up_index, down_map=down_index):
    """Return the s+ operator.

    S- = \sum_{i=1}^{n} a_{i, \beta}^{\dagger}a_{i, \alpha}

     Args:
        n_spatial_orbitals: number of spatial orbitals (n_qubits + 1 // 2).
        up_map: function mapping a spatial index to a spin-orbital index.
                Default is the canonical spin-up corresponds to even
                spin-orbitals and spin-down corresponds to odd spin-orbitals
        down_map: function mapping spatial index to spin-orbital index.
                  Default is canonical spin-up corresponds to even
                  spin-orbitals and spin-down corresponds to odd
                  spin-orbitals.

    Returns:
        operator (FermionOperator): corresponding to the s+ operator over
        n_spatial_orbitals.

    Warnings:
        Default assumes a number occupation vector representation with even
        spin-less fermions corresponding to spin-up (alpha) and odd spin-less
        fermions corresponding to spin-down (beta).
    """
    if not isinstance(n_spatial_orbitals, int):
        raise TypeError("n_orbitals must be specified as an integer")

    operator = FermionOperator()
    for ni in range(n_spatial_orbitals):
        operator += FermionOperator(((down_map(ni), 1), (up_map(ni), 0)))

    return operator

def s_squared_operator(n_spatial_orbitals):
    """Return the s^{2} operator.

    S^{2} = S- S+ + Sz( SZ + 1)

     Args:
        n_spatial_orbitals: number of spatial orbitals (n_qubits + 1 // 2).

    Returns:
        operator (FermionOperator): corresponding to the s+ operator over
        n_spatial_orbitals.

    Warnings:
        assumes a number occupation vector representation with even spin-less
        fermions corresponding to spin-up (alpha) and odd spin-less fermions
        corresponding to spin-down (beta).

    """
    if not isinstance(n_spatial_orbitals, int):
        raise TypeError("n_orbitals must be specified as an integer")

    fermion_identity = FermionOperator(())
    operator = (s_minus_operator(n_spatial_orbitals) *
                s_plus_operator(n_spatial_orbitals))
    operator += (sz_operator(n_spatial_orbitals) *
                 (sz_operator(n_spatial_orbitals) + fermion_identity))
    return operator<commit_msg>Minor string adjustment to docs in spin-gens (#189)<commit_after>#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

"""Implementing angular momentum generators."""
from openfermion.ops import FermionOperator, number_operator

def up_index(index):
    """Function to return up-orbital index given a spatial orbital index.

    Args:
        index (Int): spatial orbital index
    """
    return 2 * index

def down_index(index):
    """Function to return down-orbital index given a spatial orbital index.

    Args:
        index (Int): spatial orbital index
    """
    return 2 * index + 1

def sz_operator(n_spatial_orbitals, up_map=up_index, down_map=down_index):
    """Return the sz operator.

    .. math::
        \\begin{align}
        S^{z} = \\frac{1}{2}\sum_{i = 1}^{n}(n_{i, \\alpha} - n_{i, \\beta})
        \\end{align}

    Args:
        n_spatial_orbitals: number of spatial orbitals (n_qubits // 2).
        up_map: function mapping a spatial index to a spin-orbital index.
                Default is the canonical spin-up corresponds to even
                spin-orbitals and spin-down corresponds to odd spin-orbitals
        down_map: function mapping spatial index to spin-orbital index.
                  Default is canonical spin-up corresponds to even
                  spin-orbitals and spin-down corresponds to odd
                  spin-orbitals.

    Returns:
        operator (FermionOperator): corresponding to the sz operator over
        n_spatial_orbitals.

    Warnings:
        Default assumes a number occupation vector representation with even
        spin-less fermions corresponding to spin-up (alpha) and odd spin-less
        fermions corresponding to spin-down (beta).
    """
    if not isinstance(n_spatial_orbitals, int):
        raise TypeError("n_orbitals must be specified as an integer")

    operator = FermionOperator()
    n_spinless_orbitals = 2 * n_spatial_orbitals
    for ni in range(n_spatial_orbitals):
        operator += number_operator(n_spinless_orbitals, up_map(ni), 0.5) + \
                    number_operator(n_spinless_orbitals, down_map(ni), -0.5)

    return operator

def s_plus_operator(n_spatial_orbitals, up_map=up_index, down_map=down_index):
    """Return the s+ operator.

    .. math::
        \\begin{align}
        S^{+} = \sum_{i=1}^{n} a_{i, \\alpha}^{\dagger}a_{i, \\beta}
        \\end{align}

    Args:
        n_spatial_orbitals: number of spatial orbitals (n_qubits + 1 // 2).
        up_map: function mapping a spatial index to a spin-orbital index.
                Default is the canonical spin-up corresponds to even
                spin-orbitals and spin-down corresponds to odd spin-orbitals
        down_map: function mapping spatial index to spin-orbital index.
                  Default is canonical spin-up corresponds to even
                  spin-orbitals and spin-down corresponds to odd
                  spin-orbitals.

    Returns:
        operator (FermionOperator): corresponding to the s+ operator over
        n_spatial_orbitals.

    Warnings:
        Default assumes a number occupation vector representation with even
        spin-less fermions corresponding to spin-up (alpha) and odd spin-less
        fermions corresponding to spin-down (beta).
    """
    if not isinstance(n_spatial_orbitals, int):
        raise TypeError("n_orbitals must be specified as an integer")

    operator = FermionOperator()
    for ni in range(n_spatial_orbitals):
        operator += FermionOperator(((up_map(ni), 1), (down_map(ni), 0)))

    return operator

def s_minus_operator(n_spatial_orbitals, up_map=up_index, down_map=down_index):
    """Return the s+ operator.

    .. math::
        \\begin{align}
        S^{-} = \sum_{i=1}^{n} a_{i, \\beta}^{\dagger}a_{i, \\alpha}
        \\end{align}

    Args:
        n_spatial_orbitals: number of spatial orbitals (n_qubits + 1 // 2).
        up_map: function mapping a spatial index to a spin-orbital index.
                Default is the canonical spin-up corresponds to even
                spin-orbitals and spin-down corresponds to odd spin-orbitals
        down_map: function mapping spatial index to spin-orbital index.
                  Default is canonical spin-up corresponds to even
                  spin-orbitals and spin-down corresponds to odd
                  spin-orbitals.

    Returns:
        operator (FermionOperator): corresponding to the s- operator over
        n_spatial_orbitals.

    Warnings:
        Default assumes a number occupation vector representation with even
        spin-less fermions corresponding to spin-up (alpha) and odd spin-less
        fermions corresponding to spin-down (beta).
    """
    if not isinstance(n_spatial_orbitals, int):
        raise TypeError("n_orbitals must be specified as an integer")

    operator = FermionOperator()
    for ni in range(n_spatial_orbitals):
        operator += FermionOperator(((down_map(ni), 1), (up_map(ni), 0)))

    return operator

def s_squared_operator(n_spatial_orbitals):
    """Return the s^{2} operator.

    .. math::
        \\begin{align}
        S^{2} = S^{-} S^{+} + S^{z}( S^{z} + 1)
        \\end{align}

    Args:
        n_spatial_orbitals: number of spatial orbitals (n_qubits + 1 // 2).

    Returns:
        operator (FermionOperator): corresponding to the s+ operator over
        n_spatial_orbitals.

    Warnings:
        assumes a number occupation vector representation with even spin-less
        fermions corresponding to spin-up (alpha) and odd spin-less fermions
        corresponding to spin-down (beta).

    """
    if not isinstance(n_spatial_orbitals, int):
        raise TypeError("n_orbitals must be specified as an integer")

    fermion_identity = FermionOperator(())
    operator = (s_minus_operator(n_spatial_orbitals) *
                s_plus_operator(n_spatial_orbitals))
    operator += (sz_operator(n_spatial_orbitals) *
                 (sz_operator(n_spatial_orbitals) + fermion_identity))
    return operator<|endoftext|>


<commit_before># -*- coding: iso-8859-1 -*-
"""
:Author: Martin Wendt
:Copyright: Lesser GNU Public License, see LICENSE file attached with package

Sample implementation of a DAV provider that pulishes a mercurial reposity.

See:
    http://mercurial.berkwood.com/binaries/mercurial-1.4.win32-py2.6.exe

Sample layout::

    /<share>/
        edit/
            server/
                ext_server.py
            README.txt
        public/
        archive/
            rev1/
            rev2/
        tags/
            release1/

"""
from pprint import pprint
from wsgidav.dav_error import DAVError, HTTP_FORBIDDEN
import time
import sys
import mimetypes
import stat

import os
try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO #@UnusedImport
from wsgidav.dav_provider import DAVProvider, DAVResource
from wsgidav import util

try:
    import mercurial.ui
#    import mercurial.error
    from mercurial.__version__ import version as hgversion
    from mercurial import commands, hg
    #from mercurial import util as hgutil
except ImportError:
    print >>sys.stderr, "Could not import Mercurial API. Try 'easy_install -U mercurial'."
    raise

__docformat__ = "reStructuredText en"

_logger = util.getModuleLogger(__name__)

BUFFER_SIZE = 8192

#===============================================================================
# VirtualCollection
#===============================================================================
class VirtualCollection(DAVResource):
    """Collection resource, that contains a list of member names."""
    def __init__(self, provider, path, environ, memberNames):
        DAVResource.__init__(self, provider, path, True, environ)
        self._memberNames = memberNames
    def displayType(self):
        return "Collection"
    def getMemberNames(self):
        return self._memberNames

#===============================================================================
# HgResource
#===============================================================================
class HgResource(DAVResource):
    """Abstract base class for all resources."""
    def __init__(self, provider, path, isCollection, environ):
        super(HgResource, self).__init__(provider, path, isCollection, environ)
        self._filePath = os.path.join(self.provider.repoPath, *path.split("/"))
#        print path, self._filePath
        if isCollection:
#            self.fctx = self.provider.repo.filectx(repoFilePath, fileid="tip")
            self.fctx = None
        else:
            # Change Context for the working directory:
#            wdctx = self.provider.repo[None]
            # Change Context for the TIP:
            wdctx = self.provider.repo["tip"]
            # HG expects path without leading '/'
            repoFilePath = self.path.lstrip("/")
            self.fctx = wdctx[repoFilePath]
#        try:
#            # Change Context for the working directory:
##            wdctx = self.provider.repo[None]
#            # Change Context for the TIP:
#            wdctx = self.provider.repo["tip"]
#            # HG expects path without leading '/'
#            repoFilePath = self.path.lstrip("/")
#            # store the File Context
#            if isCollection:
#                self.fctx = self.provider.repo.filectx(repoFilePath, fileid="tip")
#            else:
#                self.fctx = wdctx[repoFilePath]
#        except mercurial.error.RepoError:
#            self.fctx = None #self.provider.repo.filectx(path, fileid="tip")
#        pprint(self.fctx)

    def getContentLength(self):
        if self.isCollection:
            return None
        return self.fctx.size()
    def getContentType(self):
        (mimetype, _mimeencoding) = mimetypes.guess_type(self.path)
        if not mimetype:
            return "application/octet-stream"
        return mimetype
    def getCreationDate(self):
        statresults = os.stat(self._filePath)
        return statresults[stat.ST_CTIME]
    def getDisplayName(self):
        if self.isCollection:
            return self.name
#        return str(self.fctx)
#        return "%s@%s,%s,%s" % (self.name, self.fctx.rev(), self.fctx.user(), self.fctx.date())
        return "%s@%s" % (self.name, self.fctx.filerev())
    def getEtag(self):
        return util.getETag(self._filePath)
    def getLastModified(self):
        if self.isCollection:
            return None
        # (secs, tz-ofs)
        return self.fctx.date()[0]
    def supportRanges(self):
        return False
    def displayType(self):
        if self.isCollection:
            return "Directory"
        return "File"

    def getMemberNames(self):
        assert self.isCollection
        cache = self.environ.get("wsgidav.hg.cache")
        dirinfos = cache["dirinfos"]
        return dirinfos[self.path][0] + dirinfos[self.path][1]
#        return self.provider._listMembers(self.path)

    def getPropertyNames(self, isAllProp):
        """Return list of supported property names in Clark Notation.

        See DAVResource.getPropertyNames()
        """
        # Let base class implementation add supported live and dead properties
        propNameList = super(HgResource, self).getPropertyNames(isAllProp)
        # Add custom live properties (report on 'allprop' and 'propnames')
        propNameList.extend(["{hg:}status",
                             "{hg:}branch",
#                             "{hg:}changectx",
                             "{hg:}date",
                             "{hg:}description",
#                             "{hg:}filenode",
                             "{hg:}filerev",
#                             "{hg:}node",
                             "{hg:}rev",
                             "{hg:}user",
                             ])
        return propNameList

    def getPropertyValue(self, propname):
        """Return the value of a property.

        See getPropertyValue()
        """
        # Supported custom live properties
        cache = self.environ.get("wsgidav.hg.cache")
        if propname == "{hg:}status":
            return cache["filestats"][self.path]
        elif propname == "{hg:}branch":
            return self.fctx.branch()
#        elif propname == "{hg:}changectx":
#            return self.fctx.changectx()
        elif propname == "{hg:}date":
            # (secs, tz-ofs)
            return str(self.fctx.date()[0])
        elif propname == "{hg:}description":
            return self.fctx.description()
#        elif propname == "{hg:}filenode":
#            return self.fctx.filenode()
        elif propname == "{hg:}filerev":
            return str(self.fctx.filerev())
#        elif propname == "{hg:}node":
#            return str(self.fctx.node())
        elif propname == "{hg:}rev":
            return str(self.fctx.rev())
        elif propname == "{hg:}user":
            return str(self.fctx.user())

        # Let base class implementation report live and dead properties
        return super(HgResource, self).getPropertyValue(propname)

    def setPropertyValue(self, propname, value, dryRun=False):
        """Set or remove property value.

        See DAVResource.setPropertyValue()
        """
        raise DAVError(HTTP_FORBIDDEN)

    def getContent(self):
        """Open content as a stream for reading.

        See DAVResource.getContent()
        """
        assert not self.isCollection
#        mime = self.getContentType()
#        if mime.startswith("text"):
#            return file(self._filePath, "r", BUFFER_SIZE)
#        return file(self._filePath, "rb", BUFFER_SIZE)
        d = self.fctx.data()
        return StringIO(d)

#===============================================================================
# HgResourceProvider
#===============================================================================

class HgResourceProvider(DAVProvider):
    """
    DAV provider that serves a VirtualResource derived structure.
    """
    def __init__(self, repoPath):
        super(HgResourceProvider, self).__init__()
        self.repoPath = repoPath
        print "Mercurial version %s" % hgversion
        self.ui = mercurial.ui.ui()
        self.repo = hg.repository(self.ui, repoPath)
        self.ui.status("Connected to repository %s\n" % self.repo.root)
        self.repoPath = self.repo.root

        # Verify integrity of the repository
        util.status("Verify repository '%s' tree..." % self.repo.root)
        commands.verify(self.ui, self.repo)

#        self.ui.status("Changelog: %s\n" % self.repo.changelog)
        print "Status:"
        pprint(self.repo.status())
#        self.repo.ui.status("Status: %s\n" % ", ".join(self.repo.status()))
#        self.repo.ui.status("preferred editor: %s\n" % self.repo.ui.editor())
        self.repo.ui.status("the default username to be used in commits: %s\n" % self.repo.ui.username())
#        self.repo.ui.status("a short form of user name USER %s\n" % self.repo.ui.shortuser(user))
        self.ui.status("Expandpath: %s\n" % self.repo.ui.expandpath(repoPath))

        print "Working directory state summary:"
        self.ui.pushbuffer()
        commands.summary(self.ui, self.repo, remote=False)
        res = self.ui.popbuffer().strip()
        reslines = [ tuple(line.split(":", 1)) for line in res.split("\n")]
        pprint(reslines)

        print "Repository state summary:"
        self.ui.pushbuffer()
        commands.identify(self.ui, self.repo,
                          num=True, id=True, branch=True, tags=True)
        res = self.ui.popbuffer().strip()
        reslines = [ tuple(line.split(":", 1)) for line in res.split("\n")]
        pprint(reslines)

#        print "Status:"
#        # files: sorted list of all file paths under source control
#        # statusdict: {filepath: status, ...}
#        # folders: {folderpath: (collectionlist, filelist), ...}
#        self.ui.pushbuffer()
#        commands.status(self.ui, self.repo, all=True)
#        res = self.ui.popbuffer()
#        self.files = []
#        self.statusdict = {}
#        for line in res.split("\n"):
#            if line.strip():
#                stat, file = line.split(" ", 1)
#                file = "/" + file.replace("\\", "/")
#                self.files.append(file)
#                self.statusdict[file] = stat
##                for parent in file.strip("/").split("/"):
#
#        self.files.sort()
#        pprint(self.statusdict)
#        pprint(self.files)

#        print "Log:"
#        self.ui.pushbuffer()
#        commands.log(self.ui, self.repo, limit=3)
#        res = self.ui.popbuffer().strip()
#        statuslist = [ tuple(line.split(":", 1)) for line in res.split("\n")]
#        pprint(statuslist)

        # 'cat': get file content for a given revision:
#        self.ui.pushbuffer()
#        commands.cat(self.ui, self.repo, filename)
#        res = self.ui.popbuffer()
        # heads
        # manifest: list all versioned files
        # parents
        # paths
        # showconfig

        print "Tip:"
        self.ui.pushbuffer()
        commands.tip(self.ui, self.repo)
        res = self.ui.popbuffer().strip()
        statuslist = [ tuple(line.split(":", 1)) for line in res.split("\n")]
        pprint(statuslist)
#        raise
#        self.repo.ui.status(self.repo.)

    def _readTree(self, rev=None):
        """Return a dictionary containing all files under source control.

        files:
            sorted list of all file paths under source control
        filestats:
            {filepath: status, ...}
        dirinfos:
            {folderpath: (collectionlist, filelist), ...}

        ::

            {'dirinfos': {'/': (['wsgidav',
                                 'tools',
                                 '.settings',
                                 'WsgiDAV.egg-info',
                                 'tests'],
                                ['index.rst',
                                 'wsgidav MAKE_DAILY_BUILD.launch',
                                 'wsgidav run_server.py DEBUG.launch',
                                 'wsgidav run_server.py RELEASE.launch',
                                 'wsgidav test_all.py.launch',
                                 'wsgidav-paste.conf',
                                 ...
                                 'setup.py']),
                          '/wsgidav': (['addons', 'samples', 'server', 'interfaces'],
                                       ['__init__.pyc',
                                        'dav_error.pyc',
                                        'dav_provider.pyc',
                                        ...
                                        'wsgidav_app.py']),
                            },
             'files': ['/.hgignore',
                       '/ADDONS.txt',
                       '/wsgidav/addons/mysql_dav_provider.py',
                       ...
                       ],
             'filestats': {'/.hgignore': 'C',
                           '/README.txt': 'C',
                           '/WsgiDAV.egg-info/PKG-INFO': 'I',
                           }
                           }
        """
        start_time = time.time()
        self.ui.pushbuffer()
#        commands.status(self.ui, self.repo, all=True)
        commands.status(self.ui, self.repo, modified=True, added=True, clean=True)
        res = self.ui.popbuffer()
        files = []
        dirinfos = {}
        filestats = {}
        for line in res.split("\n"):
            if len(line) < 3:
                continue
            stat, file = line.split(" ", 1)
            file = file.replace("\\", "/")
            # add all parent directories to 'dirinfos'
            parents = file.split("/")
            if len(parents) >= 1:
                p1 = "/"
                for i in range(0, len(parents)-1):
                    p2 = parents[i]
                    dir = dirinfos.setdefault(p1, ([], []))
                    if not p2 in dir[0]:
                        dir[0].append(p2)
                    p1 = "%s/%s" % (p1.rstrip("/"), p2)
                dirinfos.setdefault(p1, ([], []))[1].append(parents[-1])
            file = "/" + file
            files.append(file)
            filestats[file] = stat
        files.sort()
        util.status("_readTree() took %s" % (time.time() - start_time))
#        pprint(cache)
        return {"files": files,
                "dirinfos": dirinfos,
                "filestats": filestats,
                }

#    def _identify(self, path, rev=None):
#        self.ui.pushbuffer()
#        commands.identify(self.ui, self.repo,
#                          num=True, id=True, branch=True, tags=True)
#        res = self.ui.popbuffer().strip().split(" ")
#        hasChanges = res[0].endswith("+")
#        dict = {"id": res[0].strip("+"),
#                "num": res[1].strip("+"),
#                "branch": res[2],
#                "tag": res[3],
#                "hasChanges": hasChanges,
#                "path": path,
#                }
#        pprint(dict)
#        return dict

    def _listMembers(self, path, rev=None):
        """Return a list of all non-collection members"""
        # Pattern for direct members:
        glob = "glob:" + os.path.join(path, "*").lstrip("/")
#        print glob
        self.ui.pushbuffer()
        commands.status(self.ui, self.repo,
                        glob,
                        all=True)
        lines = self.ui.popbuffer().strip().split("\n")
        pprint(lines)
        return dict

    def getResourceInst(self, path, environ):
        """Return _VirtualResource object for path.

        path is expected to be
            categoryType/category/name/artifact
        for example:
            'by_tag/cool/My doc 2/info.html'

        See DAVProvider.getResourceInst()
        """
        self._count_getResourceInst += 1

        cmd, rest = util.popPath(path)
        if cmd == "":
            return VirtualCollection(self, path, environ, ["tip",
                                                           "tags",
                                                           "archive"])
        elif cmd == "tip":
            path = rest
        elif cmd == "tags":
            if  rest == "/":
                return VirtualCollection(self, path, environ, ["a", "b", "c"])
            else:
                raise
        elif cmd == "archive":
            path = rest
        else:
            return None

        # read mercurial repo into request cache
        if environ.get("wsgidav.hg.cache") is None:
            cache = environ["wsgidav.hg.cache"] = self._readTree()
        else:
            cache = environ["wsgidav.hg.cache"]

        p = "/" + path.strip("/")
        if p in cache["filestats"]:
            # It is a version controlled file
            return HgResource(self, path, False, environ)

        if p in cache["dirinfos"]:
            # It is an existing folder
            return HgResource(self, p, True, environ)
        return None

<commit_msg>Adding 'edit' and 'public' virtual folders to hg-provider<commit_after># -*- coding: iso-8859-1 -*-
"""
:Author: Martin Wendt
:Copyright: Lesser GNU Public License, see LICENSE file attached with package

Sample implementation of a DAV provider that pulishes a mercurial reposity.

See:
    http://mercurial.berkwood.com/binaries/mercurial-1.4.win32-py2.6.exe

Sample layout::

    /<share>/
        edit/
            server/
                ext_server.py
            README.txt
        public/
        archive/
            rev1/
            rev2/
        tags/
            release1/

"""
from pprint import pprint
from wsgidav.dav_error import DAVError, HTTP_FORBIDDEN
try:
    from hashlib import md5
except ImportError:
    from md5 import md5
import time
import sys
import mimetypes
import stat

import os
try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO #@UnusedImport
from wsgidav.dav_provider import DAVProvider, DAVResource
from wsgidav import util

try:
    import mercurial.ui
#    import mercurial.error
    from mercurial.__version__ import version as hgversion
    from mercurial import commands, hg
    #from mercurial import util as hgutil
except ImportError:
    print >>sys.stderr, "Could not import Mercurial API. Try 'easy_install -U mercurial'."
    raise

__docformat__ = "reStructuredText en"

_logger = util.getModuleLogger(__name__)

BUFFER_SIZE = 8192

#===============================================================================
# VirtualCollection
#===============================================================================
class VirtualCollection(DAVResource):
    """Collection resource, that contains a list of member names."""
    def __init__(self, provider, path, environ, memberNames):
        DAVResource.__init__(self, provider, path, True, environ)
        self._memberNames = memberNames
    def displayType(self):
        return "Collection"
    def getMemberNames(self):
        return self._memberNames

#===============================================================================
# HgResource
#===============================================================================
class HgResource(DAVResource):
    """Abstract base class for all resources."""
    def __init__(self, provider, path, isCollection, environ, rev, repoPath):
        super(HgResource, self).__init__(provider, path, isCollection, environ)
        self.rev = rev
        self.repoPath = repoPath

        if isCollection:
#            self.fctx = self.provider.repo.filectx(repoFilePath, fileid="tip")
            self.fctx = None
        else:
            # Change Context for the working directory:
            # rev=None: current working dir
            # rev="tip": TIP
            wdctx = self.provider.repo[self.rev]
            # HG expects path without leading '/'
            p = self.repoPath.lstrip("/")
            self.fctx = wdctx[p]
        util.write("HgResourcdec: path=%s, rev=%s, repoPath=%s, fctx=%s",
                   (self.path, self.rev, self.repoPath, self.fctx))

    def getContentLength(self):
        if self.isCollection:
            return None
        return self.fctx.size()
    def getContentType(self):
        if self.isCollection:
            return None
        (mimetype, _mimeencoding) = mimetypes.guess_type(self.path)
        if not mimetype:
            return "application/octet-stream"
        return mimetype
    def getCreationDate(self):
#        statresults = os.stat(self._filePath)
#        return statresults[stat.ST_CTIME]
        return None # TODO
    def getDisplayName(self):
        if self.isCollection:
            return self.name
        return "%s@%s" % (self.name, self.fctx.filerev())
    def getEtag(self):
#        return util.getETag(self._filePath)
        return md5(self.path).hexdigest() + "-" + str(self.getLastModified()) + "-" + str(self.getContentLength())
    def getLastModified(self):
        if self.isCollection:
            return None
        # (secs, tz-ofs)
        return self.fctx.date()[0]
    def supportRanges(self):
        return False
    def displayType(self):
        if self.isCollection:
            return "Directory"
        return "File"

    def getMemberNames(self):
        assert self.isCollection
        cache = self.environ["wsgidav.hg.cache"][str(self.rev)]
        dirinfos = cache["dirinfos"]
        return dirinfos[self.repoPath][0] + dirinfos[self.repoPath][1]
#        return self.provider._listMembers(self.path)

    def getPropertyNames(self, isAllProp):
        """Return list of supported property names in Clark Notation.

        See DAVResource.getPropertyNames()
        """
        # Let base class implementation add supported live and dead properties
        propNameList = super(HgResource, self).getPropertyNames(isAllProp)
        # Add custom live properties (report on 'allprop' and 'propnames')
        propNameList.extend(["{hg:}status",
                             "{hg:}branch",
#                             "{hg:}changectx",
                             "{hg:}date",
                             "{hg:}description",
#                             "{hg:}filenode",
                             "{hg:}filerev",
#                             "{hg:}node",
                             "{hg:}rev",
                             "{hg:}user",
                             ])
        return propNameList

    def getPropertyValue(self, propname):
        """Return the value of a property.

        See getPropertyValue()
        """
        # Supported custom live properties
        cache = self.environ.get("wsgidav.hg.cache")
        if propname == "{hg:}status":
            return cache["filestats"][self.path]
        elif propname == "{hg:}branch":
            return self.fctx.branch()
#        elif propname == "{hg:}changectx":
#            return self.fctx.changectx()
        elif propname == "{hg:}date":
            # (secs, tz-ofs)
            return str(self.fctx.date()[0])
        elif propname == "{hg:}description":
            return self.fctx.description()
#        elif propname == "{hg:}filenode":
#            return self.fctx.filenode()
        elif propname == "{hg:}filerev":
            return str(self.fctx.filerev())
#        elif propname == "{hg:}node":
#            return str(self.fctx.node())
        elif propname == "{hg:}rev":
            return str(self.fctx.rev())
        elif propname == "{hg:}user":
            return str(self.fctx.user())

        # Let base class implementation report live and dead properties
        return super(HgResource, self).getPropertyValue(propname)

    def setPropertyValue(self, propname, value, dryRun=False):
        """Set or remove property value.

        See DAVResource.setPropertyValue()
        """
        raise DAVError(HTTP_FORBIDDEN)

    def getContent(self):
        """Open content as a stream for reading.

        See DAVResource.getContent()
        """
        assert not self.isCollection
        d = self.fctx.data()
        return StringIO(d)

#===============================================================================
# HgResourceProvider
#===============================================================================

class HgResourceProvider(DAVProvider):
    """
    DAV provider that serves a VirtualResource derived structure.
    """
    def __init__(self, repoPath):
        super(HgResourceProvider, self).__init__()
        self.repoPath = repoPath
        print "Mercurial version %s" % hgversion
        self.ui = mercurial.ui.ui()
        self.repo = hg.repository(self.ui, repoPath)
        self.ui.status("Connected to repository %s\n" % self.repo.root)
        self.repoPath = self.repo.root

        # Verify integrity of the repository
        util.status("Verify repository '%s' tree..." % self.repo.root)
        commands.verify(self.ui, self.repo)

#        self.ui.status("Changelog: %s\n" % self.repo.changelog)
        print "Status:"
        pprint(self.repo.status())
        self.repo.ui.status("the default username to be used in commits: %s\n" % self.repo.ui.username())
#        self.repo.ui.status("a short form of user name USER %s\n" % self.repo.ui.shortuser(user))
        self.ui.status("Expandpath: %s\n" % self.repo.ui.expandpath(repoPath))

        print "Working directory state summary:"
        self.ui.pushbuffer()
        commands.summary(self.ui, self.repo, remote=False)
        res = self.ui.popbuffer().strip()
        reslines = [ tuple(line.split(":", 1)) for line in res.split("\n")]
        pprint(reslines)

        print "Repository state summary:"
        self.ui.pushbuffer()
        commands.identify(self.ui, self.repo,
                          num=True, id=True, branch=True, tags=True)
        res = self.ui.popbuffer().strip()
        reslines = [ tuple(line.split(":", 1)) for line in res.split("\n")]
        pprint(reslines)

#        print "Log:"
#        self.ui.pushbuffer()
#        commands.log(self.ui, self.repo, limit=3)
#        res = self.ui.popbuffer().strip()
#        statuslist = [ tuple(line.split(":", 1)) for line in res.split("\n")]
#        pprint(statuslist)

        # 'cat': get file content for a given revision:
#        self.ui.pushbuffer()
#        commands.cat(self.ui, self.repo, filename)
#        res = self.ui.popbuffer()
        # heads
        # manifest: list all versioned files
        # parents
        # paths
        # showconfig

        print "Tip:"
        self.ui.pushbuffer()
        commands.tip(self.ui, self.repo)
        res = self.ui.popbuffer().strip()
        statuslist = [ tuple(line.split(":", 1)) for line in res.split("\n")]
        pprint(statuslist)
#        raise
#        self.repo.ui.status(self.repo.)

    def _readTree(self, rev):
        """Return a dictionary containing all files under source control.

        files:
            sorted list of all file paths under source control
        filestats:
            {filepath: status, ...}
        dirinfos:
            {folderpath: (collectionlist, filelist), ...}

        ::

            {'dirinfos': {'/': (['wsgidav',
                                 'tools',
                                 '.settings',
                                 'WsgiDAV.egg-info',
                                 'tests'],
                                ['index.rst',
                                 'wsgidav MAKE_DAILY_BUILD.launch',
                                 'wsgidav run_server.py DEBUG.launch',
                                 'wsgidav run_server.py RELEASE.launch',
                                 'wsgidav test_all.py.launch',
                                 'wsgidav-paste.conf',
                                 ...
                                 'setup.py']),
                          '/wsgidav': (['addons', 'samples', 'server', 'interfaces'],
                                       ['__init__.pyc',
                                        'dav_error.pyc',
                                        'dav_provider.pyc',
                                        ...
                                        'wsgidav_app.py']),
                            },
             'files': ['/.hgignore',
                       '/ADDONS.txt',
                       '/wsgidav/addons/mysql_dav_provider.py',
                       ...
                       ],
             'filestats': {'/.hgignore': 'C',
                           '/README.txt': 'C',
                           '/WsgiDAV.egg-info/PKG-INFO': 'I',
                           }
                           }
        """
        start_time = time.time()
        self.ui.pushbuffer()
        commands.status(self.ui, self.repo,
                        modified=True, added=True, clean=True,
#                        all=True,
                        revision=rev)
        res = self.ui.popbuffer()
        files = []
        dirinfos = {}
        filestats = {}
        for line in res.split("\n"):
            if len(line) < 3:
                continue
            stat, file = line.split(" ", 1)
            file = file.replace("\\", "/")
            # add all parent directories to 'dirinfos'
            parents = file.split("/")
            if len(parents) >= 1:
                p1 = "/"
                for i in range(0, len(parents)-1):
                    p2 = parents[i]
                    dir = dirinfos.setdefault(p1, ([], []))
                    if not p2 in dir[0]:
                        dir[0].append(p2)
                    p1 = "%s/%s" % (p1.rstrip("/"), p2)
                dirinfos.setdefault(p1, ([], []))[1].append(parents[-1])
            file = "/" + file
            files.append(file)
            filestats[file] = stat
        files.sort()

        cache = {"files": files,
                 "dirinfos": dirinfos,
                 "filestats": filestats,
                 }
        util.status("_readTree(%s) took %s" % (rev, time.time() - start_time)
                    , var=cache
                    )
        return cache

#    def _identify(self, path, rev=None):
#        self.ui.pushbuffer()
#        commands.identify(self.ui, self.repo,
#                          num=True, id=True, branch=True, tags=True)
#        res = self.ui.popbuffer().strip().split(" ")
#        hasChanges = res[0].endswith("+")
#        dict = {"id": res[0].strip("+"),
#                "num": res[1].strip("+"),
#                "branch": res[2],
#                "tag": res[3],
#                "hasChanges": hasChanges,
#                "path": path,
#                }
#        pprint(dict)
#        return dict

#    def _listMembers(self, path, rev=None):
#        """Return a list of all non-collection members"""
#        # Pattern for direct members:
#        glob = "glob:" + os.path.join(path, "*").lstrip("/")
##        print glob
#        self.ui.pushbuffer()
#        commands.status(self.ui, self.repo,
#                        glob,
#                        all=True)
#        lines = self.ui.popbuffer().strip().split("\n")
#        pprint(lines)
#        return dict

    def getResourceInst(self, path, environ):
        """Return _VirtualResource object for path.

        path is expected to be
            categoryType/category/name/artifact
        for example:
            'by_tag/cool/My doc 2/info.html'

        See DAVProvider.getResourceInst()
        """
        self._count_getResourceInst += 1

        repoPath = path
        rev = None
        cmd, rest = util.popPath(path)

        if cmd == "":
            return VirtualCollection(self, path, environ,
                                     ["edit",
                                      "public",
#                                      "archive",
#                                      "tags",
                                      ])
        elif cmd == "edit":
            repoPath = "/" + rest.strip("/")
            rev = None #"."
        elif cmd == "public":
            repoPath = "/" + rest.strip("/")
            rev = "tip"
#        elif cmd == "archive":
#            repoPath = "/" + rest.strip("/")
#        elif cmd == "tags":
#            if  rest == "/":
#                return VirtualCollection(self, path, environ, ["a", "b", "c"])
#            else:
#                raise
        else:
            return None

        # read mercurial repo into request cache
        caches = environ.setdefault("wsgidav.hg.cache", {})
        if caches.get(str(rev)) is None:
            cache = caches[str(rev)] = self._readTree(rev)
        else:
            cache = caches[str(rev)]

        if repoPath in cache["filestats"]:
            # It is a version controlled file
            return HgResource(self, path, False, environ, rev, repoPath)

        if repoPath in cache["dirinfos"]:
            # It is an existing folder
            return HgResource(self, path, True, environ, rev, repoPath)
        return None
<|endoftext|>


<commit_before># coding: utf8
"""
    weasyprint.tests.test_metadata
    ------------------------------

    Test metadata of the document (bookmarks, links and destinations).

    :copyright: Copyright 2011-2012 Simon Sapin and contributors, see AUTHORS.
    :license: BSD, see LICENSE for details.

"""

from __future__ import division, unicode_literals

import io

import cairo

from .. import HTML
from ..document import PDFDocument
from .. import pdf
from .testing_utils import assert_no_logs

@assert_no_logs
def test_pdf_parser():
    fileobj = io.BytesIO()
    surface = cairo.PDFSurface(fileobj, 1, 1)
    for width, height in [
        (100, 100),
        (200, 10),
        (3.14, 987654321)
    ]:
        surface.set_size(width, height)
        surface.show_page()
    surface.finish()

    sizes = [page.get_value('MediaBox', '\[(.+?)\]').strip()
             for page in pdf.PDFFile(fileobj).pages]
    assert sizes == [b'0 0 100 100', b'0 0 200 10', b'0 0 3.14 987654321']

def get_metadata(html):
    document = HTML(string=html)._get_document(PDFDocument, [])
    return pdf.gather_metadata(document)

def get_bookmarks(html):
    (root, bookmarks), links, destinations = get_metadata(html)
    for bookmark in bookmarks:
        bookmark.pop('destination')
        bookmark.pop('label')
    return root, bookmarks

@assert_no_logs
def test_bookmarks():
    """Test the structure of the document bookmarks.

    Warning: the PDF output of this structure is not tested.

    """
    root, bookmarks = get_bookmarks('''

Title 1

Title 2

Title 3

Title 4

Title 5

Title 6

Title 7

Title 8

Title 9

Title 10

Title 11

    ''')
    assert root == dict(Count=11, First=1, Last=10)
    assert bookmarks == [
        dict(Count=0, First=None, Last=None, Next=2, Parent=0, Prev=None),
        dict(Count=4, First=3, Last=6, Next=7, Parent=0, Prev=1),
        dict(Count=0, First=None, Last=None, Next=4, Parent=2, Prev=None),
        dict(Count=1, First=5, Last=5, Next=6, Parent=2, Prev=3),
        dict(Count=0, First=None, Last=None, Next=None, Parent=4, Prev=None),
        dict(Count=0, First=None, Last=None, Next=None, Parent=2, Prev=4),
        dict(Count=2, First=8, Last=8, Next=10, Parent=0, Prev=2),
        dict(Count=1, First=9, Last=9, Next=None, Parent=7, Prev=None),
        dict(Count=0, First=None, Last=None, Next=None, Parent=8, Prev=None),
        dict(Count=1, First=11, Last=11, Next=None, Parent=0, Prev=7),
        dict(Count=0, First=None, Last=None, Next=None, Parent=10, Prev=None)]

    root, bookmarks = get_bookmarks('''

1
 level 1

2
 level 2

3
 level 1

4
 level 2

5
 level 3
    ''')
    assert root == dict(Count=5, First=1, Last=3)
    assert bookmarks == [
        dict(Count=1, First=2, Last=2, Next=3, Parent=0, Prev=None),
        dict(Count=0, First=None, Last=None, Next=None, Parent=1, Prev=None),
        dict(Count=2, First=4, Last=4, Next=None, Parent=0, Prev=1),
        dict(Count=1, First=5, Last=5, Next=None, Parent=3, Prev=None),
        dict(Count=0, First=None, Last=None, Next=None, Parent=4, Prev=None)]

    root, bookmarks = get_bookmarks('''

1
 h2 level 1

2
 h4 level 2

3
 h3 level 2

4
 h5 level 3

5
 h1 level 1

6
 h2 level 2

7
 h2 level 2

8
 h4 level 3

9
 h1 level 1
    ''')
    assert root == dict(Count=9, First=1, Last=9)
    assert bookmarks == [
        dict(Count=3, First=2, Last=3, Next=5, Parent=0, Prev=None),
        dict(Count=0, First=None, Last=None, Next=3, Parent=1, Prev=None),
        dict(Count=1, First=4, Last=4, Next=None, Parent=1, Prev=2),
        dict(Count=0, First=None, Last=None, Next=None, Parent=3, Prev=None),
        dict(Count=3, First=6, Last=7, Next=9, Parent=0, Prev=1),
        dict(Count=0, First=None, Last=None, Next=7, Parent=5, Prev=None),
        dict(Count=1, First=8, Last=8, Next=None, Parent=5, Prev=6),
        dict(Count=0, First=None, Last=None, Next=None, Parent=7, Prev=None),
        dict(Count=0, First=None, Last=None, Next=None, Parent=0, Prev=5)]
<commit_msg>Test bookmark labels and positions.<commit_after># coding: utf8
"""
    weasyprint.tests.test_metadata
    ------------------------------

    Test metadata of the document (bookmarks, links and destinations).

    :copyright: Copyright 2011-2012 Simon Sapin and contributors, see AUTHORS.
    :license: BSD, see LICENSE for details.

"""

from __future__ import division, unicode_literals

import io

import cairo

from .. import HTML
from ..document import PDFDocument
from .. import pdf
from .testing_utils import assert_no_logs

@assert_no_logs
def test_pdf_parser():
    fileobj = io.BytesIO()
    surface = cairo.PDFSurface(fileobj, 1, 1)
    for width, height in [
        (100, 100),
        (200, 10),
        (3.14, 987654321)
    ]:
        surface.set_size(width, height)
        surface.show_page()
    surface.finish()

    sizes = [page.get_value('MediaBox', '\[(.+?)\]').strip()
             for page in pdf.PDFFile(fileobj).pages]
    assert sizes == [b'0 0 100 100', b'0 0 200 10', b'0 0 3.14 987654321']

def get_metadata(html):
    document = HTML(string=html)._get_document(PDFDocument, [])
    return pdf.gather_metadata(document)

def get_bookmarks(html, structure_only=False):
    (root, bookmarks), links, destinations = get_metadata(html)
    for bookmark in bookmarks:
        if structure_only:
            bookmark.pop('destination')
            bookmark.pop('label')
        else:
            # Eliminate errors of floating point arithmetic
            # (eg. 499.99999999999994 instead of 500)
            p, x, y = bookmark['destination']
            bookmark['destination'] = p, round(x, 6), round(y, 6)
    return root, bookmarks

@assert_no_logs
def test_bookmarks():
    """Test the structure of the document bookmarks.

    Warning: the PDF output of this structure is not tested.

    """
    root, bookmarks = get_bookmarks('''

Title 1

Title 2

Title 3

Title 4

Title 5

Title 6

Title 7

Title 8

Title 9

Title 10

Title 11

    ''')
    assert root == dict(Count=11, First=1, Last=10)
    assert bookmarks == [
        dict(Count=0, First=None, Last=None, Next=2, Parent=0, Prev=None,
             label='Title 1', destination=(0, 0, 1000)),
        dict(Count=4, First=3, Last=6, Next=7, Parent=0, Prev=1,
             label='Title 2', destination=(0, 0, 900)),
        dict(Count=0, First=None, Last=None, Next=4, Parent=2, Prev=None,
             label='Title 3', destination=(0, 50, 800)),
        dict(Count=1, First=5, Last=5, Next=6, Parent=2, Prev=3,
             label='Title 4', destination=(0, 0, 700)),
        dict(Count=0, First=None, Last=None, Next=None, Parent=4, Prev=None,
             label='Title 5', destination=(0, 0, 600)),
        dict(Count=0, First=None, Last=None, Next=None, Parent=2, Prev=4,
             label='Title 6', destination=(1, 0, 900)),
        dict(Count=2, First=8, Last=8, Next=10, Parent=0, Prev=2,
             label='Title 7', destination=(1, 0, 800)),
        dict(Count=1, First=9, Last=9, Next=None, Parent=7, Prev=None,
             label='Title 8', destination=(1, 0, 700)),
        dict(Count=0, First=None, Last=None, Next=None, Parent=8, Prev=None,
             label='Title 9', destination=(1, 0, 600)),
        dict(Count=1, First=11, Last=11, Next=None, Parent=0, Prev=7,
             label='Title 10', destination=(1, 0, 500)),
        dict(Count=0, First=None, Last=None, Next=None, Parent=10, Prev=None,
             label='Title 11', destination=(1, 0, 400))]

    root, bookmarks = get_bookmarks('''

1
 level 1

2
 level 2

3
 level 1

4
 level 2

5
 level 3
    ''', structure_only=True)
    assert root == dict(Count=5, First=1, Last=3)
    assert bookmarks == [
        dict(Count=1, First=2, Last=2, Next=3, Parent=0, Prev=None),
        dict(Count=0, First=None, Last=None, Next=None, Parent=1, Prev=None),
        dict(Count=2, First=4, Last=4, Next=None, Parent=0, Prev=1),
        dict(Count=1, First=5, Last=5, Next=None, Parent=3, Prev=None),
        dict(Count=0, First=None, Last=None, Next=None, Parent=4, Prev=None)]

    root, bookmarks = get_bookmarks('''

1
 h2 level 1

2
 h4 level 2

3
 h3 level 2

4
 h5 level 3

5
 h1 level 1

6
 h2 level 2

7
 h2 level 2

8
 h4 level 3

9
 h1 level 1
    ''', structure_only=True)
    assert root == dict(Count=9, First=1, Last=9)
    assert bookmarks == [
        dict(Count=3, First=2, Last=3, Next=5, Parent=0, Prev=None),
        dict(Count=0, First=None, Last=None, Next=3, Parent=1, Prev=None),
        dict(Count=1, First=4, Last=4, Next=None, Parent=1, Prev=2),
        dict(Count=0, First=None, Last=None, Next=None, Parent=3, Prev=None),
        dict(Count=3, First=6, Last=7, Next=9, Parent=0, Prev=1),
        dict(Count=0, First=None, Last=None, Next=7, Parent=5, Prev=None),
        dict(Count=1, First=8, Last=8, Next=None, Parent=5, Prev=6),
        dict(Count=0, First=None, Last=None, Next=None, Parent=7, Prev=None),
        dict(Count=0, First=None, Last=None, Next=None, Parent=0, Prev=5)]
<|endoftext|>