Skip to content

Use cssdecl package for resolving CSS #16170

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

Closed
wants to merge 18 commits into from

Conversation

jnothman
Copy link
Contributor

Uses dependency on an external project instead of having local code to handle the resolution of DataFrame.style CSS to interpretable atoms. This allows reuse of that code and relatively independent development of more complete CSS2.2 coverage.

  • closes #xxxx N/A
  • tests added / passed
  • passes git diff upstream/master --name-only -- '*.py' | flake8 --diff
  • whatsnew entry N/A

@@ -169,11 +169,13 @@
{'alignment': {'wrap_text': True}}),
])
def test_css_to_excel(css, expected):
pytest.importorskip('cssdecl')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the main thing to test is what happens when cssdecl is not installed and to_excel is called
need an informative error message

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you have an assertion for exception message checking in your testing arsenal?

@@ -4,6 +4,7 @@ numpy=1.8.2
xlwt=0.7.5
openpyxl=1.6.2
xlsxwriter=0.4.6
cssdecl
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't add this to all of these
just 2.7 and 3.6 is enough
u can add the os x build if u want
windows prob 2 (only need to add to 1)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay. I meant to clarify but forgot.

@jnothman
Copy link
Contributor Author

Do get this working, does cssdecl need uploading to the pandas channel? Currently it's at https://github.com/jnothman/cssdecl

@jreback
Copy link
Contributor

jreback commented May 1, 2017

@jnothman nothing to do with a channel. your package should be pip installable.

@jnothman
Copy link
Contributor Author

jnothman commented May 1, 2017

Are you certain? it is definitely pip installable... and the error messages in travis are like:

PackageNotFoundError: Package missing in current osx-64 channels: 
  - cssdecl

@TomAugspurger
Copy link
Contributor

You should be able to add it to ci/requirements-*.pip

@jnothman
Copy link
Contributor Author

jnothman commented May 1, 2017 via email

@jnothman
Copy link
Contributor Author

jnothman commented May 2, 2017

Test failure appears unrelated

@codecov
Copy link

codecov bot commented May 2, 2017

Codecov Report

Merging #16170 into master will decrease coverage by 0.21%.
The diff coverage is 66.66%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master   #16170      +/-   ##
==========================================
- Coverage   90.86%   90.64%   -0.22%     
==========================================
  Files         162      161       -1     
  Lines       50819    50718     -101     
