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: add PrintOptions context manager to numpy.core. #3987

Closed
wants to merge 3 commits into from
Closed

ENH: add PrintOptions context manager to numpy.core. #3987

wants to merge 3 commits into from

Conversation

NeilGirdhar
Copy link
Contributor

Instantiating PrintOptions returns an object that is both:

  • a context manager for setting printing options, and
  • a Mapping exposing the current printing options.

Instead of using a PrintOptions object as a context manager, the
apply method can be called, which will apply the options
specified in the constructor. If the formatter printing option is not
specified, it is reset.

Printing options determine the way floating point numbers, arrays
and other NumPy objects are displayed.

Instantiating ``PrintOptions`` returns an object that is both:
* a context manager for setting printing options, and
* a Mapping exposing the current printing options.

Instead of using a ``PrintOptions`` object as a context manager, the
``apply`` method can be called, which will apply the options
specified in the constructor.  If the formatter printing option is not
specified, it is reset.

Printing options determine the way floating point numbers, arrays
and other NumPy objects are displayed.
)


class PrintOptions(Mapping):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Deriving from Mapping instead of dict so that the returned dictionary is read-only.

Copy link
Member

Choose a reason for hiding this comment

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

If this is important to know when reading the code then it should be in a
comment in the code :-)
On 28 Oct 2013 02:02, "Neil" notifications@github.com wrote:

In numpy/core/arrayprint.py:

-_summaryThreshold = 1000 # total items > triggers array summarization
+# TODO: A ChainMap (Python 3.3+) would be more elegant than a dict for
+# implementing PrintOptions.
+_printoptions = dict(

  • edgeitems=3, # repr N leading and trailing items of each dimension
  • threshold=1000, # total items > triggers array summarization
  • precision=8,
  • suppress=False,
  • linewidth=75,
  • nanstr='nan',
  • infstr='inf',
  • formatter=None, # formatting function for array elements
  • )

+class PrintOptions(Mapping):

Deriving from Mapping instead of dict so that the returned dictionary is
read-only.


Reply to this email directly or view it on GitHubhttps://github.com//pull/3987/files#r7239070
.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you. I have updated the code with the comment.

@charris
Copy link
Member

charris commented Nov 14, 2013

I like the context manager idea although I haven't had time to look closely at the code yet. What is the reason to combine the context manager and an apply function in the same class? It seems a strange combination.

The fixes should be squashed with the original commit. It probably would have been better to separate the creation of the context manager and its use into two separate commits, but we can live with a single commit if you don't want the hassle of breaking the commit up.

@NeilGirdhar
Copy link
Contributor Author

Hi Charles. I'm not enamored with the apply method either. My thought was to design PrintOptions so that it encapsulates a set of printing options and supplants the existing set_printoptions and get_printoptions functions because I would like to see (in the very long run) the interface to numpy be as lean as possible. There seem to be cases where someone wants to just set the printing options rather than temporarily setting them and the apply method gives a way of doing that.

I am happy to squash all of the changes into one commit or separate them into two commits. I'm not proficient enough with git to know exactly how to do that, but please let me know what you want and I'll figure it out.

@WarrenWeckesser
Copy link
Member

WarrenWeckesser commented Oct 28, 2017

For comparison, here's a minimal implementation of just a context manager:

from contextlib import contextmanager
import numpy as np


@contextmanager
def printoptions(**kwds):
    # TO DO: docstring...
    opts = np.get_printoptions()
    np.set_printoptions(**kwds)
    yield
    np.set_printoptions(**opts)

That would be enough for my use-cases, but if folks like the direction taken in this pull request, I would be fine with that, too. Whatever the implementation, it would be nice to get this functionality into numpy.

Edit: See the improved versions by @eric-wieser and @NeilGirdhar below.

@eric-wieser
Copy link
Member

eric-wieser commented Oct 28, 2017

@WarrenWeckesser: That minimal implementation is buggy, and should be

@contextmanager
def printoptions(**kwds):
    # TO DO: docstring...
    opts = np.get_printoptions()
    np.set_printoptions(**kwds)
    try:
        yield
    finally:
        np.set_printoptions(**opts)

There's also a bunch of cleanup in this PR that has appeared over and over again, that I think was finally merged elsewhere

I'm +1 on something simple like that, although #9444 means that changes like this give users a false sense of security when using with

@NeilGirdhar
Copy link
Contributor Author

NeilGirdhar commented Oct 28, 2017

I'm not sure why the sudden renewed interest as I recently deleted my forked repo and changelist. Doesn't it make more sense to wait until PEP 550 or PEP 555 is accepted and then build this feature onto that?

@WarrenWeckesser
Copy link
Member

@eric-wieser, @NeilGirdhar Thanks for the improved minimal version.

I don't know about any sudden renewed interest. I was just messing around with the print options, and wondered why there wasn't a context manager for them yet. I'm sure I'm not the only one who misses that functionality.

PEP 550 is still in "draft" status, and PEP 555 has been withdrawn. Implementing a context manager now means we could have it in the next release of numpy, and it would work for all the supported versions of Python.

@eric-wieser
Copy link
Member

eric-wieser commented Oct 28, 2017

I think a simple context manager with a warning in the documentation that it may not clean up properly when used in multiple threads or coroutines should be enough. (Note that np.seterr at least uses separate state for each thread)

@NeilGirdhar
Copy link
Contributor Author

NeilGirdhar commented Oct 28, 2017

@WarrenWeckesser Good point on having something ready now.

Can I make a bold suggestion? Mark set_printoptions as deprecated in favor of the context manager version. The reason is that these sort of "set" option functions leak state from one block to another and except in the simplest case where you call set_printoptions at the very start of your program, you want the context manager version. This has been a sore point in the debate between PEP 555 (exposes the context manager as its principal interface) and PEP 550 (exposes set and get as its principal interface).

I have a feeling that there is too much code using set_printoptions to deprecate it. So, at least, can we explain in the documentation that the context manager version should be preferred where possible? (Similarly, I also think errstate should be preferred over seterr and explained as such in the documentation.)

A lot of times these set functions are called in tests, when the context manager versions can be just as easily called in a test fixture. E.g., using pytest:

@pytest.fixture
def ignore_numpy_errors(scope='module'):
    with np.errstate(all='ignore'):
        yield

@WarrenWeckesser
Copy link
Member

I have a feeling that there is too much code using set_printoptions to deprecate it.

That's probably true. I've even used it in the docstrings of some scipy functions; suppress=True and precision=3 can eliminate a lot of printed output that is not interesting.

I also sometimes use set_printoptions in ipython. If I know I'm going to be working with arrays where I don't need to see the full precision, precision=<some small number> makes the array output much easier to read. Having only a context manager wouldn't work well in that case.

@charris
Copy link
Member

charris commented Nov 5, 2017

If we are going to do this for 1.14 it should be soon. It would be nice to get it in together with all the other print changes.

@charris charris modified the milestones: 1.14.0 release, 1.15.0 release Nov 5, 2017
@charris
Copy link
Member

charris commented Nov 11, 2017

Pushed off to 1.15.

@eric-wieser
Copy link
Member

Most likely closing this in favor of #10406, for anyone watching.

@charris
Copy link
Member

charris commented Jan 20, 2018

OK, I merged #10406, so closing this. @ev-br Might add something to doc/numpybook/runcode.py.

@NeilGirdhar I apologize for letting this slip for so long. It was always in the back of my mind, and suddenly it is four years later. Thanks for the effort.

@charris charris closed this Jan 20, 2018
@NeilGirdhar
Copy link
Contributor Author

@charris I'm just happy this got done!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants