From 0d808d49104c0b97b2f062af37cadf4b63cfd72d Mon Sep 17 00:00:00 2001 From: Keewis Date: Tue, 21 Apr 2020 12:59:44 +0200 Subject: [PATCH 01/20] ad a property-like descriptor that works both on objects and classes --- xarray/core/utils.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/xarray/core/utils.py b/xarray/core/utils.py index 1126cf3037f..e918fdb1bc4 100644 --- a/xarray/core/utils.py +++ b/xarray/core/utils.py @@ -787,6 +787,14 @@ def drop_dims_from_indexers( ) +class property_: + def __init__(self, func): + self._func = func + + def __get__(self, obj, cls): + return self._func(obj) + + # Singleton type, as per https://github.com/python/typing/pull/240 class Default(Enum): token = 0 From 8b60a2a4aeb66657d4168e98ee9988041cd437d2 Mon Sep 17 00:00:00 2001 From: Keewis Date: Tue, 21 Apr 2020 13:05:11 +0200 Subject: [PATCH 02/20] generate documentation for the plotting accessor methods --- doc/api.rst | 26 ++++++++++++++++++++++++++ xarray/core/dataarray.py | 2 +- xarray/core/dataset.py | 2 +- xarray/plot/plot.py | 2 +- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index b37c84e7a81..12708033efd 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -679,6 +679,32 @@ Plotting plot.step plot.FacetGrid + +Dataset methods +--------------- + +.. autosummary:: + :toctree: generated/ + + Dataset.plot.scatter + + +DataArray methods +----------------- + +.. autosummary:: + :toctree: generated/ + + DataArray.plot + DataArray.plot.contourf + DataArray.plot.contour + DataArray.plot.hist + DataArray.plot.imshow + DataArray.plot.line + DataArray.plot.pcolormesh + DataArray.plot.step + + Faceting -------- .. autosummary:: diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index ffa05ca64f0..6ad5582a516 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -2680,7 +2680,7 @@ def func(self, other): def _copy_attrs_from(self, other: Union["DataArray", Dataset, Variable]) -> None: self.attrs = other.attrs - @property + @utils.property_ def plot(self) -> _PlotMethods: """ Access plotting functions for DataArray's diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index d811d54847f..971d740c66b 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -5551,7 +5551,7 @@ def real(self): def imag(self): return self._unary_op(lambda x: x.imag, keep_attrs=True)(self) - @property + @utils.property_ def plot(self): """ Access plotting functions for Datasets. diff --git a/xarray/plot/plot.py b/xarray/plot/plot.py index 4657bee9415..8cdbf53182b 100644 --- a/xarray/plot/plot.py +++ b/xarray/plot/plot.py @@ -438,7 +438,7 @@ class _PlotMethods: For example, DataArray.plot.imshow """ - __slots__ = ("_da",) + # __slots__ = ("_da",) def __init__(self, darray): self._da = darray From 4f3780f268d0586b295cc163483264b565b94b02 Mon Sep 17 00:00:00 2001 From: Keewis Date: Tue, 21 Apr 2020 22:53:51 +0200 Subject: [PATCH 03/20] add a docstring to the custom property-like descriptor --- xarray/core/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xarray/core/utils.py b/xarray/core/utils.py index e918fdb1bc4..8fde74adc2d 100644 --- a/xarray/core/utils.py +++ b/xarray/core/utils.py @@ -788,6 +788,8 @@ def drop_dims_from_indexers( class property_: + """ Acts like a property, but on both classes and class instances """ + def __init__(self, func): self._func = func From 001c8d0e755a1869524984edcbfb58c0f2988f93 Mon Sep 17 00:00:00 2001 From: Keewis Date: Wed, 22 Apr 2020 12:34:55 +0200 Subject: [PATCH 04/20] use the accessor syntax in the main plotting section --- doc/api.rst | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 12708033efd..c2a2d8e2fda 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -667,34 +667,8 @@ Plotting :toctree: generated/ Dataset.plot - plot.scatter - DataArray.plot - plot.plot - plot.contourf - plot.contour - plot.hist - plot.imshow - plot.line - plot.pcolormesh - plot.step - plot.FacetGrid - - -Dataset methods ---------------- - -.. autosummary:: - :toctree: generated/ - Dataset.plot.scatter - - -DataArray methods ------------------ - -.. autosummary:: - :toctree: generated/ - + plot.plot DataArray.plot DataArray.plot.contourf DataArray.plot.contour From ee183b03540c5bb5bf5d8358508a4bdceb93b794 Mon Sep 17 00:00:00 2001 From: Keewis Date: Thu, 23 Apr 2020 00:59:57 +0200 Subject: [PATCH 05/20] explain why we need a custom property class --- xarray/core/utils.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/xarray/core/utils.py b/xarray/core/utils.py index 8fde74adc2d..4732ff22e11 100644 --- a/xarray/core/utils.py +++ b/xarray/core/utils.py @@ -788,7 +788,12 @@ def drop_dims_from_indexers( class property_: - """ Acts like a property, but on both classes and class instances """ + """ Acts like a property, but on both classes and class instances + + This class is necessary because some tools (e.g. pydoc and sphinx) + inspect classes where property returns itself and not the + accessor. + """ def __init__(self, func): self._func = func From c96663fb88c10b77122b085f8ca1c0c0887730f3 Mon Sep 17 00:00:00 2001 From: Keewis Date: Thu, 23 Apr 2020 01:02:42 +0200 Subject: [PATCH 06/20] rename the custom property to UncachedAccessor to match the behavior of _CachedAccessor, it also accepts the accessor class (not the object). We lose the ability for custom docstrings, though. --- xarray/core/dataarray.py | 19 +------------------ xarray/core/dataset.py | 11 +---------- xarray/core/utils.py | 13 ++++++++----- 3 files changed, 10 insertions(+), 33 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 6ad5582a516..d253a6cd01d 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -2680,24 +2680,7 @@ def func(self, other): def _copy_attrs_from(self, other: Union["DataArray", Dataset, Variable]) -> None: self.attrs = other.attrs - @utils.property_ - def plot(self) -> _PlotMethods: - """ - Access plotting functions for DataArray's - - >>> d = xr.DataArray([[1, 2], [3, 4]]) - - For convenience just call this directly - - >>> d.plot() - - Or use it as a namespace to use xarray.plot functions as - DataArray methods - - >>> d.plot.imshow() # equivalent to xarray.plot.imshow(d) - - """ - return _PlotMethods(self) + plot = utils.UncachedAccessor(_PlotMethods) def _title_for_slice(self, truncate: int = 50) -> str: """ diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 971d740c66b..5c36207ae5c 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -5551,16 +5551,7 @@ def real(self): def imag(self): return self._unary_op(lambda x: x.imag, keep_attrs=True)(self) - @utils.property_ - def plot(self): - """ - Access plotting functions for Datasets. - Use it as a namespace to use xarray.plot functions as Dataset methods - - >>> ds.plot.scatter(...) # equivalent to xarray.plot.scatter(ds,...) - - """ - return _Dataset_PlotMethods(self) + plot = utils.UncachedAccessor(_Dataset_PlotMethods) def filter_by_attrs(self, **kwargs): """Returns a ``Dataset`` with variables that match specific conditions. diff --git a/xarray/core/utils.py b/xarray/core/utils.py index 4732ff22e11..0542f850b02 100644 --- a/xarray/core/utils.py +++ b/xarray/core/utils.py @@ -787,19 +787,22 @@ def drop_dims_from_indexers( ) -class property_: +class UncachedAccessor: """ Acts like a property, but on both classes and class instances This class is necessary because some tools (e.g. pydoc and sphinx) - inspect classes where property returns itself and not the + inspect classes for which property returns itself and not the accessor. """ - def __init__(self, func): - self._func = func + def __init__(self, accessor): + self._accessor = accessor def __get__(self, obj, cls): - return self._func(obj) + if obj is None: + return self._accessor + + return self._accessor(obj) # Singleton type, as per https://github.com/python/typing/pull/240 From 261c69f65c1ccdcc82cbe962a1af6b2eac2d5c43 Mon Sep 17 00:00:00 2001 From: Keewis Date: Thu, 23 Apr 2020 01:14:39 +0200 Subject: [PATCH 07/20] declare that __call__ wraps plot --- xarray/plot/plot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/xarray/plot/plot.py b/xarray/plot/plot.py index 8cdbf53182b..9340bba28d1 100644 --- a/xarray/plot/plot.py +++ b/xarray/plot/plot.py @@ -443,6 +443,7 @@ class _PlotMethods: def __init__(self, darray): self._da = darray + @functools.wraps(plot) def __call__(self, **kwargs): return plot(self._da, **kwargs) From 6ba1b5227ba9ac0bfd84d16653b19557d298d909 Mon Sep 17 00:00:00 2001 From: Keewis Date: Thu, 23 Apr 2020 13:24:43 +0200 Subject: [PATCH 08/20] add accessor tests --- xarray/tests/test_plot.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/xarray/tests/test_plot.py b/xarray/tests/test_plot.py index bf1f9ed60bb..6bec6bf7037 100644 --- a/xarray/tests/test_plot.py +++ b/xarray/tests/test_plot.py @@ -111,6 +111,12 @@ class TestPlot(PlotTestCase): def setup_array(self): self.darray = DataArray(easy_array((2, 3, 4))) + def test_accessor(self): + from ..plot.plot import _PlotMethods + + assert DataArray.plot is _PlotMethods + assert isinstance(self.darray.plot, _PlotMethods) + def test_label_from_attrs(self): da = self.darray.copy() assert "" == label_from_attrs(da) @@ -2048,6 +2054,12 @@ def setUp(self): ds.B.attrs["units"] = "Bunits" self.ds = ds + def test_accessor(self): + from ..plot.dataset_plot import _Dataset_PlotMethods + + assert Dataset.plot is _Dataset_PlotMethods + assert isinstance(self.ds.plot, _Dataset_PlotMethods) + @pytest.mark.parametrize( "add_guide, hue_style, legend, colorbar", [ From eb54df8416add1f60f3359a78aa1cb84149e1759 Mon Sep 17 00:00:00 2001 From: Keewis Date: Fri, 8 May 2020 00:58:13 +0200 Subject: [PATCH 09/20] add the autosummary templates from pandas --- doc/_templates/autosummary/accessor.rst | 6 + .../autosummary/accessor_attribute.rst | 6 + .../autosummary/accessor_callable.rst | 6 + .../autosummary/accessor_method.rst | 6 + doc/conf.py | 114 ++++++++++++++++++ 5 files changed, 138 insertions(+) create mode 100644 doc/_templates/autosummary/accessor.rst create mode 100644 doc/_templates/autosummary/accessor_attribute.rst create mode 100644 doc/_templates/autosummary/accessor_callable.rst create mode 100644 doc/_templates/autosummary/accessor_method.rst diff --git a/doc/_templates/autosummary/accessor.rst b/doc/_templates/autosummary/accessor.rst new file mode 100644 index 00000000000..4ba745cd6fd --- /dev/null +++ b/doc/_templates/autosummary/accessor.rst @@ -0,0 +1,6 @@ +{{ fullname }} +{{ underline }} + +.. currentmodule:: {{ module.split('.')[0] }} + +.. autoaccessor:: {{ (module.split('.')[1:] + [objname]) | join('.') }} diff --git a/doc/_templates/autosummary/accessor_attribute.rst b/doc/_templates/autosummary/accessor_attribute.rst new file mode 100644 index 00000000000..b5ad65d6a73 --- /dev/null +++ b/doc/_templates/autosummary/accessor_attribute.rst @@ -0,0 +1,6 @@ +{{ fullname }} +{{ underline }} + +.. currentmodule:: {{ module.split('.')[0] }} + +.. autoaccessorattribute:: {{ (module.split('.')[1:] + [objname]) | join('.') }} diff --git a/doc/_templates/autosummary/accessor_callable.rst b/doc/_templates/autosummary/accessor_callable.rst new file mode 100644 index 00000000000..7a3301814f5 --- /dev/null +++ b/doc/_templates/autosummary/accessor_callable.rst @@ -0,0 +1,6 @@ +{{ fullname }} +{{ underline }} + +.. currentmodule:: {{ module.split('.')[0] }} + +.. autoaccessorcallable:: {{ (module.split('.')[1:] + [objname]) | join('.') }}.__call__ diff --git a/doc/_templates/autosummary/accessor_method.rst b/doc/_templates/autosummary/accessor_method.rst new file mode 100644 index 00000000000..aefbba6ef1b --- /dev/null +++ b/doc/_templates/autosummary/accessor_method.rst @@ -0,0 +1,6 @@ +{{ fullname }} +{{ underline }} + +.. currentmodule:: {{ module.split('.')[0] }} + +.. autoaccessormethod:: {{ (module.split('.')[1:] + [objname]) | join('.') }} diff --git a/doc/conf.py b/doc/conf.py index 578f9cf550d..fddd71b14bc 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -359,3 +359,117 @@ "dask": ("https://docs.dask.org/en/latest", None), "cftime": ("https://unidata.github.io/cftime", None), } + +# --------- autosummary templates ------------------ +# TODO: eventually replace this with a sphinx.ext.auto_accessor module +import sphinx +from sphinx.util import rpartition +from sphinx.ext.autodoc import Documenter, MethodDocumenter, AttributeDocumenter + + +class AccessorDocumenter(MethodDocumenter): + """ + Specialized Documenter subclass for accessors. + """ + + objtype = "accessor" + directivetype = "method" + + # lower than MethodDocumenter so this is not chosen for normal methods + priority = 0.6 + + def format_signature(self): + # this method gives an error/warning for the accessors, therefore + # overriding it (accessor has no arguments) + return "" + + +class AccessorLevelDocumenter(Documenter): + """ + Specialized Documenter subclass for objects on accessor level (methods, + attributes). + """ + + # This is the simple straightforward version + # modname is None, base the last elements (eg 'hour') + # and path the part before (eg 'Series.dt') + # def resolve_name(self, modname, parents, path, base): + # modname = 'pandas' + # mod_cls = path.rstrip('.') + # mod_cls = mod_cls.split('.') + # + # return modname, mod_cls + [base] + + def resolve_name(self, modname, parents, path, base): + if modname is None: + if path: + mod_cls = path.rstrip(".") + else: + mod_cls = None + # if documenting a class-level object without path, + # there must be a current class, either from a parent + # auto directive ... + mod_cls = self.env.temp_data.get("autodoc:class") + # ... or from a class directive + if mod_cls is None: + mod_cls = self.env.temp_data.get("py:class") + # ... if still None, there's no way to know + if mod_cls is None: + return None, [] + # HACK: this is added in comparison to ClassLevelDocumenter + # mod_cls still exists of class.accessor, so an extra + # rpartition is needed + modname, accessor = rpartition(mod_cls, ".") + modname, cls = rpartition(modname, ".") + parents = [cls, accessor] + # if the module name is still missing, get it like above + if not modname: + modname = self.env.temp_data.get("autodoc:module") + if not modname: + if sphinx.__version__ > "1.3": + modname = self.env.ref_context.get("py:module") + else: + modname = self.env.temp_data.get("py:module") + # ... else, it stays None, which means invalid + return modname, parents + [base] + + +class AccessorAttributeDocumenter(AccessorLevelDocumenter, AttributeDocumenter): + + objtype = "accessorattribute" + directivetype = "attribute" + + # lower than AttributeDocumenter so this is not chosen for normal attributes + priority = 0.6 + + +class AccessorMethodDocumenter(AccessorLevelDocumenter, MethodDocumenter): + + objtype = "accessormethod" + directivetype = "method" + + # lower than MethodDocumenter so this is not chosen for normal methods + priority = 0.6 + + +class AccessorCallableDocumenter(AccessorLevelDocumenter, MethodDocumenter): + """ + This documenter lets us removes .__call__ from the method signature for + callable accessors like Series.plot + """ + + objtype = "accessorcallable" + directivetype = "method" + + # lower than MethodDocumenter; otherwise the doc build prints warnings + priority = 0.5 + + def format_name(self): + return MethodDocumenter.format_name(self).rstrip(".__call__") + + +def setup(app): + app.add_autodocumenter(AccessorDocumenter) + app.add_autodocumenter(AccessorAttributeDocumenter) + app.add_autodocumenter(AccessorMethodDocumenter) + app.add_autodocumenter(AccessorCallableDocumenter) From b2b543288a677d64a9e74bffd2cee3964bcbfd1a Mon Sep 17 00:00:00 2001 From: Keewis Date: Fri, 8 May 2020 01:00:37 +0200 Subject: [PATCH 10/20] update the plotting section to use the accessor templates --- doc/api.rst | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index c2a2d8e2fda..a0dc37df3fc 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -665,11 +665,22 @@ Plotting .. autosummary:: :toctree: generated/ + :template: autosummary/accessor.rst + DataArray.plot Dataset.plot - Dataset.plot.scatter - plot.plot + +.. autosummary:: + :toctree: generated/ + :template: autosummary/accessor_callable.rst + DataArray.plot + +.. autosummary:: + :toctree: generated/ + :template: autosummary/accessor_method.rst + + Dataset.plot.scatter DataArray.plot.contourf DataArray.plot.contour DataArray.plot.hist From c9333652cb18b372500a6e55466cca868f920106 Mon Sep 17 00:00:00 2001 From: Keewis Date: Fri, 8 May 2020 01:32:00 +0200 Subject: [PATCH 11/20] remove the separate callable section --- doc/api.rst | 6 ------ 1 file changed, 6 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 26c25ff3d14..897371ac986 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -671,12 +671,6 @@ Plotting DataArray.plot Dataset.plot -.. autosummary:: - :toctree: generated/ - :template: autosummary/accessor_callable.rst - - DataArray.plot - .. autosummary:: :toctree: generated/ :template: autosummary/accessor_method.rst From a456bd14db7e34e048a500353c75e9ce9581f20a Mon Sep 17 00:00:00 2001 From: Keewis Date: Fri, 8 May 2020 02:08:54 +0200 Subject: [PATCH 12/20] fix the import order --- doc/conf.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index fddd71b14bc..1e114cf1508 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -20,6 +20,12 @@ import sys from contextlib import suppress +# --------- autosummary templates ------------------ +# TODO: eventually replace this with a sphinx.ext.auto_accessor module +import sphinx +from sphinx.ext.autodoc import AttributeDocumenter, Documenter, MethodDocumenter +from sphinx.util import rpartition + # make sure the source version is preferred (#3567) root = pathlib.Path(__file__).absolute().parent.parent os.environ["PYTHONPATH"] = str(root) @@ -360,13 +366,9 @@ "cftime": ("https://unidata.github.io/cftime", None), } + # --------- autosummary templates ------------------ # TODO: eventually replace this with a sphinx.ext.auto_accessor module -import sphinx -from sphinx.util import rpartition -from sphinx.ext.autodoc import Documenter, MethodDocumenter, AttributeDocumenter - - class AccessorDocumenter(MethodDocumenter): """ Specialized Documenter subclass for accessors. From 08cb916faf2bccb945bf7577b06fcc8ef198e460 Mon Sep 17 00:00:00 2001 From: Keewis Date: Thu, 14 May 2020 23:39:27 +0200 Subject: [PATCH 13/20] add the DataArray.str accessor as a new subsection --- doc/api.rst | 50 ++++++++++++++++++++++++++++++++++++++++ xarray/core/dataarray.py | 2 +- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/doc/api.rst b/doc/api.rst index 897371ac986..1161c3f7397 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -400,6 +400,56 @@ Computation :py:attr:`~core.groupby.DataArrayGroupBy.where` :py:attr:`~core.groupby.DataArrayGroupBy.quantile` + +String manipulation +------------------- + +.. autosummary:: + :toctree: generated/ + :template: autosummary/accessor_method.rst + + DataArray.str.capitalize + DataArray.str.center + DataArray.str.contains + DataArray.str.count + DataArray.str.decode + DataArray.str.encode + DataArray.str.endswith + DataArray.str.find + DataArray.str.get + DataArray.str.index + DataArray.str.isalnum + DataArray.str.isalpha + DataArray.str.isdecimal + DataArray.str.isdigit + DataArray.str.isnumeric + DataArray.str.isspace + DataArray.str.istitle + DataArray.str.isupper + DataArray.str.len + DataArray.str.ljust + DataArray.str.lower + DataArray.str.lstrip + DataArray.str.match + DataArray.str.pad + DataArray.str.repeat + DataArray.str.replace + DataArray.str.rfind + DataArray.str.rindex + DataArray.str.rjust + DataArray.str.rstrip + DataArray.str.slice + DataArray.str.slice_replace + DataArray.str.startswith + DataArray.str.strip + DataArray.str.swapcase + DataArray.str.title + DataArray.str.translate + DataArray.str.upper + DataArray.str.wrap + DataArray.str.zfill + + Reshaping and reorganizing -------------------------- diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 875624a6bf5..b3c56872436 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -3716,7 +3716,7 @@ def idxmax( # this needs to be at the end, or mypy will confuse with `str` # https://mypy.readthedocs.io/en/latest/common_issues.html#dealing-with-conflicting-names - str = property(StringAccessor) + str = utils.UncachedAccessor(StringAccessor) # priority most be higher than Variable to properly work with binary ufuncs From eaf95c4d9f54a4d46ade2f222abdd6616e3913b6 Mon Sep 17 00:00:00 2001 From: Keewis Date: Tue, 2 Jun 2020 00:08:26 +0200 Subject: [PATCH 14/20] add the datetime accessor to the main api page --- doc/api.rst | 66 ++++++++++++++++++++++++++++++++++++++++ xarray/core/dataarray.py | 2 +- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/doc/api.rst b/doc/api.rst index 886289fe91a..9d1f282c3b4 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -452,6 +452,72 @@ String manipulation DataArray.str.wrap DataArray.str.zfill +Datetimelike properties +----------------------- + +**Datetime properties**: + +.. autosummary:: + :toctree: generated/ + :template: autosummary/accessor_attribute.rst + + DataArray.dt.year + DataArray.dt.month + DataArray.dt.day + DataArray.dt.hour + DataArray.dt.minute + DataArray.dt.second + DataArray.dt.microsecond + DataArray.dt.nanosecond + DataArray.dt.weekofyear + DataArray.dt.week + DataArray.dt.dayofweek + DataArray.dt.weekday + DataArray.dt.weekday_name + DataArray.dt.dayofyear + DataArray.dt.quarter + DataArray.dt.days_in_month + DataArray.dt.daysinmonth + DataArray.dt.season + DataArray.dt.time + DataArray.dt.is_month_start + DataArray.dt.is_month_end + DataArray.dt.is_quarter_end + DataArray.dt.is_year_start + DataArray.dt.is_leap_year + +**Datetime methods**: + +.. autosummary:: + :toctree: generated/ + :template: autosummary/accessor_method.rst + + DataArray.dt.floor + DataArray.dt.ceil + DataArray.dt.round + DataArray.dt.strftime + +**Timedelta properties**: + +.. autosummary:: + :toctree: generated/ + :template: autosummary/accessor_attribute.rst + + DataArray.dt.days + DataArray.dt.seconds + DataArray.dt.microseconds + DataArray.dt.nanoseconds + +**Timedelta methods**: + +.. autosummary:: + :toctree: generated/ + :template: autosummary/accessor_method.rst + + DataArray.dt.floor + DataArray.dt.ceil + DataArray.dt.round + Reshaping and reorganizing -------------------------- diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index b3c56872436..98e4cfb8543 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -260,7 +260,7 @@ class DataArray(AbstractArray, DataWithCoords): _resample_cls = resample.DataArrayResample _weighted_cls = weighted.DataArrayWeighted - dt = property(CombinedDatetimelikeAccessor) + dt = utils.UncachedAccessor(CombinedDatetimelikeAccessor) def __init__( self, From 494c4f18819731eaf8df0f0e9815f764aba3c79a Mon Sep 17 00:00:00 2001 From: Keewis Date: Tue, 2 Jun 2020 00:55:18 +0200 Subject: [PATCH 15/20] move the plotting functions into the DataArray / Dataset sections --- doc/api.rst | 66 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 9d1f282c3b4..2669574f5df 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -233,6 +233,21 @@ Reshaping and reorganizing Dataset.sortby Dataset.broadcast_like +Plotting +-------- + +.. autosummary:: + :toctree: generated/ + :template: autosummary/accessor.rst + + Dataset.plot + +.. autosummary:: + :toctree: generated/ + :template: autosummary/accessor_method.rst + + Dataset.plot.scatter + DataArray ========= @@ -535,6 +550,33 @@ Reshaping and reorganizing DataArray.sortby DataArray.broadcast_like +Plotting +-------- + +.. autosummary:: + :toctree: generated/ + :template: autosummary/accessor.rst + + DataArray.plot + +.. autosummary:: + :toctree: generated/ + :template: autosummary/accessor_callable.rst + + DataArray.plot + +.. autosummary:: + :toctree: generated/ + :template: autosummary/accessor_method.rst + + DataArray.plot.contourf + DataArray.plot.contour + DataArray.plot.hist + DataArray.plot.imshow + DataArray.plot.line + DataArray.plot.pcolormesh + DataArray.plot.step + .. _api.ufuncs: Universal functions @@ -780,30 +822,6 @@ Creating custom indexes cftime_range -Plotting -======== - -.. autosummary:: - :toctree: generated/ - :template: autosummary/accessor.rst - - DataArray.plot - Dataset.plot - -.. autosummary:: - :toctree: generated/ - :template: autosummary/accessor_method.rst - - Dataset.plot.scatter - DataArray.plot.contourf - DataArray.plot.contour - DataArray.plot.hist - DataArray.plot.imshow - DataArray.plot.line - DataArray.plot.pcolormesh - DataArray.plot.step - - Faceting -------- .. autosummary:: From 1e45275e4aa011c1889e01580d2cffae5a2102bf Mon Sep 17 00:00:00 2001 From: Keewis Date: Tue, 2 Jun 2020 13:40:35 +0200 Subject: [PATCH 16/20] remove the documentation of the accessor class itself --- doc/api.rst | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 2669574f5df..bb0edd0dfa5 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -236,12 +236,6 @@ Reshaping and reorganizing Plotting -------- -.. autosummary:: - :toctree: generated/ - :template: autosummary/accessor.rst - - Dataset.plot - .. autosummary:: :toctree: generated/ :template: autosummary/accessor_method.rst @@ -553,12 +547,6 @@ Reshaping and reorganizing Plotting -------- -.. autosummary:: - :toctree: generated/ - :template: autosummary/accessor.rst - - DataArray.plot - .. autosummary:: :toctree: generated/ :template: autosummary/accessor_callable.rst From 371bd0f98d4917771f02f85564182e3fdf8e7afc Mon Sep 17 00:00:00 2001 From: Keewis Date: Tue, 2 Jun 2020 13:41:38 +0200 Subject: [PATCH 17/20] manually copy the docstring since functools.wraps does more than that --- xarray/plot/plot.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xarray/plot/plot.py b/xarray/plot/plot.py index d3f83c269f3..776177fbedf 100644 --- a/xarray/plot/plot.py +++ b/xarray/plot/plot.py @@ -442,10 +442,13 @@ class _PlotMethods: def __init__(self, darray): self._da = darray - @functools.wraps(plot) def __call__(self, **kwargs): return plot(self._da, **kwargs) + # we can't use functools.wraps here since that doesn't only modify + # the docstring + __doc__ = __call__.__doc__ = plot.__doc__ + @functools.wraps(hist) def hist(self, ax=None, **kwargs): return hist(self._da, ax=ax, **kwargs) From 5cd9f12a430a88d3f19d3277472523e8be13aacc Mon Sep 17 00:00:00 2001 From: Keewis Date: Tue, 2 Jun 2020 14:31:41 +0200 Subject: [PATCH 18/20] also copy the annotations and mark __call__ as wrapping plot --- xarray/plot/plot.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/xarray/plot/plot.py b/xarray/plot/plot.py index 776177fbedf..34e4f423f8f 100644 --- a/xarray/plot/plot.py +++ b/xarray/plot/plot.py @@ -445,9 +445,10 @@ def __init__(self, darray): def __call__(self, **kwargs): return plot(self._da, **kwargs) - # we can't use functools.wraps here since that doesn't only modify - # the docstring + # we can't use functools.wraps here since that also modifies the name / qualname __doc__ = __call__.__doc__ = plot.__doc__ + __call__.__wrapped__ = plot # type: ignore + __call__.__annotations__ = plot.__annotations__ @functools.wraps(hist) def hist(self, ax=None, **kwargs): From 7df80680ee7f6e2c6b43836979901de74792f005 Mon Sep 17 00:00:00 2001 From: Keewis Date: Tue, 2 Jun 2020 14:34:12 +0200 Subject: [PATCH 19/20] re-enable __slots__ --- xarray/plot/plot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/plot/plot.py b/xarray/plot/plot.py index 34e4f423f8f..e4a981daf8c 100644 --- a/xarray/plot/plot.py +++ b/xarray/plot/plot.py @@ -437,7 +437,7 @@ class _PlotMethods: For example, DataArray.plot.imshow """ - # __slots__ = ("_da",) + __slots__ = ("_da",) def __init__(self, darray): self._da = darray From 6fb736b95e7c644a1eaf47ee2fc0ffd793990b42 Mon Sep 17 00:00:00 2001 From: Keewis Date: Mon, 8 Jun 2020 23:58:22 +0200 Subject: [PATCH 20/20] update whats-new.rst --- doc/whats-new.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index e8e30917cff..e74d8bf3874 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -168,6 +168,8 @@ Documentation By `Justus Magin `_. - Narrative documentation now describes :py:meth:`map_blocks`. :ref:`dask.automatic-parallelization`. By `Deepak Cherian `_. +- Document accessors the way they are called. (:issue:`3625`, :pull:`3988`) + By `Justus Magin `_. Internal Changes ~~~~~~~~~~~~~~~~