Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v62 - TypeError: can't multiply sequence by non-int of type 'float' #2146

Closed
jbpenrath opened this issue May 2, 2024 · 1 comment
Closed
Labels
crash Problems preventing documents from being rendered

Comments

@jbpenrath
Copy link

jbpenrath commented May 2, 2024

Since I upgraded my project to version 62, I get the following error on PDF generation. It seems to be related to flex layout.

TypeError: can't multiply sequence by non-int of type 'float'
  • Platform: Docker - python:3.10-slim-bullseye (Debian 11 bullseye)

  • Python: 3.10.14

  • Packages:

    • cffi==1.16.0
    • cssselect2==0.7.0
    • fonttools==4.51.0
    • html5lib==1.1
    • pillow==10.3.0
    • pydyf==0.10.0
    • pyphen==0.15.0
    • tinycss2==1.3.0
    • Weasyprint==62.0

Here it is the full log I get :

joanie/core/utils/issuers.py:37: in generate_document
    return doc_html.write_pdf(stylesheets=[css], zoom=1, font_config=font_config)
../../../.local/lib/python3.10/site-packages/weasyprint/__init__.py:259: in write_pdf
    self.render(font_config, counter_style, **options)
../../../.local/lib/python3.10/site-packages/weasyprint/__init__.py:216: in render
    return Document._render(self, font_config, counter_style, options)
../../../.local/lib/python3.10/site-packages/weasyprint/document.py:262: in _render
    [Page(page_box) for page_box in page_boxes],
../../../.local/lib/python3.10/site-packages/weasyprint/document.py:262: in <listcomp>
    [Page(page_box) for page_box in page_boxes],
../../../.local/lib/python3.10/site-packages/weasyprint/layout/__init__.py:130: in layout_document
    pages = list(make_all_pages(context, root_box, html, pages))
../../../.local/lib/python3.10/site-packages/weasyprint/layout/page.py:906: in make_all_pages
    page, resume_at = remake_page(i, context, root_box, html)
