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

ENH: Style blocks #15954

Merged
merged 7 commits into from
Apr 15, 2017
Merged

ENH: Style blocks #15954

merged 7 commits into from
Apr 15, 2017

Conversation

TomAugspurger
Copy link
Contributor

ENH: Add blocks to Styler template

This will make subclassing the Styler and extending
the templates easier.

REF: Move template to its own file

Use Environment and PackageLoader to load it

@TomAugspurger TomAugspurger added IO HTML read_html, to_html, Styler.apply, Styler.applymap Output-Formatting __repr__ of pandas objects, to_string labels Apr 8, 2017
@TomAugspurger TomAugspurger added this to the 0.20.0 milestone Apr 8, 2017
@TomAugspurger TomAugspurger requested a review from chris-b1 April 8, 2017 22:25
@TomAugspurger
Copy link
Contributor Author

Here's a gist with the new docs: http://nbviewer.jupyter.org/gist/anonymous/db1b30a4ba4085bda612944c6ba9b64e#Extensibility

I think right now the template isn't included in the package, need to update setup.py. Will do later but have to run for now.

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.

all for the reorg, do we need this in the top-level namespace?

maybe make a sub-dir for the style things at some point (e.g. pandas/formats/style (but another PR if you think its worth it).

@@ -358,6 +358,9 @@ Other Enhancements
- ``DataFrame.to_excel()`` has a new ``freeze_panes`` parameter to turn on Freeze Panes when exporting to Excel (:issue:`15160`)
- ``pd.read_html()`` will parse multiple header rows, creating a multiindex header. (:issue:`13434`).
- HTML table output skips ``colspan`` or ``rowspan`` attribute if equal to 1. (:issue:`15403`)
- ``pd.Styler`` template now has blocks for easier extension (:issue:`15649`)
Copy link
Contributor

Choose a reason for hiding this comment

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

is this the right one? (maybe use a :class:`....` ?

@@ -56,6 +56,7 @@
from pandas.util.print_versions import show_versions
from pandas.io.api import *
from pandas.util._tester import test
from pandas.formats.style import Styler
Copy link
Contributor

Choose a reason for hiding this comment

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

you really want to add this to the top-level? (does this import stuff)?

Copy link
Contributor

Choose a reason for hiding this comment

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

I do think it needs to be importable somewhere 'public', not sure if top level is the right spot, could argue either way. Do we need a pandas.api.io?

@@ -0,0 +1,61 @@
{%- block before_style -%}{%- endblock before_style -%}
Copy link
Contributor

Choose a reason for hiding this comment

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

make sure this is in the MANIFEST.IN

Copy link
Contributor

@chris-b1 chris-b1 left a comment

Choose a reason for hiding this comment

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

Looks pretty good, only one significant comment on structure of template

{%- endblock thead %}
{%- block tbody %}
<tbody>
{%- for r in body %}
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you add a rows block here? (or alternatively empty before_rows and after_rows). What I was trying to do is add additional rows at the end of the table which I don't think is possible with these extension points.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a before_rows, after_rows and tr blocks. Here's the new structure

screen shot 2017-04-12 at 3 12 35 pm

@@ -56,6 +56,7 @@
from pandas.util.print_versions import show_versions
from pandas.io.api import *
from pandas.util._tester import test
from pandas.formats.style import Styler
Copy link
Contributor

Choose a reason for hiding this comment

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

I do think it needs to be importable somewhere 'public', not sure if top level is the right spot, could argue either way. Do we need a pandas.api.io?

@@ -400,7 +360,7 @@ def format(self, formatter, subset=None):
self._display_funcs[(i, j)] = formatter
return self

def render(self):
def render(self, **kwargs):
"""
Render the built up styles to HTML

Copy link
Contributor

Choose a reason for hiding this comment

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

Add kwargs behavior to docstring

@chris-b1
Copy link
Contributor

chris-b1 commented Apr 9, 2017

From the notebook

For convenience, we provide the styler_factory function that does the same as the custom subclass.

styler_factory -> from_custom_template

@TomAugspurger
Copy link
Contributor Author

TomAugspurger commented Apr 9, 2017 via email

@jreback
Copy link
Contributor

jreback commented Apr 9, 2017

I do think it needs to be importable somewhere 'public', not sure if top level is the right spot, could argue either way. Do we need a pandas.api.io?

yes I think this is the way to go.

@jorisvandenbossche
Copy link
Member

Do we need a pandas.api.io?

We already have a publicly accessible pandas.io, so if we want it in io, I would put it there.

@TomAugspurger
Copy link
Contributor Author

We already have a publicly accessible pandas.io, so if we want it in io, I would put it there.

Yeah, having both would be confusing. Are people ok with pandas.io.Styler? (or would it be pandas.io.api.Styler?)

@codecov
Copy link

codecov bot commented Apr 12, 2017

Codecov Report

Merging #15954 into master will increase coverage by <.01%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master   #15954      +/-   ##
==========================================
+ Coverage      91%      91%   +<.01%     
==========================================
  Files         145      145              
  Lines       49581    49586       +5     
==========================================
+ Hits        45123    45128       +5     
  Misses       4458     4458
Flag Coverage Δ
#multiple 88.77% <100%> (ø) ⬆️
#single 40.57% <53.84%> (-0.01%) ⬇️
Impacted Files Coverage Δ
pandas/formats/style.py 96.28% <100%> (+0.1%) ⬆️
pandas/__init__.py 92.85% <100%> (+0.17%) ⬆️
pandas/core/algorithms.py 94.53% <0%> (-0.05%) ⬇️
pandas/core/base.py 95.51% <0%> (ø) ⬆️
pandas/core/series.py 94.89% <0%> (ø) ⬆️
pandas/indexes/base.py 96.09% <0%> (ø) ⬆️
pandas/core/categorical.py 95.85% <0%> (ø) ⬆️
pandas/tseries/index.py 95.43% <0%> (ø) ⬆️
pandas/util/testing.py 81.13% <0%> (ø) ⬆️

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 c4d71ce...5bff74f. Read the comment docs.

@codecov
Copy link

codecov bot commented Apr 12, 2017

Codecov Report

Merging #15954 into master will decrease coverage by 0.02%.
The diff coverage is 50%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master   #15954      +/-   ##
==========================================
- Coverage   91.02%      91%   -0.03%     
==========================================
  Files         145      146       +1     
  Lines       50391    50416      +25     
==========================================
+ Hits        45870    45880      +10     
- Misses       4521     4536      +15
Flag Coverage Δ
#multiple 88.8% <50%> (-0.03%) ⬇️
#single 40.32% <28.57%> (-0.02%) ⬇️
Impacted Files Coverage Δ
pandas/util/importing.py 0% <0%> (ø)
pandas/formats/style.py 96.28% <100%> (+0.1%) ⬆️
pandas/io/api.py 71.42% <20%> (-28.58%) ⬇️
pandas/core/common.py 90.68% <0%> (-0.35%) ⬇️

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 614a48e...c6765f3. Read the comment docs.

@TomAugspurger
Copy link
Contributor Author

jinja2 being optional makes putting this in the namespace a bit strange. Happy to hear suggestions on ways to make that / the API tests better :/

from jinja2 import Template
from jinja2 import (
PackageLoader, Environment, ChoiceLoader, FileSystemLoader
)
except ImportError:
msg = "pandas.Styler requires jinja2. "\
Copy link
Contributor

Choose a reason for hiding this comment

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

do

try:
     from jinja2 .....
except ImportError:
     class Styler(ImportError):
            pass
     return

will give you a Styler but if you try to use it and jinja2 is not installed will raise I think you can give it a nice message a 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.

@jreback didn't quite work since you can't early-return from a module.

However, this does work:

try:
    from pandas.formats.style import Styler
except:
    from pandas.compat import add_metaclass

    # We want to *not* raise an ImportError upon init
    # We *do* want to raise an ImportError with a custom message
    # when the class is instantiated or subclassed.

    _msg = ("pandas.io.api.Styler requires jinja2. "
            "Please install with `conda install Jinja2` "
            "or `pip install Jinaj2`")

    class _Check(type):
        """Raises import error upon subclassing."""
        def __init__(cls, name, bases, clsdict):
            if len(cls.mro()) > 2:
                raise ImportError(_msg)
            super(_Check, cls).__init__(name, bases, clsdict)

    @add_metaclass(_Check)
    class Styler(object):
        def __init__(self, *args, **kargs):
            raise ImportError(_msg)

Are we ok with a metaclass, just for a nice error message? :D

Copy link
Contributor

Choose a reason for hiding this comment

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

oh if that works great.

@@ -0,0 +1,2 @@
""" Public IO API """
from pandas.formats.style import Styler # noqa
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm fine with having a pandas.api.io, but isn't somewhat the opposite of the recommendation in #13634? Which is the top level modules would be 'public' and internals moved into core - so this should be exposed as pd.io.Styler? (I could be misunderstanding that issue)

Copy link
Contributor

Choose a reason for hiding this comment

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

pandas.io is going to stay, so importing from pandas.io is fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Huh, thought I removed that. Also might have just messed up my rebase. This isn't supposed to be there. It's supposed to be pandas.io.api

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it should be just be pandas.io? The second level api are import * into the top level.

Copy link
Contributor

Choose a reason for hiding this comment

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

pandas.io.api imports * to pandas namespace; so you could make Styler just show up there (or you can leave the from pandas.io.api import * and just del Styler (prob shorter)

Use Environment and PackageLoader to load it

TODO: packaging, __init__.py?

ENH: Add blocks to Styler template

This will make subclassing the Styler and extending
the templates easier.

Jinja2 is optional, so Styler is maybe not there

Nicer import failure

Fixed rebase
@jorisvandenbossche
Copy link
Member

As I said above, we already have public things in pandas.io, so I don't really see the point of creating a new pandas.api.io ?

@jorisvandenbossche
Copy link
Member

Ah, what @chris-b1 said :-)

@jorisvandenbossche
Copy link
Member

Currently, our API documentation lists it as pandas.formats.style.Styler (so when moving, we should deprecate that). So one possibility would be to move the full formats module into pandas.io ? Or move to core and only expose the public ones in pandas.io (or in pandas.io.formats / pandas.io.style).

If we decide to move the formats module (which would certainly be for another PR), we could also just leave Styler where it is for now in this PR, and do the move / change of locations / deprecation in the other PR.

@TomAugspurger
Copy link
Contributor Author

OK, should be good now. Sorry about that.

@jreback
Copy link
Contributor

jreback commented Apr 14, 2017

I am shortly going to move a bunch of things around. But pandas.io is going to stay (pandas.formats will move).

@jreback
Copy link
Contributor

jreback commented Apr 14, 2017

you could move .style under .io if you want.

Copy link
Member

@jorisvandenbossche jorisvandenbossche left a comment

Choose a reason for hiding this comment

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

api.rst will need an update as well, depending on what we decide

@@ -410,6 +370,11 @@ def render(self):
-------
rendered: str
the rendered HTML
**kwargs:
Copy link
Member

Choose a reason for hiding this comment

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

you need to make the docstring a raw string (or escape the *'s), otherwise sphinx will complain :-)

@@ -482,6 +482,9 @@ Other Enhancements
- ``DataFrame.to_excel()`` has a new ``freeze_panes`` parameter to turn on Freeze Panes when exporting to Excel (:issue:`15160`)
- ``pd.read_html()`` will parse multiple header rows, creating a multiindex header. (:issue:`13434`).
- HTML table output skips ``colspan`` or ``rowspan`` attribute if equal to 1. (:issue:`15403`)
- ``pd.io.api.Styler`` template now has blocks for easier extension (:issue:`15649`)
Copy link
Member

Choose a reason for hiding this comment

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

You have an example in the notebook I suppose. Is it possible to put a link here to such an example? Not sure how it works with nbsphinx included files to put links to specific sections

@jreback jreback mentioned this pull request Apr 14, 2017
@TomAugspurger
Copy link
Contributor Author

TomAugspurger commented Apr 14, 2017

You have an example in the notebook I suppose. Is it possible to put a link here to such an example? Not sure how it works with nbsphinx included files to put links to specific sections

@jorisvandenbossche yeah that worked! It's just

:ref`text <notebook.ipynb#section-title>`

# We *do* want to raise an ImportError with a custom message
# when the class is instantiated or subclassed.
@_add_metaclass(_UnSubclassable)
class Styler(object):
Copy link
Contributor

Choose a reason for hiding this comment

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

isn't this jinja2 ?

@@ -49,7 +49,8 @@ class TestPDApi(Base, tm.TestCase):
'Period', 'PeriodIndex', 'RangeIndex', 'UInt64Index',
'Series', 'SparseArray', 'SparseDataFrame',
'SparseSeries', 'TimeGrouper', 'Timedelta',
'TimedeltaIndex', 'Timestamp', 'Interval', 'IntervalIndex']
'TimedeltaIndex', 'Timestamp', 'Interval', 'IntervalIndex',
'Styler']
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 actually del Styler from pandas namespace

@jreback
Copy link
Contributor

jreback commented Apr 14, 2017

@TomAugspurger lgtm. merge when ready.

@jreback
Copy link
Contributor

jreback commented Apr 14, 2017

FYI: http://pandas-docs.github.io/pandas-docs-travis/style.html#Fun-stuff is raising in built dev docs.

@jreback
Copy link
Contributor

jreback commented Apr 14, 2017

ipywidgets I think is needed in doc-build.

@TomAugspurger
Copy link
Contributor Author

ipywidgets I think is needed in doc-build.

Did you see that in one of the travis logs? https://travis-ci.org/pandas-dev/pandas/jobs/222148043 seems to be ok.

@jreback
Copy link
Contributor

jreback commented Apr 14, 2017

@TomAugspurger see the link above, its in the built docs.

@TomAugspurger
Copy link
Contributor Author

Whoops, looked right past that. Yeah I guess they've split that out now.

I can do now, or in a separate PR since all the checks already passed here.

@jreback
Copy link
Contributor

jreback commented Apr 14, 2017

either way
or can add and push then merge

@jreback
Copy link
Contributor

jreback commented Apr 14, 2017

good to go?

pandas/io/api.py Outdated
@@ -17,6 +17,23 @@
from pandas.io.pickle import read_pickle, to_pickle
from pandas.io.packers import read_msgpack, to_msgpack
from pandas.io.gbq import read_gbq
try:
from pandas.formats.style import Styler
except:
Copy link
Contributor

Choose a reason for hiding this comment

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

ImportError

@jreback jreback merged commit 413e2c6 into pandas-dev:master Apr 15, 2017
@jreback
Copy link
Contributor

jreback commented Apr 15, 2017

thanks @TomAugspurger

@jorisvandenbossche
Copy link
Member

Opened a follow-up issue for the remaining issues that were being discussed: #16009

@jreback
Copy link
Contributor

jreback commented Apr 15, 2017

http://pandas-docs.github.io/pandas-docs-travis/style.html

maybe internally should just catch warnings?

/home/travis/miniconda3/envs/pandas/lib/python3.5/site-packages/matplotlib/colors.py:581: RuntimeWarning: invalid value encountered in less
  cbook._putmask(xa, xa < 0.0, -1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
IO HTML read_html, to_html, Styler.apply, Styler.applymap Output-Formatting __repr__ of pandas objects, to_string
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants