reduce pollution of pg namespace#1846
Conversation
pyqtgraph/colormap.py
Outdated
| @@ -1,3 +1,7 @@ | |||
| __all__ = [ | |||
| # 'listMaps', 'get', | |||
| 'getFromMatplotlib', 'getFromColorcet', 'makeMonochrome', 'modulatedBarData', 'ColorMap'] | |||
There was a problem hiding this comment.
I think
__all__ = ['ColorMap']
should be sufficient here. The function names were chosen to live in a separate colormap namespace and don't make much sense as e.g. pg.getFromColorcet (or even pg.get)
To me it seems cleaner to have
pg.colormap.get('CET1')
rather than
pg.getColormap('CET1')
and it doesn't require any more typing either.
There was a problem hiding this comment.
For the stuff in functions.py we should maybe start by changing the documentation, and conceptionally move the more abstract functions to live in pg.functions.
My personal preference for deciding what should live in the top-level pg. namespace would be to ask "Would I need this when doing manual data visualization in an interactive notebook?" That is where short, easily memorized calls are most relevant. From that point of view, the pg. namespace should hold things like mkColor(), but not arrayToQPath().
As far as I can tell, the use of the more abstract functions throughout the library is typically something like
import pg.functions as fn
fn.eq(a,b)and maybe the documentation should encourage that use, too.
Right now, we also have functionality like affineSliceCoords in the pg. namespace, even though this is not even in the documentation.
This is obviously a policy discussion that requires more input from more people, but from the technical side of things:
Would there be a convenient way to add deprecation messages for the use of selected abstract functions directly from the pg. namespace (while keeping the access through pg.functions indisturbed)?
There was a problem hiding this comment.
I have implemented the deprecation following the pointer to https://www.python.org/dev/peps/pep-0562/ from @ntjess
examples/verlet_chain/chain.py
Outdated
| stepped = pg.QtCore.Signal() | ||
| relaxed = pg.QtCore.Signal() | ||
| stepped = QtCore.Signal() | ||
| relaxed = QtCore.Signal() |
There was a problem hiding this comment.
I have no actual objection to this change, but having this as
pg.QtCore might be a useful reminder that this version of QtCore has been touched by the import into PyQtGraph.
In this specific case, the code relies on the patched-in alias QtCore.Signal = QtCore.pyqtSignal when running on PyQt.
Where that isn't the case and only the normal functionality is used, simplifying pg.QtCore to QtCore seems like a good idea.
There was a problem hiding this comment.
I have created fake QtCore, QtGui, QtWidgets so that pyqtgraph's monkeying of the module attributes are local.
In particular, QtCore.Signal = QtCore.pyqtSignal is now a local change.
Monkey-patching in methods to classes will still modify the actual classes, but most of those are deprecated and slated for removal in 0.13.0
|
Technically, QtCore was never directly imported into pg namespace, only indirectly by "import *". Personally, I don't use pg.QtGui not pg.QtCore, and my code starts like this: import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui, QtWidgetsor from PySide6 import QtCore, QtGui, QtWidgets
import pyqtgraph as pg |
tests/widgets/test_graphics_view.py
Outdated
| # XXX: Removal of central item is not clear in code | ||
| scene = view.sceneObj | ||
| assert isinstance(scene, pg.GraphicsScene) | ||
| assert isinstance(scene, pg.GraphicsScene.GraphicsScene) |
There was a problem hiding this comment.
Do we have a clear convention for the capitalization of modules?
This one probably does not need to be accessed by the user typically, and thus doesn't justify a change.
But in an ideal world (with no existing user code), should this change to pg.graphicsscene.GraphicsScene?
I would argue that a no-caps convention is used by e.g. pg.functions and pg.colormap.
There was a problem hiding this comment.
Well, whatever convention there might be, it can't be retroactively applied to modules that have been around such a long time.
There was a problem hiding this comment.
I'll give you that, although this may be the first time that we are encouraging access outside of the pg. namespace. :)
I should have really phrased that more openly, though. Sorry about that!
Is there anything we can do to keep module names as consistent as possible in the future?
Do you have a preference for the naming?
I do like no-caps for modules, and find e.g. pg.GraphicsScene.GraphicsScene a little confusing. But if it turns out that we really like CamelCase names for the modules, then we might still be able to change the module name e.g. for colormap.py. All the module level functions are still marked experimental there, but if we wanted to rename it, we probably shouldn't wait any longer.
This shouldn't be any holdup for this PR, though.
There was a problem hiding this comment.
although this may be the first time that we are encouraging access outside of the pg. namespace.
That's a question to be answered for GraphicsScene. It was imported into pg namespace only indirectly. Only one place in the library accessed it as pg.GraphicsScene. So I had a choice of either importing it explicitly into pg namespace or modifying the access to pg.GraphicsScene.GraphicsScene. I picked the latter, but if anyone feels that the former would be a better choice, I can make the change.
Do you have a preference for the naming?
Well, I prefer snake_case. :-) But there's a rule that when modifying code, try to make it look like the rest.
There was a problem hiding this comment.
@pijyoi regarding snake case wonder if we should try to integrate the from __feature__ import snake_case Not sure what's involved with that level of integration though.
FWIW, https://stackoverflow.com/a/48916205/9463643
|
Thanks, I have used it to deprecate access of functions in functions.py via pg. The example I used |
|
Potentially, |
|
I would say that the basic color functions live quite nicely in the I wouldn't mind moving |
|
These are the attributes from In [1]: import pyqtgraph as pg
In [2]: print(pg.functions.deprecated_import_all)
['disconnect', 'makeArrowPath', 'traceImage', 'colorToAlpha', 'downsample', 'siApply', 'interpolateArray',
'toposort', 'SignalBlock', 'invertQTransform', 'interweaveArrays', 'affineSliceCoords', 'Color', 'subArray']There aren't that many of them, so one other safe option is to not deprecate them and henceforth be more judicious about what new functions qualify for going into
|
|
I have removed policy-related decisions from the changes. So, no deprecations. If any indirectly-imported attribute was used within the library, then it gets explicitly imported into pg namespace. Exceptions:
|
|
Sorry I've been busy and haven't had time to chime in here. I'm fully supportive of the effort to minimize the modules, classes and functions in the pg namespace. Feel free to be aggressive about moving things around using deprecation warnings with pointing to where the moved objects can be accessed from. FYI, MetaArray will be getting removed from the library in a few versions. |
|
@pijyoi what you did with the "fake" Qt modules to avoid monkey patching them is brilliant. I'm good w/ merging this; will give some time for others to chime in. |
|
I don't have the time to contribute a well-argued reply, but I wanted to say that I like this effort a lot. I personally believe that this should be a continued after this PR; The next step might be to change the documentation to encourage using the more technical utility functions from the |
|
What are your thoughts on exposing |
|
The comments in So leaving them out was a deliberate choice, not an accidental oversight. |
|
@pijyoi sorry you haven't been able to get more direct input here. This is the sort of thing my brain defaults to "its just how it is" and I go with it; so I'm likely go to along with most proposed changes. Generally speaking, I am very supportive of cleaning up the namespace. I'm very supportive of putting deprecation warnings on items currently in the namespace but there is general agreement that it should not be there, and getting the ball rolling on moving them out. In another PR/issue, we discussed the use-case of camelCase vs. snake_case being a reflection of whether the method/function was intended to be public or not. I think you were onto something there; but I do think private-ish, should start with an I do wonder if we can tap into PySide6's I do think some of the colormap stuff is a bit confusing ( I think this PR is good as is. For the more potentially controversial cases, perhaps I should start an issue (or a discussion?) that we can discuss more specific stuff there? |
|
UPDATE: For the record, these are the symbols eliminated from the pg namespace. Out[7]:
{'Colors',
'FLOAT_REGEX',
'INT_REGEX',
'ImageViewTemplate_pyside6',
'IsosurfaceDataCache',
'LRU',
'MATRIX_RGB_FROM_XYZ',
'MATRIX_XYZ_FROM_RGB',
'QT_LIB',
'QtVersion',
'SI_PREFIXES',
'SI_PREFIXES_ASCII',
'SI_PREFIX_EXPONENTS',
'Symbols',
'VECTOR_XYZn',
'ViewBoxMenu',
'arrayToQPolygonF',
'atan2',
'axisCtrlTemplate_pyside6',
'basestring',
'create_qpolygonf',
'ctypes',
'dataType',
'decimal',
'degrees',
'division',
'fn',
'getNumbaFunctions',
'hypot',
'isSequence',
'makeCrosshair',
'math',
'ndarray_from_qpolygonf',
'ndarray_to_qimage',
'np',
'operator',
'plotConfigTemplate_pyside6',
'qimage_to_ndarray',
're',
'reduce',
'string',
'struct',
'try_fastpath_argb',
'warnings',
'weakref'} |
|
|
|
Well, it wasn't so much removed as it was not added in. If it was in pg namespace, it was an indirect import due to lack of |
ACQ4 uses |
Just to clarify, did you mean that ACQ4 accesses it as pg.OrderedDict? |
Yes, correct. |
pyqtgraph/__init__.py
Outdated
|
|
||
| # indirect imports known to be used outside of the library | ||
| from .metaarray import MetaArray | ||
| from collections import OrderedDict |
There was a problem hiding this comment.
This import line will obscure the deprecation warning for anyone importing OrderedDict from pyqtgraph. We should keep it as from .orderedict so as to properly prepare such importers.
There was a problem hiding this comment.
I didn't think of that, so I tried it out. It seems that from collections yields the same behavior as current master whereas from .ordereddict obscures it.
current master
d:\>py -c "import pyqtgraph as pg; pg.OrderedDict"
d:\>py -c "from pyqtgraph.ordereddict import OrderedDict"
<string>:1: DeprecationWarning: OrderedDict is in the standard library for supported versions of Python. Will be removed in 0.13
with from collections import OrderedDict
d:\>py -c "import pyqtgraph as pg; pg.OrderedDict"
d:\>py -c "from pyqtgraph.ordereddict import OrderedDict"
<string>:1: DeprecationWarning: OrderedDict is in the standard library for supported versions of Python. Will be removed in 0.13
with from .ordereddict import OrderedDict
d:\>py -c "import pyqtgraph as pg; pg.OrderedDict"
d:\>py -c "from pyqtgraph.ordereddict import OrderedDict"
There was a problem hiding this comment.
Yeah, I realized that would be the case after I went to bed last night. I guess the better warning here would be to use from .ordereddict in __init__.py and replace ordereddict.py with:
from collections import OrderedDict as _OrderedDict
import warnings
class OrderedDict(_OrderedDict):
def __init__(self, *a, **kw):
warnings.warn(
"OrderedDict is in the standard library for supported versions of Python. Will be removed in 0.13",
DeprecationWarning,
stacklevel=2,
)
super(OrderedDict, self).__init__(*a, **kw)There was a problem hiding this comment.
I'm 👍 for warnings in __init__ method (it's what I did for MetaArray)
|
I think this is ready to merge, @outofculture @ixjlyons ? |
|
Looks reasonable to me. The local Qt modules thing is a neat trick, though I admit I was a little puzzled by it until I read more context here in the comments. A comment about localizing the effects of the monkey-patching might be nice. Funny enough, it does add another item to the top-level pyqtgraph namespace ( Maybe not worth it, just a thought. |
|
I have changed the directory hierarchy to the following. |
This PR adds
__all__to the various files imported into pg namespace by__init__.py.functions.pystands out in particular as it defines many functions, some of which are really only meant for use within the library. For backwards compatibility, the functions are still exported except for those functions which have only been added recently. The preferred way of accessing those functions is viapg.functions.Most functions in
colormap.pyalso look like they shouldn't reside within pg namespace. @NilsNemitz, could you provide some input here?Potentially this PR could break user code that used accidental imports from pg namespace. E.g.