Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
Cloud:OpenStack:Upstream:Wallaby
python-fixtures
fixtures-pr36-py39.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File fixtures-pr36-py39.patch of Package python-fixtures
From f8df2192bf1190ff21a3067bb9e132b6db85191e Mon Sep 17 00:00:00 2001 From: Stephen Finucane <stephenfin@redhat.com> Date: Thu, 25 Feb 2021 10:13:52 +0000 Subject: [PATCH 1/4] Remove use of 'extras' We weren't explicitly stating this dependency, instead relying on it being brought in by testtools. Since we want to remove it from testtools, we should drop it from here also. There's only one use of the 'try_imports' helper and a simple try-except does almost the same thing. Signed-off-by: Stephen Finucane <stephenfin@redhat.com> --- fixtures/_fixtures/mockpatch.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) Index: fixtures-3.0.0/fixtures/_fixtures/mockpatch.py =================================================================== --- fixtures-3.0.0.orig/fixtures/_fixtures/mockpatch.py +++ fixtures-3.0.0/fixtures/_fixtures/mockpatch.py @@ -15,13 +15,15 @@ # License for the specific language governing permissions and limitations # under the License. -import extras - import fixtures -mock = extras.try_imports(['mock', 'unittest.mock'], None) -mock_default = extras.try_imports( - ['mock.DEFAULT', 'unittest.mock.DEFAULT'], None) +# TODO(stephenfin): Make this configurable +try: + import mock +except ImportError: + import unittest.mock as mock + +mock_default = mock.DEFAULT class _Base(fixtures.Fixture): Index: fixtures-3.0.0/fixtures/_fixtures/monkeypatch.py =================================================================== --- fixtures-3.0.0.orig/fixtures/_fixtures/monkeypatch.py +++ fixtures-3.0.0/fixtures/_fixtures/monkeypatch.py @@ -15,7 +15,7 @@ __all__ = [ 'MonkeyPatch' - ] +] import functools import sys @@ -25,9 +25,6 @@ from fixtures import Fixture _class_types = (type, ) -if getattr(types, 'ClassType', None): - # Python 2 has multiple types of classes. - _class_types = _class_types + (types.ClassType,) def _coerce_values(obj, name, new_value, sentinel): @@ -84,9 +81,11 @@ def _coerce_values(obj, name, new_value, # bound state rather than having it bound to the new object # it has been patched onto. captured_method = new_value + @functools.wraps(old_value) def avoid_get(*args, **kwargs): return captured_method(*args, **kwargs) + new_value = avoid_get return (new_value, old_value) @@ -139,18 +138,21 @@ class MonkeyPatch(Fixture): __import__(location, {}, {}) except ImportError: pass + components = location.split('.') current = __import__(components[0], {}, {}) for component in components[1:]: current = getattr(current, component) sentinel = object() - new_value, old_value = _coerce_values(current, attribute, - self.new_value, sentinel) + new_value, old_value = _coerce_values( + current, attribute, self.new_value, sentinel) + if self.new_value is self.delete: if old_value is not sentinel: delattr(current, attribute) else: setattr(current, attribute, new_value) + if old_value is sentinel: self.addCleanup(self._safe_delete, current, attribute) else: Index: fixtures-3.0.0/fixtures/tests/_fixtures/test_monkeypatch.py =================================================================== --- fixtures-3.0.0.orig/fixtures/tests/_fixtures/test_monkeypatch.py +++ fixtures-3.0.0/fixtures/tests/_fixtures/test_monkeypatch.py @@ -14,6 +14,7 @@ # limitations under that license. import functools +import sys import testtools from testtools.matchers import Is @@ -32,7 +33,7 @@ class C(object): class D(object): def bar(self): pass - def bar_two_args(self, arg): + def bar_two_args(self, arg=None): return (self, arg) @classmethod def bar_cls(cls): @@ -188,14 +189,27 @@ class TestMonkeyPatch(testtools.TestCase 'fixtures.tests._fixtures.test_monkeypatch.C.foo_cls', D.bar_cls_args) with fixture: - cls, target_class = C.foo_cls() - self.expectThat(cls, Is(D)) - self.expectThat(target_class, Is(C)) - cls, target_class = C().foo_cls() - self.expectThat(cls, Is(D)) - self.expectThat(target_class, Is(C)) - self._check_restored_static_or_class_method(oldmethod, oldmethod_inst, - C, 'foo_cls') + # Python 3.9 changes the behavior of the classmethod decorator so + # that it now honours the descriptor binding protocol [1]. + # This means we're now going to call the monkeypatched classmethod + # the way we would have if it hadn't been monkeypatched: simply + # with the class + # + # https://bugs.python.org/issue19072 + if sys.version_info >= (3, 9): + cls, = C.foo_cls() + self.expectThat(cls, Is(D)) + cls, = C().foo_cls() + self.expectThat(cls, Is(D)) + else: + cls, target_class = C.foo_cls() + self.expectThat(cls, Is(D)) + self.expectThat(target_class, Is(C)) + cls, target_class = C().foo_cls() + self.expectThat(cls, Is(D)) + self.expectThat(target_class, Is(C)) + self._check_restored_static_or_class_method( + oldmethod, oldmethod_inst, C, 'foo_cls') def test_patch_classmethod_with_function(self): oldmethod = C.foo_cls @@ -222,12 +236,20 @@ class TestMonkeyPatch(testtools.TestCase with fixture: slf, cls = C.foo_cls() self.expectThat(slf, Is(d)) - self.expectThat(cls, Is(C)) + # See note in test_patch_classmethod_with_classmethod on changes in + # Python 3.9 + if sys.version_info >= (3, 9): + self.expectThat(cls, Is(None)) + else: + self.expectThat(cls, Is(C)) slf, cls = C().foo_cls() self.expectThat(slf, Is(d)) - self.expectThat(cls, Is(C)) - self._check_restored_static_or_class_method(oldmethod, oldmethod_inst, - C, 'foo_cls') + if sys.version_info >= (3, 9): + self.expectThat(cls, Is(None)) + else: + self.expectThat(cls, Is(C)) + self._check_restored_static_or_class_method( + oldmethod, oldmethod_inst, C, 'foo_cls') def test_patch_function_with_staticmethod(self): oldmethod = fake_no_args Index: fixtures-3.0.0/NEWS =================================================================== --- fixtures-3.0.0.orig/NEWS +++ fixtures-3.0.0/NEWS @@ -2,10 +2,12 @@ fixtures release notes ---------------------- - NEXT ~~~~ +* Dropped support for Python 2.7, Python 3.4 and Python 3.5 (EOL) +* Added support for Python 3.7, Python 3.8, and Python 3.9 + 3.0.0 ~~~~~ Index: fixtures-3.0.0/README =================================================================== --- fixtures-3.0.0.orig/README +++ fixtures-3.0.0/README @@ -25,7 +25,7 @@ compatible test cases easy and straight Dependencies ============ -* Python 2.6+, or 3.3+ +* Python 3.6+ This is the base language fixtures is written in and for. * pbr @@ -38,8 +38,6 @@ Dependencies For use in a unit test suite using the included glue, one of: -* Python 2.7+ - * unittest2 * bzrlib.tests @@ -182,10 +180,8 @@ cleanup, and return the fixture. This le >>> import testtools >>> import unittest -Note that we use testtools TestCase here as we need to guarantee a -TestCase.addCleanup method in this doctest. Unittest2 - Python2.7 and above - -also have ``addCleanup``. testtools has it's own implementation of -``useFixture`` so there is no need to use ``fixtures.TestWithFixtures`` with +Note that we use ``testtools.TestCase``. testtools has it's own implementation +of ``useFixture`` so there is no need to use ``fixtures.TestWithFixtures`` with ``testtools.TestCase``. >>> class NoddyTest(testtools.TestCase, fixtures.TestWithFixtures): @@ -364,7 +360,7 @@ FakePopen Pretend to run an external command rather than needing it to be present to run tests. - >>> from testtools.compat import BytesIO + >>> from io import BytesIO >>> fixture = fixtures.FakePopen(lambda _:{'stdout': BytesIO('foobar')}) MockPatchObject @@ -398,7 +394,7 @@ Adapts ``mock.patch.multiple`` to be use MonkeyPatch +++++++++++ -Control the value of a named python attribute. +Control the value of a named Python attribute. >>> def fake_open(path, mode): ... pass @@ -420,7 +416,7 @@ temporary file creation where an explici PackagePathEntry ++++++++++++++++ -Adds a single directory to the path for an existing python package. This adds +Adds a single directory to the path for an existing Python package. This adds to the package.__path__ list. If the directory is already in the path, nothing happens, if it isn't then it is added on setUp and removed on cleanUp. @@ -500,6 +496,5 @@ system call. Contributing ============ -Fixtures has its project homepage on Launchpad -<https://launchpad.net/python-fixtures>. Source code is hosted on GitHub +Fixtures has its project homepage on GitHub <https://github.com/testing-cabal/fixtures>. Index: fixtures-3.0.0/setup.cfg =================================================================== --- fixtures-3.0.0.orig/setup.cfg +++ fixtures-3.0.0/setup.cfg @@ -2,33 +2,37 @@ name = fixtures summary = Fixtures, reusable state for writing clean tests and more. description-file = - README + README author = Robert Collins author-email = robertc@robertcollins.net -home-page = https://launchpad.net/python-fixtures -classifier = - Development Status :: 6 - Mature - Intended Audience :: Developers +home-page = https://github.com/testing-cabal/fixtures +python-requires = >=3.6 +classifier = + Development Status :: 6 - Mature + Intended Audience :: Developers License :: OSI Approved :: BSD License - License :: OSI Approved :: Apache Software License - Operating System :: OS Independent - Programming Language :: Python - Programming Language :: Python :: 3 - Topic :: Software Development :: Quality Assurance + License :: OSI Approved :: Apache Software License + Operating System :: OS Independent + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: Implementation :: CPython + Programming Language :: Python :: Implementation :: PyPy + Topic :: Software Development :: Quality Assurance Topic :: Software Development :: Testing [files] packages = fixtures -[bdist_wheel] -universal = 1 - [extras] -test = - mock -docs = - docutils +test = + mock +docs = + docutils [egg_info] tag_build = Index: fixtures-3.0.0/fixtures/__init__.py =================================================================== --- fixtures-3.0.0.orig/fixtures/__init__.py +++ fixtures-3.0.0/fixtures/__init__.py @@ -1,12 +1,12 @@ # fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, 2011, Robert Collins <robertc@robertcollins.net> -# +# # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. -# +# # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -29,7 +29,7 @@ Most users will want to look at TestWith # the version number: major, minor, micro, releaselevel, and serial. All # values except releaselevel are integers; the release level is 'alpha', # 'beta', 'candidate', or 'final'. The version_info value corresponding to the -# Python version 2.0 is (2, 0, 0, 'final', 0)." Additionally we use a +# Python version 3.9 is (3, 9, 0, 'final', 0)." Additionally we use a # releaselevel of 'dev' for unreleased under-development code. # # If the releaselevel is 'alpha' then the major/minor/micro components are not Index: fixtures-3.0.0/fixtures/_fixtures/logger.py =================================================================== --- fixtures-3.0.0.orig/fixtures/_fixtures/logger.py +++ fixtures-3.0.0/fixtures/_fixtures/logger.py @@ -16,9 +16,6 @@ from logging import StreamHandler, getLogger, INFO, Formatter import sys -import six -from testtools.compat import _u - from fixtures import Fixture from fixtures._fixtures.streams import StringStream @@ -66,7 +63,8 @@ class LogHandler(Fixture): class StreamHandlerRaiseException(StreamHandler): """Handler class that will raise an exception on formatting errors.""" def handleError(self, record): - six.reraise(*sys.exc_info()) + _, value, tb = sys.exc_info() + raise value.with_traceback(tb) class FakeLogger(Fixture): @@ -103,7 +101,7 @@ class FakeLogger(Fixture): self._formatter = formatter def _setUp(self): - name = _u("pythonlogging:'%s'") % self._name + name = "pythonlogging:'%s'" % self._name output = self.useFixture(StringStream(name)).stream self._output = output handler = StreamHandlerRaiseException(output) Index: fixtures-3.0.0/fixtures/_fixtures/streams.py =================================================================== --- fixtures-3.0.0.orig/fixtures/_fixtures/streams.py +++ fixtures-3.0.0/fixtures/_fixtures/streams.py @@ -20,7 +20,6 @@ __all__ = [ ] import io -import sys from fixtures import Fixture import testtools @@ -69,15 +68,6 @@ def _string_stream_factory(): upper = io.TextIOWrapper(lower, encoding="utf8") # See http://bugs.python.org/issue7955 upper._CHUNK_SIZE = 1 - # In theory, this is sufficient and correct, but on Python2, - # upper.write(_b('foo")) will whinge louadly. - if sys.version_info[0] < 3: - upper_write = upper.write - def safe_write(str_or_bytes): - if type(str_or_bytes) is str: - str_or_bytes = str_or_bytes.decode('utf8') - return upper_write(str_or_bytes) - upper.write = safe_write return upper, lower Index: fixtures-3.0.0/fixtures/callmany.py =================================================================== --- fixtures-3.0.0.orig/fixtures/callmany.py +++ fixtures-3.0.0/fixtures/callmany.py @@ -19,9 +19,6 @@ __all__ = [ import sys -from testtools.compat import ( - reraise, - ) from testtools.helpers import try_import @@ -86,7 +83,7 @@ class CallMany(object): if result and raise_errors: if 1 == len(result): error = result[0] - reraise(error[0], error[1], error[2]) + raise error[1].with_traceback(error[2]) else: raise MultipleExceptions(*result) if not raise_errors: Index: fixtures-3.0.0/fixtures/fixture.py =================================================================== --- fixtures-3.0.0.orig/fixtures/fixture.py +++ fixtures-3.0.0/fixtures/fixture.py @@ -25,10 +25,6 @@ __all__ = [ import itertools import sys -import six -from testtools.compat import ( - advance_iterator, - ) from testtools.helpers import try_import from fixtures.callmany import ( @@ -46,7 +42,7 @@ def combine_details(source_details, targ new_name = name disambiguator = itertools.count(1) while new_name in target_details: - new_name = '%s-%d' % (name, advance_iterator(disambiguator)) + new_name = '%s-%d' % (name, next(disambiguator)) name = new_name target_details[name] = content_object @@ -211,7 +207,7 @@ class Fixture(object): if issubclass(err[0], Exception): raise MultipleExceptions(*errors) else: - six.reraise(*err) + raise err[1].with_traceback(err[2]) def _setUp(self): """Template method for subclasses to override. Index: fixtures-3.0.0/fixtures/tests/_fixtures/test_logger.py =================================================================== --- fixtures-3.0.0.orig/fixtures/tests/_fixtures/test_logger.py +++ fixtures-3.0.0/fixtures/tests/_fixtures/test_logger.py @@ -13,13 +13,12 @@ # license you chose for the specific language governing permissions and # limitations under that license. +import io import logging -import sys import time import testtools from testtools import TestCase -from testtools.compat import StringIO from fixtures import ( FakeLogger, @@ -32,12 +31,8 @@ from fixtures import ( # testing formatter overrides. class FooFormatter(logging.Formatter): def format(self, record): - # custom formatters interface changes in python 3.2 - if sys.version_info < (3, 2): - self._fmt = "Foo " + self._fmt - else: - self._style = logging.PercentStyle("Foo " + self._style._fmt) - self._fmt = self._style._fmt + self._style = logging.PercentStyle("Foo " + self._style._fmt) + self._fmt = self._style._fmt return logging.Formatter.format(self, record) @@ -58,7 +53,7 @@ class FakeLoggerTest(TestCase, TestWithF self.assertEqual("some message\n", fixture.output) def test_replace_and_restore_handlers(self): - stream = StringIO() + stream = io.StringIO() logger = logging.getLogger() logger.addHandler(logging.StreamHandler(stream)) logger.setLevel(logging.INFO) @@ -71,7 +66,7 @@ class FakeLoggerTest(TestCase, TestWithF self.assertEqual("one\nthree\n", stream.getvalue()) def test_preserving_existing_handlers(self): - stream = StringIO() + stream = io.StringIO() self.logger.addHandler(logging.StreamHandler(stream)) self.logger.setLevel(logging.INFO) fixture = FakeLogger(nuke_handlers=False) @@ -174,7 +169,7 @@ class LogHandlerTest(TestCase, TestWithF self.assertEqual(["some message"], fixture.handler.msgs) def test_replace_and_restore_handlers(self): - stream = StringIO() + stream = io.StringIO() logger = logging.getLogger() logger.addHandler(logging.StreamHandler(stream)) logger.setLevel(logging.INFO) @@ -187,7 +182,7 @@ class LogHandlerTest(TestCase, TestWithF self.assertEqual("one\nthree\n", stream.getvalue()) def test_preserving_existing_handlers(self): - stream = StringIO() + stream = io.StringIO() self.logger.addHandler(logging.StreamHandler(stream)) self.logger.setLevel(logging.INFO) fixture = LogHandler(self.CustomHandler(), nuke_handlers=False) Index: fixtures-3.0.0/fixtures/tests/_fixtures/test_popen.py =================================================================== --- fixtures-3.0.0.orig/fixtures/tests/_fixtures/test_popen.py +++ fixtures-3.0.0/fixtures/tests/_fixtures/test_popen.py @@ -13,13 +13,10 @@ # license you chose for the specific language governing permissions and # limitations under that license. +import io import subprocess import testtools -from testtools.compat import ( - _b, - BytesIO, - ) from fixtures import FakePopen, TestWithFixtures from fixtures._fixtures.popen import FakeProcess @@ -91,8 +88,8 @@ class TestFakeProcess(testtools.TestCase self.assertEqual(0, proc.returncode) def test_communicate_with_out(self): - proc = FakeProcess({}, {'stdout': BytesIO(_b('foo'))}) - self.assertEqual((_b('foo'), ''), proc.communicate()) + proc = FakeProcess({}, {'stdout': io.BytesIO(b'foo')}) + self.assertEqual((b'foo', ''), proc.communicate()) self.assertEqual(0, proc.returncode) def test_kill(self): @@ -101,4 +98,4 @@ class TestFakeProcess(testtools.TestCase def test_wait_with_timeout_and_endtime(self): proc = FakeProcess({}, {}) - self.assertEqual(0 , proc.wait(timeout=4, endtime=7)) + self.assertEqual(0, proc.wait(timeout=4, endtime=7)) Index: fixtures-3.0.0/fixtures/tests/_fixtures/test_pythonpackage.py =================================================================== --- fixtures-3.0.0.orig/fixtures/tests/_fixtures/test_pythonpackage.py +++ fixtures-3.0.0/fixtures/tests/_fixtures/test_pythonpackage.py @@ -1,12 +1,12 @@ # fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Robert Collins <robertc@robertcollins.net> -# +# # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. -# +# # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -16,7 +16,6 @@ import os.path import testtools -from testtools.compat import _b from fixtures import PythonPackage, TestWithFixtures @@ -32,7 +31,7 @@ class TestPythonPackage(testtools.TestCa fixture.cleanUp() def test_writes_package(self): - fixture = PythonPackage('foo', [('bar.py', _b('woo'))]) + fixture = PythonPackage('foo', [('bar.py', b'woo')]) fixture.setUp() try: self.assertEqual('', open(os.path.join(fixture.base, 'foo', @@ -43,7 +42,7 @@ class TestPythonPackage(testtools.TestCa fixture.cleanUp() def test_no__init__(self): - fixture = PythonPackage('foo', [('bar.py', _b('woo'))], init=False) + fixture = PythonPackage('foo', [('bar.py', b'woo')], init=False) fixture.setUp() try: self.assertFalse(os.path.exists(os.path.join(fixture.base, 'foo', Index: fixtures-3.0.0/fixtures/tests/_fixtures/test_streams.py =================================================================== --- fixtures-3.0.0.orig/fixtures/tests/_fixtures/test_streams.py +++ fixtures-3.0.0/fixtures/tests/_fixtures/test_streams.py @@ -14,10 +14,6 @@ # limitations under that license. from testtools import TestCase -from testtools.compat import ( - _b, - _u, - ) from testtools.matchers import Contains from fixtures import ( @@ -40,7 +36,7 @@ class TestByteStreams(TestCase): fixture = ByteStream(detail_name) with fixture: content = fixture.getDetails()[detail_name] - self.assertEqual(_u(""), content.as_text()) + self.assertEqual("", content.as_text()) def test_stream_content_in_details(self): detail_name = 'test' @@ -49,7 +45,7 @@ class TestByteStreams(TestCase): stream = fixture.stream content = fixture.getDetails()[detail_name] # Output after getDetails is called is included. - stream.write(_b("testing 1 2 3")) + stream.write(b"testing 1 2 3") self.assertEqual("testing 1 2 3", content.as_text()) def test_stream_content_reset(self): @@ -58,15 +54,15 @@ class TestByteStreams(TestCase): with fixture: stream = fixture.stream content = fixture.getDetails()[detail_name] - stream.write(_b("testing 1 2 3")) + stream.write(b"testing 1 2 3") with fixture: # The old content object returns the old usage - self.assertEqual(_u("testing 1 2 3"), content.as_text()) + self.assertEqual("testing 1 2 3", content.as_text()) content = fixture.getDetails()[detail_name] # A new fixture returns the new output: stream = fixture.stream - stream.write(_b("1 2 3 testing")) - self.assertEqual(_u("1 2 3 testing"), content.as_text()) + stream.write(b"1 2 3 testing") + self.assertEqual("1 2 3 testing", content.as_text()) class TestStringStreams(TestCase): @@ -76,7 +72,7 @@ class TestStringStreams(TestCase): fixture = StringStream(detail_name) with fixture: content = fixture.getDetails()[detail_name] - self.assertEqual(_u(""), content.as_text()) + self.assertEqual("", content.as_text()) def test_stream_content_in_details(self): detail_name = 'test' @@ -85,7 +81,7 @@ class TestStringStreams(TestCase): stream = fixture.stream content = fixture.getDetails()[detail_name] # Output after getDetails is called is included. - stream.write(_u("testing 1 2 3")) + stream.write("testing 1 2 3") self.assertEqual("testing 1 2 3", content.as_text()) def test_stream_content_reset(self): @@ -94,12 +90,12 @@ class TestStringStreams(TestCase): with fixture: stream = fixture.stream content = fixture.getDetails()[detail_name] - stream.write(_u("testing 1 2 3")) + stream.write("testing 1 2 3") with fixture: # The old content object returns the old usage - self.assertEqual(_u("testing 1 2 3"), content.as_text()) + self.assertEqual("testing 1 2 3", content.as_text()) content = fixture.getDetails()[detail_name] # A new fixture returns the new output: stream = fixture.stream - stream.write(_u("1 2 3 testing")) - self.assertEqual(_u("1 2 3 testing"), content.as_text()) + stream.write("1 2 3 testing") + self.assertEqual("1 2 3 testing", content.as_text()) Index: fixtures-3.0.0/requirements.txt =================================================================== --- fixtures-3.0.0.orig/requirements.txt +++ fixtures-3.0.0/requirements.txt @@ -1,3 +1,2 @@ pbr>=0.11 -six testtools>=0.9.22
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor