Bug regression in chart module

Anonymous avatarAnonymous created an issue

Dear openpyxl developer team,

I developed a little utility tool (to generate an Excel report) using openpyxl 1.5.8 with Python 2.7.3. It works great but I would to make some enhancement.

Yesterday, I saw that a new version of openpyxl was out : 1.6.1. I also read the following information on https://bitbucket.org/ericgazoni/openpyxl/wiki/Home:

Openpyxl supports python (even if some tests can fail on 2.4) from 2.4 to 2.6.

Thus, I installed Python 2.6 with openpyxl 1.6.1 and I got this error:

Traceback (most recent call last): File "C:\Users\amarie.ADMINARTELYS\Projets\CWE\projets\CWEReportGenerator\src\CWEReportGenerator.py", line 68, in <module> main(sys.argv[1:]) File "C:\Users\amarie.ADMINARTELYS\Projets\CWE\projets\CWEReportGenerator\src\CWEReportGenerator.py", line 65, in main simulation.create_report() File "C:\Users\amarie.ADMINARTELYS\Projets\CWE\projets\CWEReportGenerator\src\Simulation.py", line 101, in create_report self.create_convergence_sheet(wb) File "C:\Users\amarie.ADMINARTELYS\Projets\CWE\projets\CWEReportGenerator\src\Simulation.py", line 271, in create_convergence_sheet chart1.add_serie(xyserie12) File "build\bdist.win32\egg\openpyxl\chart.py", line 221, in add_serie File "build\bdist.win32\egg\openpyxl\chart.py", line 250, in _compute_min_max File "build\bdist.win32\egg\openpyxl\chart.py", line 188, in mymax ValueError: max() arg is an empty sequence

The problem comes when I try to add a serie to a chart...

I tried to use openpyxl 1.5.8 with Python 2.6 and it worked well. With Python 2.7, I got the error for openpyxl 1.6.1 and not with openpyxl 1.5.8.

I suspects a regression that is why I report this bug. I could provide you the code but, unfortunately I can't provide you the data (except if I have the time to anonymiszd them).

Regards,

Alexandre

Comments (10)

  1. Charlie Clark

    Alexandre, can you please sample data and the shortest possible piece of code to demonstrate the regression. The data doesn't have to be your complete dataset.

  2. Alexandre Marié

    Dear Charlie,

    I think I found where the problem come from...

    Here is the code I use:

        chart1 = BarChart()        
        chart1.add_serie(xyserie11)
        chart1.add_serie(xyserie12)
    
        # add legends and properties of the first graph
        chart1.lang = "gb-GB"
        chart1.title = "Welfare distribution"
        chart1.y_axis.title = "Welfare (euros)"
    
        # set the min value for the first graph
        chart1.y_axis.min = min( 0, min(xyserie11.get_min_max()[0], xyserie12.get_min_max()[0]) )
    
        # add the first chart to the workbook
        ws.add_chart(chart1)
    

    And I got this error:

        chart1.y_axis.min = min( 0, min(xyserie11.get_min_max()[0], xyserie12.get_min_max()[0]) )
        File "build\bdist.win32\egg\openpyxl\chart.py", line 153, in get_min_max
    

    AttributeError: 'Serie' object has no attribute 'mymax'

    I checked in the source of chart.py and the code which doesn't work is:

        def get_min_max(self):
    
        if self.error_bar:
            err_cache = self.error_bar.values._get_cache()
            vals = [v + err_cache[i] \
                for i, v in enumerate(self.values._get_cache())]
        else:
            vals = self.values._get_cache()
        return min(vals), self.mymax(vals)
    

    It is a method of the Serie object. And it can't work because Serie object doesn't have any method called mymax(). This method is in class Chart... I guess that Serie object should inherit from Chart object... I saw that the signature of the Serie object was: "class Serie(object):" as all objects in the chart file... But object is not defined... I think the problem come from here!

    By the way, if I tried to do that:

        chart1.y_axis.min = min( 0, min(xyserie11.get_min_max()[0], xyserie12.get_min_max()[0]) )
    

    It is because I have negative values in my series and the y_axis.min is set to 0 in the openpyxl source code (line 45 of chart file and this value is not changed in compute_min_max() method)... Thus, I can't see negative values in the bar chart...

    Would it be possible to use automatic values from Excel? Indeed, in the produced .xlsx sheet, I can't see negative values in my bar chart but when I right-click on the y axis, I can change axis options from "fixed" to "automatic" and the result is exactly what I want.

  3. Charlie Clark

    Hi Alexandre,

    thanks for the detailed post. I did some work on the charts modules last week and noticed the same missing method (there are other errors). I fixed this in my fork - well, it won't raise any errors and mymax won't choke on 0 and empty strings - but I haven't got very far in determining how the the method should really work. Could you give my fork a try (you'll need to download it and install it manually in a separate virtualenv which is a bit tricky on Windows but doable) and let me know if that works?

    I don't think that Serie should inherit at all from Chart. If anything, it is closer to Reference than Chart but I think it probably just makes sense to keep them separate: you can have references without having charts and you can have more than one series in chart.

  4. Alexandre Marié

    Hi Charlie,

    I would be happy to test your fork... Could you indicate me how I can download and install it? Thx.

    Actually, I don't really take the time to think about inheritance between graphic objects. I just noticed that the get_min_max() method from the Serie object use a mymax() method which not exist in Serie.

    I told you in my first comment that it was working with openpyxl 1.5.8. I checked the code of this method in openpyxl 1.5.8:

    def get_min_max(self):
    
        if self.error_bar:
            err_cache = self.error_bar.values._get_cache()
            vals = [v + err_cache[i] \
                for i, v in enumerate(self.values._get_cache())]
        else:
            vals = self.values._get_cache()
        return min(vals), max(vals)
    

    As you can see, in this version, the called method is the standard max() and not mymax()...

  5. Charlie Clark

    Hi Alexandre,

    interesting that bug crept in between versions. That should have been caught by tests, except there aren't any tests for the chart module, just for chartwriter. My fork includes them: https://bitbucket.org/charlie_x/openpyxl-ccr

    I suspect behaviour will still not be what you want - there are quite a few things that I'm not clear about - but we should be able to fix it.

  6. Charlie Clark

    Sample worksheet with values +10 to -10 and a chart produced by openpyxl. Negative values not displaying.

    from openpyxl import Workbook
    wb = Workbook()
    ws = wb.get_active_sheet()
    for idx, v in enumerate(range(10, -10, -1)):
        ws.cell(row=idx, column=0).value = v
    from openpyxl.chart import Reference, Serie, BarChart
    ref = Reference(ws, (0,0), (20, 0))
    series = Serie(ref)
    chart = BarChart()
    chart.add_serie(series)
    ws.add_chart(chart)
    wb.save("sample2.xlsx")
    
  7. Log in to comment
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.