==========================================
- Hits        46177    45975     -202     
- Misses       4642     4743     +101
Flag Coverage Δ
#multiple 88.42% <66.66%> (-0.23%) ⬇️
#single 40.36% <0%> (+0.02%) ⬆️
Impacted Files Coverage Δ
pandas/io/formats/excel.py 66.55% <66.66%> (-30.09%) ⬇️
pandas/io/excel.py 79.73% <0%> (-0.89%) ⬇️
pandas/io/formats/style.py 96.06% <0%> (-0.31%) ⬇️
pandas/util/testing.py 78.88% <0%> (-0.19%) ⬇️
pandas/compat/__init__.py 62.22% <0%> (-0.11%) ⬇️
pandas/core/generic.py 91.63% <0%> (ø) ⬆️
pandas/core/config_init.py 94.52% <0%> (+0.23%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a7a0574...fa9ca9f. Read the comment docs.

@codecov
Copy link

codecov bot commented May 2, 2017

Codecov Report

Merging #16170 into master will decrease coverage by 0.24%.
The diff coverage is 57.14%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master   #16170      +/-   ##
==========================================
- Coverage   90.38%   90.13%   -0.25%     
==========================================
  Files         161      160       -1     
  Lines       50916    50807     -109     
==========================================
- Hits        46021    45797     -224     
- Misses       4895     5010     +115
Flag Coverage Δ
#multiple 87.9% <57.14%> (-0.24%) ⬇️
#single 40.25% <0%> (-0.08%) ⬇️
Impacted Files Coverage Δ
pandas/io/formats/excel.py 43.37% <57.14%> (-29.78%) ⬇️
pandas/io/gbq.py 25% <0%> (-58.34%) ⬇️
pandas/io/excel.py 57.84% <0%> (-3.85%) ⬇️
pandas/io/formats/style.py 95.64% <0%> (-0.28%) ⬇️
pandas/core/frame.py 97.68% <0%> (-0.1%) ⬇️
pandas/plotting/_converter.py 65.05% <0%> (+1.81%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0ea0f25...2d83322. Read the comment docs.

with pytest.raises(ImportError) as rec:
pd.DataFrame({"A": [1, 2]}).style.to_excel("openpyxl")

assert 'not installed' in rec.value.msg
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except that it seemed to fall CI

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:( will push a fix soon

@jreback
Copy link
Contributor

jreback commented May 2, 2017

this PR needs to wait for 0.21.0 fyi. would like to have the dep filled out / more testing and docs.

@jreback jreback added the IO Excel read_excel, to_excel label May 2, 2017
@jnothman
Copy link
Contributor Author

jnothman commented May 2, 2017 via email

@jreback
Copy link
Contributor

jreback commented May 3, 2017

@jnothman what I mean is that this dep is used, tested on CI, has docs etc. Yes I know you carved it out of pandas, but it needs to be a properly supported (and versioned package). Its not a big deal to do that (and it kind of forces you to make it user friendly). We are going to start pushing to 0.21.0 soon anyhow (right after the release of 0.20.0)

@jnothman
Copy link
Contributor Author

jnothman commented May 3, 2017 via email

@jnothman
Copy link
Contributor Author

jnothman commented May 4, 2017


def assert_equal_style(cell1, cell2):
# XXX: should find a better way to check equality
assert cell1.alignment.__dict__ == cell2.alignment.__dict__
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can just assert ceil1.alignment == cell2.alignment, though much better would be to add an __eq__ method to cell itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cell is in openpyxl. Are you suggesting I monkeypatch??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I can't recall why right now, but testing for equality of cell.alignment did not work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In [7]: import openpyxl

In [8]: a1 = openpyxl.Workbook().create_sheet()['A1']

In [9]: a2 = openpyxl.Workbook().create_sheet()['A2']

In [11]: a1.alignment = openpyxl.styles.Alignment(horizontal='center')

In [12]: a2.alignment
Out[12]:
<openpyxl.styles.alignment.Alignment object>
Parameters:
wrapText=None, vertical=None, readingOrder=0.0, textRotation=0, relativeIndent=0.0, horizontal=None, justifyLastLine=None, indent=0.0, shrinkToFit=None

In [13]: a2.alignment == a1.alignment
Out[13]: False

In [14]: a2.alignment = openpyxl.styles.Alignment(horizontal='center')

In [15]: a2.alignment == a1.alignment
Out[15]: False

@@ -2490,3 +2499,71 @@ def custom_converter(css):
n_cells += 1

assert n_cells == (10 + 1) * (3 + 1)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would split the style tests off into another test file. you can in fact make a sub-dir excel and move test_excel.py there as well (not that if you do this, you need to add a __init__.py and add the dir to setup.py.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I'm getting you entirely, as I'm trying to work out how this relates to the boundaries between io/excel and io/formats/excel. Do you want to see this all merged?

IMO, a logical structure for the code might be:

  • pandas/io/excel/read.py - *Reader
  • pandas/io/excel/write.py - ExcelFormatter, *Writer
  • pandas/io/excel/style.py - CSSToExcelConverter

with test files to correspond.

Or perhaps pandas/io/excel/format.py should include CSSToExcelConverter and ExcelFormatter as pandas/io/formats/excel.py does currently.

However, the tests you're concerned to move here are end-to-end tests covering the Styler.to_excel interface and writers.

Please clarify your vision if you can.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the code org and the test org ideally will mirror each other. but they can be moved independently. for now I want smaller / more well defined / does 1 thing test files. I think this is a nice way to organize things into chunks. Then can prob tackle the code reorg.

The formatting code lives in pandas/io/formats/*, and style fits right in here.
Style shouldn't be directly connected (nor depended on in any way) in the core code. It is simply output formatting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're saying that this test belongs in pandas/tests/io/formats...?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes move all style things there (in test_to_excel)

@jreback
Copy link
Contributor

jreback commented May 6, 2017

this looks reasonable. pls rebase and ping when ready.

@@ -0,0 +1 @@
cssdecl
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add to ci/requirements-3.5.pip & requirements-2.7.pip as well (what you added here barely tests anything and only on windows).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. Strange. I think I must have misperformed a patch commit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd presume 3.6, 3.5_DOC and 2.7_WIN sufficient, though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes that is fine. note that you might want to create a conda-forge package for this at some point (otherwise you can't include it directly in a dependecy tree as they don't support pip install directly).

@@ -9,6 +9,7 @@ python-dateutil
pytz
openpyxl
xlsxwriter
cssdecl
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is for developement and not necessary to have this, pls remove.

@jreback
Copy link
Contributor

jreback commented May 6, 2017

add a note in api-breaking in 0.21.0

@jnothman
Copy link
Contributor Author

jnothman commented May 7, 2017

Done apart from directory restructure.

@jnothman
Copy link
Contributor Author

jnothman commented May 7, 2017

So is this okay for now?

@jnothman
Copy link
Contributor Author

jnothman commented May 7, 2017

re conda-forge, I'm still learning the ropes a little, but have at least built a package and posted it to my own channel.

@jreback
Copy link
Contributor

jreback commented May 7, 2017

re conda-forge, I'm still learning the ropes a little, but have at least built a package and posted it to my own channel.

yeah you can submit a recipe to conda-forge and then it will create a feedstock. should be pretty easy for this. not required, but nice.

Copy link
Contributor

@jreback jreback left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor comments. ping on green.

@@ -249,6 +249,7 @@ Optional Dependencies
* `openpyxl <http://packages.python.org/openpyxl/>`__: openpyxl version 1.6.1
or higher (but lower than 2.0.0), or version 2.2 or higher, for writing .xlsx files (xlrd >= 0.9.0)
* `XlsxWriter <https://pypi.python.org/pypi/XlsxWriter>`__: Alternative Excel writer
* `cssdecl <https://pypi.python.org/pypi/cssdecl>`__: along with one of openpyxl or XlsxWriter for exporting styled DataFrames
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you have a min version requirement we can put it here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've decided I can probably keep the interface backwards compatible. There may, in the future, be min versions for certain features, e.g. gradient fills used for bar charts, but there will also be a min version of openpyxl for that (and I don't think xlsxwriter supports)

Copy link
Contributor Author

@jnothman jnothman May 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

xlsxwriter support mentioned here depends largely on #16149 being merged

['', '', '']],
index=df.index, columns=df.columns)

pytest.importorskip('jinja2')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this also need cssdecl to be imported?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you asking if the fixture also needs cssdecl to be imported? no, since we allow for a custom style_converter.

pytest.importorskip(engine)

if engine == 'openpyxl' and openpyxl_compat.is_compat(major_ver=1):
pytest.xfail('openpyxl1 does not support some openpyxl2-compatible '
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can skip rather than xfail here

@@ -0,0 +1 @@
cssdecl
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add to requirements-2.7 as well

@jreback
Copy link
Contributor

jreback commented May 7, 2017

@TomAugspurger ?

@@ -34,6 +34,7 @@ Other Enhancements
Backwards incompatible API changes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

- The ``DataFrame.style.to_excel()`` styled Excel export functionality now requires an external dependency, `cssdecl <http://pypi.python.org/pypi/cssdecl>`_.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually add a little note in io.rst (about the change as well)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume you mean style.ipynb

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't this add export to xlxswriter as compared to 0.20.1? if so pls add another note in Other Enhancements section. (maybe also need to update the io.rst docs?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry I see you mentioned that above. nvm. then.

@jnothman
Copy link
Contributor Author

jnothman commented May 8, 2017

Green, @jreback

@jreback
Copy link
Contributor

jreback commented May 10, 2017

can you rebase on master. skips were not printing correctly, so now fixed.

@jreback jreback added this to the 0.21.0 milestone May 11, 2017
@@ -34,6 +34,7 @@ Other Enhancements
Backwards incompatible API changes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

- The ``DataFrame.style.to_excel()`` styled Excel export functionality now requires an external dependency, `cssdecl <http://pypi.python.org/pypi/cssdecl>`_.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just add the PR number (this number) to the end of the whatsnew. & rebase. ping on green to merge.

@jnothman
Copy link
Contributor Author

jnothman commented May 11, 2017 via email

@jnothman
Copy link
Contributor Author

PR number added to what's new. Master merged.

cssdecl's next release (and I'll try to package it in conda-forge too) should at least handle some CSS cleaning better and handle border: 1px solid red and similar shorthands.

@jnothman
Copy link
Contributor Author

I've just been informed of the package weasyprint which does everything cssdecl should do, it seems. My literature review want good enough. Let me look a bit further into whether we can use that rather than reinvent the wheel. Closing for now.

@jnothman jnothman closed this May 15, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement IO Excel read_excel, to_excel
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants