Show More
@@ -371,7 +371,7 b' class DisplayObject(object):' | |||||
371 | with gzip.open(BytesIO(data), 'rt', encoding=encoding) as fp: |
|
371 | with gzip.open(BytesIO(data), 'rt', encoding=encoding) as fp: | |
372 | encoding = None |
|
372 | encoding = None | |
373 | data = fp.read() |
|
373 | data = fp.read() | |
374 |
|
374 | |||
375 | # decode data, if an encoding was specified |
|
375 | # decode data, if an encoding was specified | |
376 | # We only touch self.data once since |
|
376 | # We only touch self.data once since | |
377 | # subclasses such as SVG have @data.setter methods |
|
377 | # subclasses such as SVG have @data.setter methods | |
@@ -1155,7 +1155,12 b' class Video(DisplayObject):' | |||||
1155 |
|
1155 | |||
1156 | @skip_doctest |
|
1156 | @skip_doctest | |
1157 | def set_matplotlib_formats(*formats, **kwargs): |
|
1157 | def set_matplotlib_formats(*formats, **kwargs): | |
1158 | """Select figure formats for the inline backend. Optionally pass quality for JPEG. |
|
1158 | """ | |
|
1159 | .. deprecated:: 7.23 | |||
|
1160 | ||||
|
1161 | use `matplotlib_inline.backend_inline.set_matplotlib_formats()` | |||
|
1162 | ||||
|
1163 | Select figure formats for the inline backend. Optionally pass quality for JPEG. | |||
1159 |
|
1164 | |||
1160 | For example, this enables PNG and JPEG output with a JPEG quality of 90%:: |
|
1165 | For example, this enables PNG and JPEG output with a JPEG quality of 90%:: | |
1161 |
|
1166 | |||
@@ -1173,20 +1178,28 b' def set_matplotlib_formats(*formats, **kwargs):' | |||||
1173 | **kwargs |
|
1178 | **kwargs | |
1174 | Keyword args will be relayed to ``figure.canvas.print_figure``. |
|
1179 | Keyword args will be relayed to ``figure.canvas.print_figure``. | |
1175 | """ |
|
1180 | """ | |
1176 | from IPython.core.interactiveshell import InteractiveShell |
|
1181 | warnings.warn( | |
1177 | from IPython.core.pylabtools import select_figure_formats |
|
1182 | "`set_matplotlib_formats` is deprecated since IPython 7.23, directly " | |
1178 | # build kwargs, starting with InlineBackend config |
|
1183 | "use `matplotlib_inline.backend_inline.set_matplotlib_formats()`", | |
1179 | kw = {} |
|
1184 | DeprecationWarning, | |
1180 | from ipykernel.pylab.config import InlineBackend |
|
1185 | stacklevel=2, | |
1181 | cfg = InlineBackend.instance() |
|
1186 | ) | |
1182 | kw.update(cfg.print_figure_kwargs) |
|
1187 | ||
1183 | kw.update(**kwargs) |
|
1188 | from matplotlib_inline.backend_inline import ( | |
1184 | shell = InteractiveShell.instance() |
|
1189 | set_matplotlib_formats as set_matplotlib_formats_orig, | |
1185 | select_figure_formats(shell, formats, **kw) |
|
1190 | ) | |
|
1191 | ||||
|
1192 | set_matplotlib_formats_orig(*formats, **kwargs) | |||
1186 |
|
1193 | |||
1187 | @skip_doctest |
|
1194 | @skip_doctest | |
1188 | def set_matplotlib_close(close=True): |
|
1195 | def set_matplotlib_close(close=True): | |
1189 | """Set whether the inline backend closes all figures automatically or not. |
|
1196 | """ | |
|
1197 | .. deprecated:: 7.23 | |||
|
1198 | ||||
|
1199 | use `matplotlib_inline.backend_inline.set_matplotlib_close()` | |||
|
1200 | ||||
|
1201 | ||||
|
1202 | Set whether the inline backend closes all figures automatically or not. | |||
1190 |
|
1203 | |||
1191 | By default, the inline backend used in the IPython Notebook will close all |
|
1204 | By default, the inline backend used in the IPython Notebook will close all | |
1192 | matplotlib figures automatically after each cell is run. This means that |
|
1205 | matplotlib figures automatically after each cell is run. This means that | |
@@ -1206,6 +1219,15 b' def set_matplotlib_close(close=True):' | |||||
1206 | Should all matplotlib figures be automatically closed after each cell is |
|
1219 | Should all matplotlib figures be automatically closed after each cell is | |
1207 | run? |
|
1220 | run? | |
1208 | """ |
|
1221 | """ | |
1209 | from ipykernel.pylab.config import InlineBackend |
|
1222 | warnings.warn( | |
1210 | cfg = InlineBackend.instance() |
|
1223 | "`set_matplotlib_close` is deprecated since IPython 7.23, directly " | |
1211 | cfg.close_figures = close |
|
1224 | "use `matplotlib_inline.backend_inline.set_matplotlib_close()`", | |
|
1225 | DeprecationWarning, | |||
|
1226 | stacklevel=2, | |||
|
1227 | ) | |||
|
1228 | ||||
|
1229 | from matplotlib_inline.backend_inline import ( | |||
|
1230 | set_matplotlib_close as set_matplotlib_close_orig, | |||
|
1231 | ) | |||
|
1232 | ||||
|
1233 | set_matplotlib_close_orig(close) |
@@ -3527,6 +3527,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
3527 | display figures inline. |
|
3527 | display figures inline. | |
3528 | """ |
|
3528 | """ | |
3529 | from IPython.core import pylabtools as pt |
|
3529 | from IPython.core import pylabtools as pt | |
|
3530 | from matplotlib_inline.backend_inline import configure_inline_support | |||
3530 | gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select) |
|
3531 | gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select) | |
3531 |
|
3532 | |||
3532 | if gui != 'inline': |
|
3533 | if gui != 'inline': | |
@@ -3540,7 +3541,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
3540 | gui, backend = pt.find_gui_and_backend(self.pylab_gui_select) |
|
3541 | gui, backend = pt.find_gui_and_backend(self.pylab_gui_select) | |
3541 |
|
3542 | |||
3542 | pt.activate_matplotlib(backend) |
|
3543 | pt.activate_matplotlib(backend) | |
3543 |
|
|
3544 | configure_inline_support(self, backend) | |
3544 |
|
3545 | |||
3545 | # Now we must activate the gui pylab wants to use, and fix %run to take |
|
3546 | # Now we must activate the gui pylab wants to use, and fix %run to take | |
3546 | # plot updates into account |
|
3547 | # plot updates into account |
@@ -5,30 +5,32 b'' | |||||
5 | # Distributed under the terms of the Modified BSD License. |
|
5 | # Distributed under the terms of the Modified BSD License. | |
6 |
|
6 | |||
7 | from io import BytesIO |
|
7 | from io import BytesIO | |
|
8 | import warnings | |||
8 |
|
9 | |||
9 | from IPython.core.display import _pngxy |
|
10 | from IPython.core.display import _pngxy | |
10 | from IPython.utils.decorators import flag_calls |
|
11 | from IPython.utils.decorators import flag_calls | |
11 |
|
12 | |||
12 | # If user specifies a GUI, that dictates the backend, otherwise we read the |
|
13 | # If user specifies a GUI, that dictates the backend, otherwise we read the | |
13 | # user's mpl default from the mpl rc structure |
|
14 | # user's mpl default from the mpl rc structure | |
14 |
backends = { |
|
15 | backends = { | |
15 | 'gtk': 'GTKAgg', |
|
16 | "tk": "TkAgg", | |
16 |
|
|
17 | "gtk": "GTKAgg", | |
17 | 'wx': 'WXAgg', |
|
18 | "gtk3": "GTK3Agg", | |
18 | 'qt4': 'Qt4Agg', |
|
19 | "wx": "WXAgg", | |
19 | 'qt5': 'Qt5Agg', |
|
20 | "qt4": "Qt4Agg", | |
20 |
|
|
21 | "qt5": "Qt5Agg", | |
21 | 'osx': 'MacOSX', |
|
22 | "qt": "Qt5Agg", | |
22 | 'nbagg': 'nbAgg', |
|
23 | "osx": "MacOSX", | |
23 | 'notebook': 'nbAgg', |
|
24 | "nbagg": "nbAgg", | |
24 | 'agg': 'agg', |
|
25 | "notebook": "nbAgg", | |
25 | 'svg': 'svg', |
|
26 | "agg": "agg", | |
26 | 'pdf': 'pdf', |
|
27 | "svg": "svg", | |
27 | 'ps': 'ps', |
|
28 | "pdf": "pdf", | |
28 | 'inline': 'module://ipykernel.pylab.backend_inline', |
|
29 | "ps": "ps", | |
29 | 'ipympl': 'module://ipympl.backend_nbagg', |
|
30 | "inline": "module://matplotlib_inline.backend_inline", | |
30 |
|
|
31 | "ipympl": "module://ipympl.backend_nbagg", | |
31 | } |
|
32 | "widget": "module://ipympl.backend_nbagg", | |
|
33 | } | |||
32 |
|
34 | |||
33 | # We also need a reverse backends2guis mapping that will properly choose which |
|
35 | # We also need a reverse backends2guis mapping that will properly choose which | |
34 | # GUI support to activate based on the desired matplotlib backend. For the |
|
36 | # GUI support to activate based on the desired matplotlib backend. For the | |
@@ -44,12 +46,12 b" backend2gui['GTK3Cairo'] = 'gtk3'" | |||||
44 | backend2gui['WX'] = 'wx' |
|
46 | backend2gui['WX'] = 'wx' | |
45 | backend2gui['CocoaAgg'] = 'osx' |
|
47 | backend2gui['CocoaAgg'] = 'osx' | |
46 | # And some backends that don't need GUI integration |
|
48 | # And some backends that don't need GUI integration | |
47 |
del backend2gui[ |
|
49 | del backend2gui["nbAgg"] | |
48 |
del backend2gui[ |
|
50 | del backend2gui["agg"] | |
49 |
del backend2gui[ |
|
51 | del backend2gui["svg"] | |
50 |
del backend2gui[ |
|
52 | del backend2gui["pdf"] | |
51 |
del backend2gui[ |
|
53 | del backend2gui["ps"] | |
52 |
del backend2gui[ |
|
54 | del backend2gui["module://matplotlib_inline.backend_inline"] | |
53 |
|
55 | |||
54 | #----------------------------------------------------------------------------- |
|
56 | #----------------------------------------------------------------------------- | |
55 | # Matplotlib utilities |
|
57 | # Matplotlib utilities | |
@@ -96,10 +98,10 b' def figsize(sizex, sizey):' | |||||
96 |
|
98 | |||
97 | def print_figure(fig, fmt='png', bbox_inches='tight', **kwargs): |
|
99 | def print_figure(fig, fmt='png', bbox_inches='tight', **kwargs): | |
98 | """Print a figure to an image, and return the resulting file data |
|
100 | """Print a figure to an image, and return the resulting file data | |
99 |
|
101 | |||
100 | Returned data will be bytes unless ``fmt='svg'``, |
|
102 | Returned data will be bytes unless ``fmt='svg'``, | |
101 | in which case it will be unicode. |
|
103 | in which case it will be unicode. | |
102 |
|
104 | |||
103 | Any keyword args are passed to fig.canvas.print_figure, |
|
105 | Any keyword args are passed to fig.canvas.print_figure, | |
104 | such as ``quality`` or ``bbox_inches``. |
|
106 | such as ``quality`` or ``bbox_inches``. | |
105 | """ |
|
107 | """ | |
@@ -112,7 +114,7 b" def print_figure(fig, fmt='png', bbox_inches='tight', **kwargs):" | |||||
112 | if fmt == 'retina': |
|
114 | if fmt == 'retina': | |
113 | dpi = dpi * 2 |
|
115 | dpi = dpi * 2 | |
114 | fmt = 'png' |
|
116 | fmt = 'png' | |
115 |
|
117 | |||
116 | # build keyword args |
|
118 | # build keyword args | |
117 | kw = { |
|
119 | kw = { | |
118 | "format":fmt, |
|
120 | "format":fmt, | |
@@ -162,7 +164,7 b' def mpl_runner(safe_execfile):' | |||||
162 | A function suitable for use as the ``runner`` argument of the %run magic |
|
164 | A function suitable for use as the ``runner`` argument of the %run magic | |
163 | function. |
|
165 | function. | |
164 | """ |
|
166 | """ | |
165 |
|
167 | |||
166 | def mpl_execfile(fname,*where,**kw): |
|
168 | def mpl_execfile(fname,*where,**kw): | |
167 | """matplotlib-aware wrapper around safe_execfile. |
|
169 | """matplotlib-aware wrapper around safe_execfile. | |
168 |
|
170 | |||
@@ -243,7 +245,7 b' def select_figure_formats(shell, formats, **kwargs):' | |||||
243 | bs = "%s" % ','.join([repr(f) for f in bad]) |
|
245 | bs = "%s" % ','.join([repr(f) for f in bad]) | |
244 | gs = "%s" % ','.join([repr(f) for f in supported]) |
|
246 | gs = "%s" % ','.join([repr(f) for f in supported]) | |
245 | raise ValueError("supported formats are: %s not %s" % (gs, bs)) |
|
247 | raise ValueError("supported formats are: %s not %s" % (gs, bs)) | |
246 |
|
248 | |||
247 | if 'png' in formats: |
|
249 | if 'png' in formats: | |
248 | png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs)) |
|
250 | png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs)) | |
249 | if 'retina' in formats or 'png2x' in formats: |
|
251 | if 'retina' in formats or 'png2x' in formats: | |
@@ -274,7 +276,7 b' def find_gui_and_backend(gui=None, gui_select=None):' | |||||
274 | Returns |
|
276 | Returns | |
275 | ------- |
|
277 | ------- | |
276 | A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg', |
|
278 | A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg', | |
277 |
'WXAgg','Qt4Agg','module:// |
|
279 | 'WXAgg','Qt4Agg','module://matplotlib_inline.backend_inline','agg'). | |
278 | """ |
|
280 | """ | |
279 |
|
281 | |||
280 | import matplotlib |
|
282 | import matplotlib | |
@@ -308,7 +310,7 b' def activate_matplotlib(backend):' | |||||
308 |
|
310 | |||
309 | import matplotlib |
|
311 | import matplotlib | |
310 | matplotlib.interactive(True) |
|
312 | matplotlib.interactive(True) | |
311 |
|
313 | |||
312 | # Matplotlib had a bug where even switch_backend could not force |
|
314 | # Matplotlib had a bug where even switch_backend could not force | |
313 | # the rcParam to update. This needs to be set *before* the module |
|
315 | # the rcParam to update. This needs to be set *before* the module | |
314 | # magic of switch_backend(). |
|
316 | # magic of switch_backend(). | |
@@ -329,11 +331,11 b' def activate_matplotlib(backend):' | |||||
329 |
|
331 | |||
330 | def import_pylab(user_ns, import_all=True): |
|
332 | def import_pylab(user_ns, import_all=True): | |
331 | """Populate the namespace with pylab-related values. |
|
333 | """Populate the namespace with pylab-related values. | |
332 |
|
334 | |||
333 | Imports matplotlib, pylab, numpy, and everything from pylab and numpy. |
|
335 | Imports matplotlib, pylab, numpy, and everything from pylab and numpy. | |
334 |
|
336 | |||
335 | Also imports a few names from IPython (figsize, display, getfigs) |
|
337 | Also imports a few names from IPython (figsize, display, getfigs) | |
336 |
|
338 | |||
337 | """ |
|
339 | """ | |
338 |
|
340 | |||
339 | # Import numpy as np/pyplot as plt are conventions we're trying to |
|
341 | # Import numpy as np/pyplot as plt are conventions we're trying to | |
@@ -346,12 +348,12 b' def import_pylab(user_ns, import_all=True):' | |||||
346 | "plt = pyplot\n" |
|
348 | "plt = pyplot\n" | |
347 | ) |
|
349 | ) | |
348 | exec(s, user_ns) |
|
350 | exec(s, user_ns) | |
349 |
|
351 | |||
350 | if import_all: |
|
352 | if import_all: | |
351 | s = ("from matplotlib.pylab import *\n" |
|
353 | s = ("from matplotlib.pylab import *\n" | |
352 | "from numpy import *\n") |
|
354 | "from numpy import *\n") | |
353 | exec(s, user_ns) |
|
355 | exec(s, user_ns) | |
354 |
|
356 | |||
355 | # IPython symbols to add |
|
357 | # IPython symbols to add | |
356 | user_ns['figsize'] = figsize |
|
358 | user_ns['figsize'] = figsize | |
357 | from IPython.display import display |
|
359 | from IPython.display import display | |
@@ -361,7 +363,12 b' def import_pylab(user_ns, import_all=True):' | |||||
361 |
|
363 | |||
362 |
|
364 | |||
363 | def configure_inline_support(shell, backend): |
|
365 | def configure_inline_support(shell, backend): | |
364 | """Configure an IPython shell object for matplotlib use. |
|
366 | """ | |
|
367 | .. deprecated: 7.23 | |||
|
368 | ||||
|
369 | use `matplotlib_inline.backend_inline.configure_inline_support()` | |||
|
370 | ||||
|
371 | Configure an IPython shell object for matplotlib use. | |||
365 |
|
372 | |||
366 | Parameters |
|
373 | Parameters | |
367 | ---------- |
|
374 | ---------- | |
@@ -369,51 +376,13 b' def configure_inline_support(shell, backend):' | |||||
369 |
|
376 | |||
370 | backend : matplotlib backend |
|
377 | backend : matplotlib backend | |
371 | """ |
|
378 | """ | |
372 | # If using our svg payload backend, register the post-execution |
|
379 | warnings.warn( | |
373 | # function that will pick up the results for display. This can only be |
|
380 | "`configure_inline_support` is deprecated since IPython 7.23, directly " | |
374 | # done with access to the real shell object. |
|
381 | "use `matplotlib_inline.backend_inline.configure_inline_support()`", | |
|
382 | DeprecationWarning, | |||
|
383 | stacklevel=2, | |||
|
384 | ) | |||
375 |
|
385 | |||
376 | # Note: if we can't load the inline backend, then there's no point |
|
386 | from matplotlib_inline.backend_inline import configure_inline_support_orig | |
377 | # continuing (such as in terminal-only shells in environments without |
|
|||
378 | # zeromq available). |
|
|||
379 | try: |
|
|||
380 | from ipykernel.pylab.backend_inline import InlineBackend |
|
|||
381 | except ImportError: |
|
|||
382 | return |
|
|||
383 | import matplotlib |
|
|||
384 |
|
387 | |||
385 | cfg = InlineBackend.instance(parent=shell) |
|
388 | configure_inline_support_orig(shell, backend) | |
386 | cfg.shell = shell |
|
|||
387 | if cfg not in shell.configurables: |
|
|||
388 | shell.configurables.append(cfg) |
|
|||
389 |
|
||||
390 | if backend == backends['inline']: |
|
|||
391 | from ipykernel.pylab.backend_inline import flush_figures |
|
|||
392 | shell.events.register('post_execute', flush_figures) |
|
|||
393 |
|
||||
394 | # Save rcParams that will be overwrittern |
|
|||
395 | shell._saved_rcParams = {} |
|
|||
396 | for k in cfg.rc: |
|
|||
397 | shell._saved_rcParams[k] = matplotlib.rcParams[k] |
|
|||
398 | # load inline_rc |
|
|||
399 | matplotlib.rcParams.update(cfg.rc) |
|
|||
400 | new_backend_name = "inline" |
|
|||
401 | else: |
|
|||
402 | from ipykernel.pylab.backend_inline import flush_figures |
|
|||
403 | try: |
|
|||
404 | shell.events.unregister('post_execute', flush_figures) |
|
|||
405 | except ValueError: |
|
|||
406 | pass |
|
|||
407 | if hasattr(shell, '_saved_rcParams'): |
|
|||
408 | matplotlib.rcParams.update(shell._saved_rcParams) |
|
|||
409 | del shell._saved_rcParams |
|
|||
410 | new_backend_name = "other" |
|
|||
411 |
|
||||
412 | # only enable the formats once -> don't change the enabled formats (which the user may |
|
|||
413 | # has changed) when getting another "%matplotlib inline" call. |
|
|||
414 | # See https://github.com/ipython/ipykernel/issues/29 |
|
|||
415 | cur_backend = getattr(configure_inline_support, "current_backend", "unset") |
|
|||
416 | if new_backend_name != cur_backend: |
|
|||
417 | # Setup the default figure format |
|
|||
418 | select_figure_formats(shell, cfg.figure_formats, **cfg.print_figure_kwargs) |
|
|||
419 | configure_inline_support.current_backend = new_backend_name |
|
@@ -135,7 +135,7 b' def test_image_filename_defaults():' | |||||
135 | nt.assert_is_none(img._repr_jpeg_()) |
|
135 | nt.assert_is_none(img._repr_jpeg_()) | |
136 |
|
136 | |||
137 | def _get_inline_config(): |
|
137 | def _get_inline_config(): | |
138 |
from |
|
138 | from matplotlib_inline.config import InlineBackend | |
139 | return InlineBackend.instance() |
|
139 | return InlineBackend.instance() | |
140 |
|
140 | |||
141 |
|
141 |
@@ -15,6 +15,7 b' from nose import SkipTest' | |||||
15 | import nose.tools as nt |
|
15 | import nose.tools as nt | |
16 |
|
16 | |||
17 | from matplotlib import pyplot as plt |
|
17 | from matplotlib import pyplot as plt | |
|
18 | import matplotlib_inline | |||
18 | import numpy as np |
|
19 | import numpy as np | |
19 |
|
20 | |||
20 | from IPython.core.getipython import get_ipython |
|
21 | from IPython.core.getipython import get_ipython | |
@@ -175,13 +176,15 b' class TestPylabSwitch(object):' | |||||
175 | pt.activate_matplotlib = act_mpl |
|
176 | pt.activate_matplotlib = act_mpl | |
176 | self._save_ip = pt.import_pylab |
|
177 | self._save_ip = pt.import_pylab | |
177 | pt.import_pylab = lambda *a,**kw:None |
|
178 | pt.import_pylab = lambda *a,**kw:None | |
178 | self._save_cis = pt.configure_inline_support |
|
179 | self._save_cis = matplotlib_inline.backend_inline.configure_inline_support | |
179 |
pt.configure_inline_support = |
|
180 | matplotlib_inline.backend_inline.configure_inline_support = ( | |
|
181 | lambda *a, **kw: None | |||
|
182 | ) | |||
180 |
|
183 | |||
181 | def teardown(self): |
|
184 | def teardown(self): | |
182 | pt.activate_matplotlib = self._save_am |
|
185 | pt.activate_matplotlib = self._save_am | |
183 | pt.import_pylab = self._save_ip |
|
186 | pt.import_pylab = self._save_ip | |
184 | pt.configure_inline_support = self._save_cis |
|
187 | matplotlib_inline.backend_inline.configure_inline_support = self._save_cis | |
185 | import matplotlib |
|
188 | import matplotlib | |
186 | matplotlib.rcParams = self._saved_rcParams |
|
189 | matplotlib.rcParams = self._saved_rcParams | |
187 | matplotlib.rcParamsOrig = self._saved_rcParamsOrig |
|
190 | matplotlib.rcParamsOrig = self._saved_rcParamsOrig |
@@ -191,15 +191,16 b' extras_require = dict(' | |||||
191 | ) |
|
191 | ) | |
192 |
|
192 | |||
193 | install_requires = [ |
|
193 | install_requires = [ | |
194 |
|
|
194 | "setuptools>=18.5", | |
195 |
|
|
195 | "jedi>=0.16", | |
196 |
|
|
196 | "decorator", | |
197 |
|
|
197 | "pickleshare", | |
198 |
|
|
198 | "traitlets>=4.2", | |
199 |
|
|
199 | "prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1", | |
200 |
|
|
200 | "pygments", | |
201 |
|
|
201 | "backcall", | |
202 |
|
|
202 | "stack_data", | |
|
203 | "matplotlib-inline", | |||
203 | ] |
|
204 | ] | |
204 |
|
205 | |||
205 | # Platform-specific dependencies: |
|
206 | # Platform-specific dependencies: |
General Comments 0
You need to be logged in to leave comments.
Login now