../../../.local/lib/python3.10/site-packages/weasyprint/layout/page.py:844: in remake_page
    page, resume_at, next_page = make_page(
../../../.local/lib/python3.10/site-packages/weasyprint/layout/page.py:646: in make_page
    root_box, resume_at, next_page, _, _, _ = block_level_layout(
../../../.local/lib/python3.10/site-packages/weasyprint/layout/block.py:59: in block_level_layout
    return block_level_layout_switch(
../../../.local/lib/python3.10/site-packages/weasyprint/layout/block.py:75: in block_level_layout_switch
    return block_box_layout(
../../../.local/lib/python3.10/site-packages/weasyprint/layout/block.py:124: in block_box_layout
    result = block_container_layout(
../../../.local/lib/python3.10/site-packages/weasyprint/layout/block.py:714: in block_container_layout
    next_page, new_children, new_max_lines) = _in_flow_layout(
../../../.local/lib/python3.10/site-packages/weasyprint/layout/block.py:506: in _in_flow_layout
    collapsing_through, max_lines) = block_level_layout(
../../../.local/lib/python3.10/site-packages/weasyprint/layout/block.py:59: in block_level_layout
    return block_level_layout_switch(
../../../.local/lib/python3.10/site-packages/weasyprint/layout/block.py:82: in block_level_layout_switch
    result = flex_layout(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

context = <weasyprint.layout.LayoutContext object at 0x7f8d53dba650>
box = <FlexBox body>, bottom_space = 0, skip_stack = None
containing_block = <BlockBox html>, page_is_empty = True, absolute_boxes = []
fixed_boxes = []

    def flex_layout(context, box, bottom_space, skip_stack, containing_block,
                    page_is_empty, absolute_boxes, fixed_boxes):
        from . import block, preferred
    
        context.create_block_formatting_context()
        resume_at = None
    
        # Step 1 is done in formatting_structure.boxes
        # Step 2
        if box.style['flex_direction'].startswith('row'):
            axis, cross = 'width', 'height'
        else:
            axis, cross = 'height', 'width'
    
        margin_left = 0 if box.margin_left == 'auto' else box.margin_left
        margin_right = 0 if box.margin_right == 'auto' else box.margin_right
        margin_top = 0 if box.margin_top == 'auto' else box.margin_top
        margin_bottom = 0 if box.margin_bottom == 'auto' else box.margin_bottom
    
        if getattr(box, axis) != 'auto':
            available_main_space = getattr(box, axis)
        else:
            if axis == 'width':
                available_main_space = (
                    containing_block.width -
                    margin_left - margin_right -
                    box.padding_left - box.padding_right -
                    box.border_left_width - box.border_right_width)
            else:
                main_space = context.page_bottom - bottom_space - box.position_y
                if containing_block.height != 'auto':
                    if isinstance(containing_block.height, Dimension):
                        assert containing_block.height.unit == 'px'
                        main_space = min(main_space, containing_block.height.value)
                    else:
                        main_space = min(main_space, containing_block.height)
                available_main_space = (
                    main_space -
                    margin_top - margin_bottom -
                    box.padding_top - box.padding_bottom -
                    box.border_top_width - box.border_bottom_width)
    
        if getattr(box, cross) != 'auto':
            available_cross_space = getattr(box, cross)
        else:
            if cross == 'height':
                main_space = (
                    context.page_bottom - bottom_space - box.content_box_y())
                if containing_block.height != 'auto':
                    if isinstance(containing_block.height, Dimension):
                        assert containing_block.height.unit == 'px'
                        main_space = min(main_space, containing_block.height.value)
                    else:
                        main_space = min(main_space, containing_block.height)
                available_cross_space = (
                    main_space -
                    margin_top - margin_bottom -
                    box.padding_top - box.padding_bottom -
                    box.border_top_width - box.border_bottom_width)
            else:
                available_cross_space = (
                    containing_block.width -
                    margin_left - margin_right -
                    box.padding_left - box.padding_right -
                    box.border_left_width - box.border_right_width)
    
        # Step 3
        children = box.children
        parent_box = box.copy_with_children(children)
        resolve_percentages(parent_box, containing_block)
        # TODO: removing auto margins is OK for this step, but margins should be
        # calculated later.
        if parent_box.margin_top == 'auto':
            box.margin_top = parent_box.margin_top = 0
        if parent_box.margin_bottom == 'auto':
            box.margin_bottom = parent_box.margin_bottom = 0
        if parent_box.margin_left == 'auto':
            box.margin_left = parent_box.margin_left = 0
        if parent_box.margin_right == 'auto':
            box.margin_right = parent_box.margin_right = 0
        if isinstance(parent_box, boxes.FlexBox):
            block.block_level_width(parent_box, containing_block)
        else:
            parent_box.width = preferred.flex_max_content_width(
                context, parent_box)
        original_skip_stack = skip_stack
        children = sorted(children, key=lambda item: item.style['order'])
        if skip_stack is not None:
            (skip, skip_stack), = skip_stack.items()
            if box.style['flex_direction'].endswith('-reverse'):
                children = children[:skip + 1]
            else:
                children = children[skip:]
            skip_stack = skip_stack
        else:
            skip_stack = None
        child_skip_stack = skip_stack
        for child in children:
            if not child.is_flex_item:
                continue
    
            # See https://www.w3.org/TR/css-flexbox-1/#min-size-auto
            if child.style['overflow'] == 'visible':
                main_flex_direction = axis
            else:
                main_flex_direction = None
            resolve_percentages(child, containing_block, main_flex_direction)
            child.position_x = parent_box.content_box_x()
            child.position_y = parent_box.content_box_y()
            if child.min_width == 'auto':
                specified_size = child.width if child.width != 'auto' else inf
                if isinstance(child, boxes.ParentBox):
                    new_child = child.copy_with_children(child.children)
                else:
                    new_child = child.copy()
                new_child.style = child.style.copy()
                new_child.style['width'] = 'auto'
                new_child.style['min_width'] = Dimension(0, 'px')
                new_child.style['max_width'] = Dimension(inf, 'px')
                content_size = min_content_width(context, new_child, outer=False)
                child.min_width = min(specified_size, content_size)
            elif child.min_height == 'auto':
                # TODO: find a way to get min-content-height
                specified_size = child.height if child.height != 'auto' else inf
                if isinstance(child, boxes.ParentBox):
                    new_child = child.copy_with_children(child.children)
                else:
                    new_child = child.copy()
                new_child.style = child.style.copy()
                new_child.style['height'] = 'auto'
                new_child.style['min_height'] = Dimension(0, 'px')
                new_child.style['max_height'] = Dimension(inf, 'px')
                new_child = block.block_level_layout(
                    context, new_child, -inf, child_skip_stack, parent_box,
                    page_is_empty)[0]
                content_size = new_child.height
                child.min_height = min(specified_size, content_size)
    
            child.style = child.style.copy()
    
            if child.style['flex_basis'] == 'content':
                flex_basis = child.flex_basis = 'content'
            else:
                resolve_one_percentage(child, 'flex_basis', available_main_space)
                flex_basis = child.flex_basis
    
            # "If a value would resolve to auto for width, it instead resolves
            # to content for flex-basis." Let's do this for height too.
            # See https://www.w3.org/TR/css-flexbox-1/#propdef-flex-basis
            resolve_one_percentage(child, axis, available_main_space)
            if flex_basis == 'auto':
                if child.style[axis] == 'auto':
                    flex_basis = 'content'
                else:
                    if axis == 'width':
                        flex_basis = child.border_width()
                        if child.margin_left != 'auto':
                            flex_basis += child.margin_left
                        if child.margin_right != 'auto':
                            flex_basis += child.margin_right
                    else:
                        flex_basis = child.border_height()
                        if child.margin_top != 'auto':
                            flex_basis += child.margin_top
                        if child.margin_bottom != 'auto':
                            flex_basis += child.margin_bottom
    
            # Step 3.A
            if flex_basis != 'content':
                child.flex_base_size = flex_basis
    
            # TODO: Step 3.B
            # TODO: Step 3.C
    
            # Step 3.D is useless, as we never have infinite sizes on paged media
    
            # Step 3.E
            else:
                child.style[axis] = 'max-content'
    
                # TODO: don't set style value, support *-content values instead
                if child.style[axis] == 'max-content':
                    child.style[axis] = 'auto'
                    if axis == 'width':
                        child.flex_base_size = max_content_width(context, child)
                    else:
                        if isinstance(child, boxes.ParentBox):
                            new_child = child.copy_with_children(child.children)
                        else:
                            new_child = child.copy()
                        new_child.width = inf
                        new_child = block.block_level_layout(
                            context, new_child, -inf, child_skip_stack, parent_box,
                            page_is_empty, absolute_boxes, fixed_boxes)[0]
                        child.flex_base_size = new_child.margin_height()
                elif child.style[axis] == 'min-content':
                    child.style[axis] = 'auto'
                    if axis == 'width':
                        child.flex_base_size = min_content_width(context, child)
                    else:
                        if isinstance(child, boxes.ParentBox):
                            new_child = child.copy_with_children(child.children)
                        else:
                            new_child = child.copy()
                        new_child.width = 0
                        new_child = block.block_level_layout(
                            context, new_child, -inf, child_skip_stack, parent_box,
                            page_is_empty, absolute_boxes, fixed_boxes)[0]
                        child.flex_base_size = new_child.margin_height()
                else:
                    assert child.style[axis].unit == 'px'
                    # TODO: should we add padding, borders and margins?
                    child.flex_base_size = child.style[axis].value
    
            child.hypothetical_main_size = max(
                getattr(child, f'min_{axis}'), min(
                    child.flex_base_size, getattr(child, f'max_{axis}')))
    
            # Skip stack is only for the first child
            child_skip_stack = None
    
        # Step 4
        # TODO: the whole step has to be fixed
        if axis == 'width':
            block.block_level_width(box, containing_block)
        else:
            if box.style['height'] != 'auto':
                if box.style['height'].unit == '%':
>                   box.height = box.style['height'].value / 100. * containing_block.height
E                   TypeError: can't multiply sequence by non-int of type 'float'
jbpenrath added a commit to openfun/joanie that referenced this issue May 2, 2024
A major version of weasyprint has been released but some bugs appears.
Currently, we do not pin weasyprint to a fixed version. For now, we just pin
weasyprint to the latest version of v61.

Kozea/WeasyPrint#2146
jbpenrath added a commit to openfun/joanie that referenced this issue May 2, 2024
A major version of weasyprint has been released but some bugs appears.
Currently, we do not pin weasyprint to a fixed version. For now, we just pin
weasyprint to the latest version of v61.

Kozea/WeasyPrint#2146
jbpenrath added a commit to openfun/joanie that referenced this issue May 2, 2024
A major version of weasyprint has been released but some bugs appears.
Currently, we do not pin weasyprint to a fixed version. For now, we just pin
weasyprint to the latest version of v61.

Kozea/WeasyPrint#2146
@liZe liZe closed this as completed in 6dd6470 May 2, 2024
@liZe liZe added the crash Problems preventing documents from being rendered label May 2, 2024
@liZe
Copy link
Member

liZe commented May 2, 2024

Thanks for the report. The crash has been fixed, but the size of the flex item is still wrong (it was wrong too in 61).

Related to #2135.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
crash Problems preventing documents from being rendered
Projects
None yet
Development

No branches or pull requests

2 participants