python-subunit
Advanced tools
| # See https://github.com/jelmer/disperse | ||
| news_file: "NEWS" | ||
| timeout_days: 5 | ||
| tag_name: "$VERSION" | ||
| verify_command: "autoreconf -i && ./configure && make distcheck" | ||
| update_version { | ||
| path: "configure.ac" | ||
| new_line: "m4_define([SUBUNIT_MAJOR_VERSION], [$MAJOR_VERSION])" | ||
| } | ||
| update_version { | ||
| path: "configure.ac" | ||
| new_line: "m4_define([SUBUNIT_MINOR_VERSION], [$MINOR_VERSION])" | ||
| } | ||
| update_version { | ||
| path: "configure.ac" | ||
| new_line: "m4_define([SUBUNIT_MICRO_VERSION], [$MICRO_VERSION])" | ||
| } | ||
| update_version { | ||
| path: "python/subunit/__init__.py" | ||
| new_line: "__version__ = $STATUS_TUPLED_VERSION" | ||
| } | ||
| launchpad_project: "subunit" |
@@ -13,8 +13,8 @@ name: Tests | ||
| matrix: | ||
| python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', 'pypy3'] | ||
| python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', 'pypy3.7'] | ||
| os: ['ubuntu-latest'] | ||
| steps: | ||
| - uses: actions/checkout@v2 | ||
| - uses: actions/checkout@v4 | ||
| - name: Set up Python ${{ matrix.python-version }} | ||
| uses: actions/setup-python@v2 | ||
| uses: actions/setup-python@v4 | ||
| with: | ||
@@ -21,0 +21,0 @@ python-version: ${{ matrix.python-version }} |
+0
-2
@@ -32,6 +32,4 @@ Subunit is licensed under two licenses, the Apache License, Version 2.0 or the | ||
| A known list of such code is maintained here: | ||
| * The python/iso8601 module by Michael Twomey, distributed under an MIT style | ||
| licence - see python/iso8601/LICENSE for details. | ||
| * The runtests.py and python/subunit/tests/TestUtil.py module are GPL test | ||
| support modules. They are not installed by Subunit - they are only ever | ||
| used on the build machine. Copyright 2004 Canonical Limited. |
+7
-1
@@ -5,8 +5,14 @@ --------------------- | ||
| NEXT (In development) | ||
| 1.4.3 (2023-09-17) | ||
| --------------------- | ||
| * Subunit now has a dependency on an external iso8601 | ||
| module rather than shipping its own. | ||
| (Jelmer Vernooij) | ||
| IMPROVEMENTS | ||
| ~~~~~~~~~~~~ | ||
| * Drop various compatibility wrappers for Python < 3.6. | ||
| BUG FIXES | ||
@@ -13,0 +19,0 @@ ~~~~~~~~~ |
+4
-7
| Metadata-Version: 2.1 | ||
| Name: python-subunit | ||
| Version: 1.4.2 | ||
| Version: 1.4.3 | ||
| Summary: Python implementation of subunit test streaming protocol | ||
@@ -16,3 +16,2 @@ Home-page: http://launchpad.net/subunit | ||
| Classifier: Programming Language :: Python :: 3 | ||
| Classifier: Programming Language :: Python :: 3.6 | ||
| Classifier: Programming Language :: Python :: 3.7 | ||
@@ -22,4 +21,5 @@ Classifier: Programming Language :: Python :: 3.8 | ||
| Classifier: Programming Language :: Python :: 3.10 | ||
| Classifier: Programming Language :: Python :: 3.11 | ||
| Classifier: Topic :: Software Development :: Testing | ||
| Requires-Python: >=3.6 | ||
| Requires-Python: >=3.7 | ||
| Provides-Extra: docs | ||
@@ -46,5 +46,2 @@ Provides-Extra: test | ||
| subunit reuses iso8601 by Michael Twomey, distributed under an MIT style | ||
| licence - see python/iso8601/LICENSE for details. | ||
| Subunit | ||
@@ -519,5 +516,5 @@ ------- | ||
| * Do a make distcheck, which will update Makefile etc. | ||
| * Do a PyPI release: PYTHONPATH=../../python python ../../setup.py sdist bdist_wheel upload -s | ||
| * Do a PyPI release: PYTHONPATH=python python setup.py sdist bdist_wheel; twine upload -s dist/* | ||
| * Upload the regular one to LP. | ||
| * Push a tagged commit. | ||
| git push -t origin master:master |
+4
-2
| [build-system] | ||
| # These are the assumed default build requirements from pip: | ||
| # https://pip.pypa.io/en/stable/reference/pip/#pep-517-and-518-support | ||
| requires = ["setuptools>=43.0.0"] | ||
| build-backend = "setuptools.build_meta" | ||
| [tool.ruff] | ||
| line-length = 120 | ||
| target-version = "py37" |
| Metadata-Version: 2.1 | ||
| Name: python-subunit | ||
| Version: 1.4.2 | ||
| Version: 1.4.3 | ||
| Summary: Python implementation of subunit test streaming protocol | ||
@@ -16,3 +16,2 @@ Home-page: http://launchpad.net/subunit | ||
| Classifier: Programming Language :: Python :: 3 | ||
| Classifier: Programming Language :: Python :: 3.6 | ||
| Classifier: Programming Language :: Python :: 3.7 | ||
@@ -22,4 +21,5 @@ Classifier: Programming Language :: Python :: 3.8 | ||
| Classifier: Programming Language :: Python :: 3.10 | ||
| Classifier: Programming Language :: Python :: 3.11 | ||
| Classifier: Topic :: Software Development :: Testing | ||
| Requires-Python: >=3.6 | ||
| Requires-Python: >=3.7 | ||
| Provides-Extra: docs | ||
@@ -46,5 +46,2 @@ Provides-Extra: test | ||
| subunit reuses iso8601 by Michael Twomey, distributed under an MIT style | ||
| licence - see python/iso8601/LICENSE for details. | ||
| Subunit | ||
@@ -519,5 +516,5 @@ ------- | ||
| * Do a make distcheck, which will update Makefile etc. | ||
| * Do a PyPI release: PYTHONPATH=../../python python ../../setup.py sdist bdist_wheel upload -s | ||
| * Do a PyPI release: PYTHONPATH=python python setup.py sdist bdist_wheel; twine upload -s dist/* | ||
| * Upload the regular one to LP. | ||
| * Push a tagged commit. | ||
| git push -t origin master:master |
@@ -1,2 +0,2 @@ | ||
| extras | ||
| iso8601 | ||
| testtools>=0.9.34 | ||
@@ -3,0 +3,0 @@ |
@@ -8,10 +8,6 @@ Apache-2.0 | ||
| all_tests.py | ||
| disperse.conf | ||
| pyproject.toml | ||
| setup.py | ||
| .github/workflows/main.yml | ||
| python/iso8601/LICENSE | ||
| python/iso8601/README | ||
| python/iso8601/README.subunit | ||
| python/iso8601/setup.py | ||
| python/iso8601/test_iso8601.py | ||
| python/subunit/__init__.py | ||
@@ -23,3 +19,2 @@ python/subunit/_output.py | ||
| python/subunit/filters.py | ||
| python/subunit/iso8601.py | ||
| python/subunit/progress_model.py | ||
@@ -26,0 +21,0 @@ python/subunit/run.py |
@@ -118,5 +118,2 @@ # | ||
| from io import BytesIO | ||
| from io import StringIO | ||
| from io import UnsupportedOperation as _UnsupportedOperation | ||
| import os | ||
@@ -127,7 +124,10 @@ import re | ||
| import unittest | ||
| from io import BytesIO | ||
| from io import UnsupportedOperation as _UnsupportedOperation | ||
| from extras import safe_hasattr | ||
| from testtools import content, content_type, ExtendedToOriginalDecorator | ||
| import iso8601 | ||
| from testtools import ExtendedToOriginalDecorator, content, content_type | ||
| from testtools.compat import _b, _u | ||
| from testtools.content import TracebackContent | ||
| from testtools.compat import _b, _u | ||
| try: | ||
@@ -139,5 +139,5 @@ from testtools.testresult.real import _StringException | ||
| "_StringException, check your version.") | ||
| from testtools import testresult, CopyStreamResult | ||
| from testtools import CopyStreamResult, testresult | ||
| from subunit import chunked, details, iso8601, test_results | ||
| from subunit import chunked, details | ||
| from subunit.v2 import ByteStreamToStreamResult, StreamResultToBytes | ||
@@ -157,4 +157,24 @@ | ||
| __version__ = (1, 4, 2, 'final', 0) | ||
| __version__ = (1, 4, 3, 'final', 0) | ||
| __all__ = [ | ||
| 'join_dir', | ||
| 'tags_to_new_gone', | ||
| 'content', | ||
| 'content_type', | ||
| 'TestProtocolServer', | ||
| 'TestProtocolClient', | ||
| 'RemoteError', | ||
| 'RemotedTestCase', | ||
| 'IsolatedTestCase', | ||
| 'IsolatedTestSuite', | ||
| 'run_isolated', | ||
| 'TAP2SubUnit', | ||
| 'tag_stream', | ||
| 'ProtocolTestCase', | ||
| 'make_stream_binary', | ||
| 'read_test_list', | ||
| 'TestResultStats', | ||
| ] | ||
| PROGRESS_SET = 0 | ||
@@ -559,3 +579,3 @@ PROGRESS_CUR = 1 | ||
| try: | ||
| event_time = iso8601.parse_date(line[offset:-1]) | ||
| event_time = iso8601.parse_date(line[offset:-1].decode()) | ||
| except TypeError: | ||
@@ -803,3 +823,3 @@ raise TypeError(_u("Failed to parse %r, got %r") | ||
| """ | ||
| time = a_datetime.astimezone(iso8601.Utc()) | ||
| time = a_datetime.astimezone(iso8601.UTC) | ||
| self._stream.write(_b("time: %04d-%02d-%02d %02d:%02d:%02d.%06dZ\n" % ( | ||
@@ -815,3 +835,3 @@ time.year, time.month, time.day, time.hour, time.minute, | ||
| self._stream.write(_b(" [ multipart\n")) | ||
| for name, content in sorted(details.items()): | ||
| for name, content in sorted(details.items()): # noqa: F402 | ||
| self._stream.write(_b("Content-Type: %s/%s" % | ||
@@ -883,3 +903,4 @@ (content.content_type.type, content.content_type.subtype))) | ||
| def run(self, result=None): | ||
| if result is None: result = self.defaultTestResult() | ||
| if result is None: | ||
| result = self.defaultTestResult() | ||
| result.startTest(self) | ||
@@ -911,3 +932,4 @@ result.addError(self, RemoteError(_u("Cannot run RemotedTestCases.\n"))) | ||
| def run(self, result=None): | ||
| if result is None: result = self.defaultTestResult() | ||
| if result is None: | ||
| result = self.defaultTestResult() | ||
| self._run(result) | ||
@@ -937,3 +959,4 @@ | ||
| def run(self, result=None): | ||
| if result is None: result = self.defaultTestResult() | ||
| if result is None: | ||
| result = self.defaultTestResult() | ||
| run_isolated(unittest.TestCase, self, result) | ||
@@ -952,3 +975,4 @@ | ||
| def run(self, result=None): | ||
| if result is None: result = testresult.TestResult() | ||
| if result is None: | ||
| result = testresult.TestResult() | ||
| run_isolated(unittest.TestSuite, self, result) | ||
@@ -1056,3 +1080,5 @@ | ||
| # not a plan line, or have seen one before | ||
| match = re.match(r"(ok|not ok)(?:\s+(\d+)?)?(?:\s+([^#]*[^#\s]+)\s*)?(?:\s+#\s+(TODO|SKIP|skip|todo)(?:\s+(.*))?)?\n", line) | ||
| match = re.match( | ||
| r"(ok|not ok)(?:\s+(\d+)?)?(?:\s+([^#]*[^#\s]+)\s*)?" | ||
| r"(?:\s+#\s+(TODO|SKIP|skip|todo)(?:\s+(.*))?)?\n", line) | ||
| if match: | ||
@@ -1059,0 +1085,0 @@ # new test, emit current one. |
@@ -17,15 +17,10 @@ # subunit: extensions to python unittest to get test results from subprocesses. | ||
| import datetime | ||
| import sys | ||
| from functools import partial | ||
| from optparse import ( | ||
| OptionGroup, | ||
| OptionParser, | ||
| OptionValueError, | ||
| ) | ||
| import sys | ||
| from optparse import OptionGroup, OptionParser, OptionValueError | ||
| from subunit import make_stream_binary | ||
| from subunit.iso8601 import UTC | ||
| from iso8601 import UTC | ||
| from subunit.v2 import StreamResultToBytes | ||
| _FINAL_ACTIONS = frozenset([ | ||
@@ -32,0 +27,0 @@ 'exists', |
@@ -15,3 +15,2 @@ # subunit: extensions to python unittest to get test results from subprocesses. | ||
| from errno import EEXIST | ||
| import io | ||
@@ -22,2 +21,3 @@ import json | ||
| import sys | ||
| from errno import EEXIST | ||
| from textwrap import dedent | ||
@@ -24,0 +24,0 @@ |
@@ -19,3 +19,3 @@ # | ||
| from io import BytesIO, StringIO | ||
| from io import BytesIO | ||
@@ -22,0 +22,0 @@ from testtools import content, content_type |
@@ -19,4 +19,4 @@ #!/usr/bin/env python3 | ||
| import sys | ||
| from optparse import OptionParser | ||
| import sys | ||
@@ -23,0 +23,0 @@ from testtools import ExtendedToStreamDecorator |
@@ -19,9 +19,6 @@ #!/usr/bin/env python3 | ||
| import sys | ||
| from optparse import OptionParser | ||
| import sys | ||
| from testtools import ( | ||
| StreamToExtendedDecorator, | ||
| StreamResultRouter, | ||
| ) | ||
| from testtools import StreamResultRouter, StreamToExtendedDecorator | ||
@@ -28,0 +25,0 @@ from subunit import ByteStreamToStreamResult, TestProtocolClient |
@@ -29,18 +29,12 @@ #!/usr/bin/env python3 | ||
| import re | ||
| import sys | ||
| from optparse import OptionParser | ||
| import sys | ||
| import re | ||
| from testtools import ExtendedToStreamDecorator, StreamToExtendedDecorator | ||
| from subunit import ( | ||
| StreamResultToBytes, | ||
| read_test_list, | ||
| ) | ||
| from subunit import StreamResultToBytes, read_test_list | ||
| from subunit.filters import filter_by_result, find_stream | ||
| from subunit.test_results import ( | ||
| and_predicates, | ||
| make_tag_filter, | ||
| TestResultFilter, | ||
| ) | ||
| from subunit.test_results import (TestResultFilter, and_predicates, | ||
| make_tag_filter) | ||
@@ -106,4 +100,4 @@ | ||
| def _compile_re_from_list(l): | ||
| return re.compile("|".join(l), re.MULTILINE) | ||
| def _compile_re_from_list(list): | ||
| return re.compile("|".join(list), re.MULTILINE) | ||
@@ -110,0 +104,0 @@ |
@@ -19,15 +19,10 @@ #!/usr/bin/env python | ||
| import sys | ||
| from optparse import OptionParser | ||
| import sys | ||
| from testtools import ( | ||
| CopyStreamResult, StreamResultRouter, | ||
| StreamSummary) | ||
| from testtools import CopyStreamResult, StreamResultRouter, StreamSummary | ||
| from subunit import ByteStreamToStreamResult | ||
| from subunit.filters import find_stream | ||
| from subunit.test_results import ( | ||
| CatFiles, | ||
| TestIdPrintingResult, | ||
| ) | ||
| from subunit.test_results import CatFiles, TestIdPrintingResult | ||
@@ -34,0 +29,0 @@ |
@@ -20,8 +20,10 @@ #!/usr/bin/env python3 | ||
| import gi | ||
| import sys | ||
| gi.require_version('Gtk', '3.0') | ||
| from gi.repository import Notify | ||
| from testtools import StreamToExtendedDecorator | ||
| from gi.repository import Notify # noqa: E402 | ||
| from testtools import StreamToExtendedDecorator # noqa: E402 | ||
| from subunit import TestResultStats | ||
| from subunit.filters import run_filter_script | ||
| from subunit import TestResultStats # noqa: E402 | ||
| from subunit.filters import run_filter_script # noqa: E402 | ||
@@ -28,0 +30,0 @@ if not Notify.init("Subunit-notify"): |
@@ -23,2 +23,3 @@ #!/usr/bin/env python3 | ||
| def main(): | ||
@@ -25,0 +26,0 @@ sys.exit(output_main()) |
@@ -53,16 +53,12 @@ #!/usr/bin/env python3 | ||
| import gi | ||
| gi.require_version('Gtk', '3.0') | ||
| from gi.repository import Gtk, GObject | ||
| from gi.repository import GObject, Gtk # noqa: E402 | ||
| from testtools import StreamToExtendedDecorator # noqa: E402 | ||
| from testtools import StreamToExtendedDecorator | ||
| from subunit import (PROGRESS_POP, PROGRESS_PUSH, PROGRESS_SET, # noqa: E402 | ||
| ByteStreamToStreamResult) | ||
| from subunit.progress_model import ProgressModel # noqa: E402 | ||
| from subunit import ( | ||
| PROGRESS_POP, | ||
| PROGRESS_PUSH, | ||
| PROGRESS_SET, | ||
| ByteStreamToStreamResult, | ||
| ) | ||
| from subunit.progress_model import ProgressModel | ||
| class GTKTestResult(unittest.TestResult): | ||
@@ -69,0 +65,0 @@ |
@@ -19,8 +19,9 @@ #!/usr/bin/env python | ||
| import sys | ||
| import unittest | ||
| from operator import methodcaller | ||
| from optparse import OptionParser | ||
| import sys | ||
| import unittest | ||
| from testtools import StreamToExtendedDecorator, DecorateTestCaseResult, StreamResultRouter | ||
| from testtools import (DecorateTestCaseResult, StreamResultRouter, | ||
| StreamToExtendedDecorator) | ||
@@ -54,4 +55,4 @@ from subunit import ByteStreamToStreamResult | ||
| if options.progress: | ||
| from bzrlib import ui | ||
| from bzrlib.tests import TextTestRunner | ||
| from bzrlib import ui | ||
| ui.ui_factory = ui.make_ui_for_terminal(None, sys.stdout, sys.stderr) | ||
@@ -58,0 +59,0 @@ runner = TextTestRunner() |
@@ -17,12 +17,9 @@ # subunit: extensions to python unittest to get test results from subprocesses. | ||
| import sys | ||
| from optparse import OptionParser | ||
| import sys | ||
| from extras import safe_hasattr | ||
| from testtools import CopyStreamResult, StreamResult, StreamResultRouter | ||
| from subunit import ( | ||
| DiscardStream, ProtocolTestCase, ByteStreamToStreamResult, | ||
| StreamResultToBytes, | ||
| ) | ||
| from subunit import (ByteStreamToStreamResult, DiscardStream, ProtocolTestCase, | ||
| StreamResultToBytes) | ||
| from subunit.test_results import CatFiles | ||
@@ -187,3 +184,3 @@ | ||
| post_run_hook(result) | ||
| if not safe_hasattr(result, 'wasSuccessful'): | ||
| if not hasattr(result, 'wasSuccessful'): | ||
| result = result.decorated | ||
@@ -190,0 +187,0 @@ if result.wasSuccessful(): |
@@ -28,13 +28,7 @@ #!/usr/bin/python3 | ||
| from testtools import ExtendedToStreamDecorator | ||
| from testtools.run import (BUFFEROUTPUT, CATCHBREAK, FAILFAST, USAGE_AS_MAIN, | ||
| TestProgram, list_test) | ||
| from subunit import StreamResultToBytes | ||
| from subunit.test_results import AutoTimingTestResultDecorator | ||
| from testtools.run import ( | ||
| BUFFEROUTPUT, | ||
| CATCHBREAK, | ||
| FAILFAST, | ||
| list_test, | ||
| TestProgram, | ||
| USAGE_AS_MAIN, | ||
| ) | ||
@@ -93,3 +87,3 @@ | ||
| fileno = self.stream.fileno() | ||
| except: | ||
| except: # noqa: E722 | ||
| fileno = None | ||
@@ -115,7 +109,7 @@ if fileno is not None: | ||
| 'buffer': ''} | ||
| if self.failfast != False: | ||
| if self.failfast is not False: | ||
| usage['failfast'] = FAILFAST | ||
| if self.catchbreak != False: | ||
| if self.catchbreak is not False: | ||
| usage['catchbreak'] = CATCHBREAK | ||
| if self.buffer != False: | ||
| if self.buffer is not False: | ||
| usage['buffer'] = BUFFEROUTPUT | ||
@@ -122,0 +116,0 @@ usage_text = self.USAGE % usage |
@@ -23,10 +23,6 @@ # | ||
| import testtools | ||
| from testtools.content import ( | ||
| text_content, | ||
| TracebackContent, | ||
| ) | ||
| from testtools import StreamResult | ||
| from testtools.testcase import PlaceHolder | ||
| from testtools.content import TracebackContent, text_content | ||
| from subunit import iso8601 | ||
| import iso8601 | ||
| import subunit | ||
@@ -37,3 +33,3 @@ | ||
| # it. | ||
| class TestResultDecorator(object): | ||
| class TestResultDecorator: | ||
| """General pass-through decorator. | ||
@@ -120,3 +116,3 @@ | ||
| def __init__(self, decorated): | ||
| self.super = super(HookedTestResultDecorator, self) | ||
| self.super = super() | ||
| self.super.__init__(decorated) | ||
@@ -196,3 +192,3 @@ | ||
| self._time = None | ||
| super(AutoTimingTestResultDecorator, self).__init__(decorated) | ||
| super().__init__(decorated) | ||
@@ -203,3 +199,3 @@ def _before_event(self): | ||
| return | ||
| time = datetime.datetime.utcnow().replace(tzinfo=iso8601.Utc()) | ||
| time = datetime.datetime.utcnow().replace(tzinfo=iso8601.UTC) | ||
| self.decorated.time(time) | ||
@@ -226,3 +222,3 @@ | ||
| class TagsMixin(object): | ||
| class TagsMixin: | ||
@@ -286,3 +282,3 @@ def __init__(self): | ||
| def __init__(self, result): | ||
| super(TagCollapsingDecorator, self).__init__(result) | ||
| super().__init__(result) | ||
| self._clear_tags() | ||
@@ -301,3 +297,3 @@ | ||
| def __init__(self, decorated): | ||
| super(TimeCollapsingDecorator, self).__init__(decorated) | ||
| super().__init__(decorated) | ||
| self._last_received_time = None | ||
@@ -348,3 +344,3 @@ self._last_sent_time = None | ||
| def __init__(self, result, predicate): | ||
| super(_PredicateFilter, self).__init__(result) | ||
| super().__init__(result) | ||
| self._clear_tags() | ||
@@ -438,3 +434,3 @@ self.decorated = TimeCollapsingDecorator( | ||
| else: | ||
| return super(_PredicateFilter, self).tags(new_tags, gone_tags) | ||
| return super().tags(new_tags, gone_tags) | ||
@@ -510,3 +506,3 @@ def time(self, a_time): | ||
| predicate = and_predicates(predicates) | ||
| super(TestResultFilter, self).__init__( | ||
| super().__init__( | ||
| _PredicateFilter(result, predicate)) | ||
@@ -524,3 +520,3 @@ if fixup_expected_failures is None: | ||
| else: | ||
| super(TestResultFilter, self).addError( | ||
| super().addError( | ||
| test, err=err, details=details) | ||
@@ -533,3 +529,3 @@ | ||
| else: | ||
| super(TestResultFilter, self).addFailure( | ||
| super().addFailure( | ||
| test, err=err, details=details) | ||
@@ -542,3 +538,3 @@ | ||
| else: | ||
| super(TestResultFilter, self).addSuccess(test, details=details) | ||
| super().addSuccess(test, details=details) | ||
@@ -565,3 +561,3 @@ def _failure_expected(self, test): | ||
| """Create a FilterResult object outputting to stream.""" | ||
| super(TestIdPrintingResult, self).__init__() | ||
| super().__init__() | ||
| self._stream = stream | ||
@@ -668,7 +664,7 @@ self.show_exists = show_exists | ||
| """ | ||
| super(TestByTestResult, self).__init__() | ||
| super().__init__() | ||
| self._on_test = on_test | ||
| def startTest(self, test): | ||
| super(TestByTestResult, self).startTest(test) | ||
| super().startTest(test) | ||
| self._start_time = self._now() | ||
@@ -683,3 +679,3 @@ # There's no supported (i.e. tested) behaviour that relies on these | ||
| self._stop_time = self._now() | ||
| super(TestByTestResult, self).stopTest(test) | ||
| super().stopTest(test) | ||
| self._on_test( | ||
@@ -700,3 +696,3 @@ test=test, | ||
| def addSuccess(self, test, details=None): | ||
| super(TestByTestResult, self).addSuccess(test) | ||
| super().addSuccess(test) | ||
| self._status = 'success' | ||
@@ -706,3 +702,3 @@ self._details = details | ||
| def addFailure(self, test, err=None, details=None): | ||
| super(TestByTestResult, self).addFailure(test, err, details) | ||
| super().addFailure(test, err, details) | ||
| self._status = 'failure' | ||
@@ -712,3 +708,3 @@ self._details = self._err_to_details(test, err, details) | ||
| def addError(self, test, err=None, details=None): | ||
| super(TestByTestResult, self).addError(test, err, details) | ||
| super().addError(test, err, details) | ||
| self._status = 'error' | ||
@@ -718,3 +714,3 @@ self._details = self._err_to_details(test, err, details) | ||
| def addSkip(self, test, reason=None, details=None): | ||
| super(TestByTestResult, self).addSkip(test, reason, details) | ||
| super().addSkip(test, reason, details) | ||
| self._status = 'skip' | ||
@@ -729,3 +725,3 @@ if details is None: | ||
| def addExpectedFailure(self, test, err=None, details=None): | ||
| super(TestByTestResult, self).addExpectedFailure(test, err, details) | ||
| super().addExpectedFailure(test, err, details) | ||
| self._status = 'xfail' | ||
@@ -735,3 +731,3 @@ self._details = self._err_to_details(test, err, details) | ||
| def addUnexpectedSuccess(self, test, details=None): | ||
| super(TestByTestResult, self).addUnexpectedSuccess(test, details) | ||
| super().addUnexpectedSuccess(test, details) | ||
| self._status = 'success' | ||
@@ -744,3 +740,3 @@ self._details = details | ||
| def __init__(self, stream): | ||
| super(CsvResult, self).__init__(self._on_test) | ||
| super().__init__(self._on_test) | ||
| self._write_row = csv.writer(stream).writerow | ||
@@ -752,3 +748,3 @@ | ||
| def startTestRun(self): | ||
| super(CsvResult, self).startTestRun() | ||
| super().startTestRun() | ||
| self._write_row(['test', 'status', 'start_time', 'stop_time']) | ||
@@ -755,0 +751,0 @@ |
@@ -17,3 +17,2 @@ # | ||
| import sys | ||
| from unittest import TestLoader | ||
@@ -23,3 +22,2 @@ | ||
| # Before the test module imports to avoid circularity. | ||
@@ -33,18 +31,8 @@ # For testing: different pythons have different str() implementations. | ||
| from subunit.tests import ( | ||
| test_chunked, | ||
| test_details, | ||
| test_filters, | ||
| test_filter_to_disk, | ||
| test_output_filter, | ||
| test_progress_model, | ||
| test_run, | ||
| test_subunit_filter, | ||
| test_subunit_stats, | ||
| test_subunit_tags, | ||
| test_tap2subunit, | ||
| test_test_protocol, | ||
| test_test_protocol2, | ||
| test_test_results, | ||
| ) | ||
| from subunit.tests import (test_chunked, test_details, test_filter_to_disk, # noqa: E402 | ||
| test_filters, test_output_filter, | ||
| test_progress_model, test_run, test_subunit_filter, | ||
| test_subunit_stats, test_subunit_tags, | ||
| test_tap2subunit, test_test_protocol, | ||
| test_test_protocol2, test_test_results) | ||
@@ -51,0 +39,0 @@ |
| #!/usr/bin/env python3 | ||
| import sys | ||
| if sys.platform == "win32": | ||
| import msvcrt, os | ||
| import msvcrt | ||
| import os | ||
| msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) | ||
@@ -6,0 +8,0 @@ if len(sys.argv) == 2: |
| #!/usr/bin/env python3 | ||
| import sys | ||
| print("test old mcdonald") | ||
@@ -4,0 +5,0 @@ print("success old mcdonald") |
@@ -18,4 +18,4 @@ # | ||
| import unittest | ||
| from io import BytesIO | ||
| import unittest | ||
@@ -22,0 +22,0 @@ from testtools.compat import _b |
@@ -21,3 +21,2 @@ # | ||
| import subunit.tests | ||
| from subunit import content, content_type, details | ||
@@ -47,3 +46,2 @@ | ||
| parser = details.SimpleDetailsParser(None) | ||
| traceback = "" | ||
| expected = {} | ||
@@ -63,3 +61,2 @@ expected['traceback'] = content.Content( | ||
| parser = details.SimpleDetailsParser(None) | ||
| traceback = "" | ||
| expected = {} | ||
@@ -74,3 +71,2 @@ expected['reason'] = content.Content( | ||
| parser = details.SimpleDetailsParser(None) | ||
| traceback = "" | ||
| expected = {} | ||
@@ -77,0 +73,0 @@ expected['message'] = content.Content( |
@@ -21,5 +21,3 @@ # | ||
| from testtools import TestCase | ||
| from testtools.matchers import ( | ||
| FileContains | ||
| ) | ||
| from testtools.matchers import FileContains | ||
@@ -29,2 +27,3 @@ from subunit import _to_disk | ||
| class SmokeTest(TestCase): | ||
@@ -39,3 +38,3 @@ | ||
| writer.status( | ||
| 'foo', 'success', set(['tag']), file_name='fred', | ||
| 'foo', 'success', {'tag'}, file_name='fred', | ||
| file_bytes=b'abcdefg', eof=True, mime_type='text/plain') | ||
@@ -42,0 +41,0 @@ writer.stopTestRun() |
@@ -17,3 +17,2 @@ # | ||
| import sys | ||
| from tempfile import NamedTemporaryFile | ||
@@ -23,7 +22,6 @@ | ||
| from subunit import read_test_list | ||
| from subunit.filters import find_stream | ||
| from subunit import read_test_list | ||
| class TestReadTestList(TestCase): | ||
@@ -30,0 +28,0 @@ |
@@ -18,30 +18,18 @@ # | ||
| import datetime | ||
| import optparse | ||
| from contextlib import contextmanager | ||
| from functools import partial | ||
| from io import BytesIO, StringIO, TextIOWrapper | ||
| import optparse | ||
| import sys | ||
| from io import BytesIO, TextIOWrapper | ||
| from tempfile import NamedTemporaryFile | ||
| from contextlib import contextmanager | ||
| from iso8601 import UTC | ||
| from testtools import TestCase | ||
| from testtools.compat import _u | ||
| from testtools.matchers import ( | ||
| Equals, | ||
| Matcher, | ||
| MatchesAny, | ||
| MatchesListwise, | ||
| Mismatch, | ||
| raises, | ||
| ) | ||
| from testtools.matchers import (Equals, Matcher, MatchesAny, MatchesListwise, | ||
| Mismatch, raises) | ||
| from testtools.testresult.doubles import StreamResult | ||
| from subunit.iso8601 import UTC | ||
| from subunit.v2 import StreamResultToBytes, ByteStreamToStreamResult | ||
| from subunit._output import ( | ||
| _ALL_ACTIONS, | ||
| _FINAL_ACTIONS, | ||
| generate_stream_results, | ||
| parse_arguments, | ||
| ) | ||
| import subunit._output as _o | ||
| from subunit._output import (_ALL_ACTIONS, _FINAL_ACTIONS, | ||
| generate_stream_results, parse_arguments) | ||
@@ -125,3 +113,4 @@ | ||
| def test_requires_test_id(self): | ||
| fn = lambda: safe_parse_arguments(args=[self.option]) | ||
| def fn(): | ||
| return safe_parse_arguments(args=[self.option]) | ||
| self.assertThat( | ||
@@ -143,6 +132,7 @@ fn, | ||
| def test_can_run_without_args(self): | ||
| args = safe_parse_arguments([]) | ||
| safe_parse_arguments([]) | ||
| def test_cannot_specify_more_than_one_status_command(self): | ||
| fn = lambda: safe_parse_arguments(['--fail', 'foo', '--skip', 'bar']) | ||
| def fn(): | ||
| return safe_parse_arguments(["--fail", "foo", "--skip", "bar"]) | ||
| self.assertThat( | ||
@@ -154,3 +144,4 @@ fn, | ||
| def test_cannot_specify_mimetype_without_attach_file(self): | ||
| fn = lambda: safe_parse_arguments(['--mimetype', 'foo']) | ||
| def fn(): | ||
| return safe_parse_arguments(["--mimetype", "foo"]) | ||
| self.assertThat( | ||
@@ -162,3 +153,4 @@ fn, | ||
| def test_cannot_specify_filename_without_attach_file(self): | ||
| fn = lambda: safe_parse_arguments(['--file-name', 'foo']) | ||
| def fn(): | ||
| return safe_parse_arguments(["--file-name", "foo"]) | ||
| self.assertThat( | ||
@@ -174,3 +166,4 @@ fn, | ||
| def test_must_specify_tags_with_tags_options(self): | ||
| fn = lambda: safe_parse_arguments(['--fail', 'foo', '--tag']) | ||
| def fn(): | ||
| return safe_parse_arguments(["--fail", "foo", "--tag"]) | ||
| self.assertThat( | ||
@@ -216,3 +209,3 @@ fn, | ||
| def setUp(self): | ||
| super(StatusStreamResultTests, self).setUp() | ||
| super().setUp() | ||
| self.patch(_o, 'create_timestamp', lambda: self._dummy_timestamp) | ||
@@ -240,3 +233,3 @@ self.test_id = self.getUniqueString() | ||
| result._events[1], | ||
| MatchesStatusCall(test_tags=set(['hello', 'world'])) | ||
| MatchesStatusCall(test_tags={'hello', 'world'}) | ||
| ) | ||
@@ -396,3 +389,3 @@ | ||
| MatchesStatusCall(call='startTestRun'), | ||
| MatchesStatusCall(test_id=self.test_id, test_tags=set(['foo', 'bar'])), | ||
| MatchesStatusCall(test_id=self.test_id, test_tags={'foo', 'bar'}), | ||
| MatchesStatusCall(test_id=self.test_id, test_tags=None), | ||
@@ -537,3 +530,3 @@ MatchesStatusCall(call='stopTestRun'), | ||
| with temp_file_contents(b"Hello") as f: | ||
| specified_file_name = self.getUniqueString() | ||
| self.getUniqueString() | ||
| result = get_result_for([ | ||
@@ -563,3 +556,3 @@ '--attach-file', | ||
| MatchesStatusCall(call='startTestRun'), | ||
| MatchesStatusCall(test_tags=set(['foo'])), | ||
| MatchesStatusCall(test_tags={'foo'}), | ||
| MatchesStatusCall(call='stopTestRun'), | ||
@@ -601,3 +594,3 @@ ]) | ||
| return Mismatch( | ||
| "Value for key is %r, not %r" % (call_tuple[pos], v) | ||
| "Value for key is {!r}, not {!r}".format(call_tuple[pos], v) | ||
| ) | ||
@@ -604,0 +597,0 @@ except IndexError: |
@@ -19,3 +19,2 @@ # | ||
| import subunit | ||
| from subunit.progress_model import ProgressModel | ||
@@ -22,0 +21,0 @@ |
@@ -73,3 +73,3 @@ # | ||
| return [], [] | ||
| class Loader(object): | ||
| class Loader: | ||
| errors = ['failed import'] | ||
@@ -76,0 +76,0 @@ loader = Loader() |
@@ -19,15 +19,14 @@ # | ||
| from datetime import datetime | ||
| from io import BytesIO | ||
| import os | ||
| import subprocess | ||
| import sys | ||
| import unittest | ||
| from datetime import datetime | ||
| from io import BytesIO | ||
| from testtools import TestCase | ||
| from testtools.compat import _b | ||
| from testtools import TestCase | ||
| from testtools.testresult.doubles import ExtendedTestResult, StreamResult | ||
| import iso8601 | ||
| import subunit | ||
| from subunit import iso8601 | ||
| from subunit.test_results import make_tag_filter, TestResultFilter | ||
@@ -97,3 +96,3 @@ from subunit import ByteStreamToStreamResult, StreamResultToBytes | ||
| ['passed', 'error', 'skipped', 'todo'])) | ||
| self.assertEquals(tests_expected, tests_included) | ||
| self.assertEqual(tests_expected, tests_included) | ||
@@ -113,9 +112,10 @@ def test_tags_tracked_correctly(self): | ||
| foo = subunit.RemotedTestCase('foo') | ||
| self.assertEquals( | ||
| self.assertEqual( | ||
| [('startTest', foo), | ||
| ('tags', set(['a']), set()), | ||
| ('tags', {'a'}, set()), | ||
| ('addSuccess', foo), | ||
| ('stopTest', foo), | ||
| ], | ||
| result._events) | ||
| result._events | ||
| ) | ||
@@ -136,3 +136,3 @@ def test_exclude_errors(self): | ||
| result_filter = TestResultFilter(filtered_result, | ||
| fixup_expected_failures=set(["failed"])) | ||
| fixup_expected_failures={"failed"}) | ||
| self.run_tests(result_filter) | ||
@@ -147,3 +147,3 @@ self.assertEqual(['failed', 'todo'], | ||
| result_filter = TestResultFilter(filtered_result, | ||
| fixup_expected_failures=set(["error"])) | ||
| fixup_expected_failures={"error"}) | ||
| self.run_tests(result_filter) | ||
@@ -158,3 +158,3 @@ self.assertEqual(['error', 'todo'], | ||
| result_filter = TestResultFilter(filtered_result, filter_success=False, | ||
| fixup_expected_failures=set(["passed"])) | ||
| fixup_expected_failures={"passed"}) | ||
| self.run_tests(result_filter) | ||
@@ -268,3 +268,3 @@ self.assertEqual(['passed'], | ||
| result_filter.stopTestRun() | ||
| foo = subunit.RemotedTestCase('foo') | ||
| subunit.RemotedTestCase('foo') | ||
| self.maxDiff = None | ||
@@ -286,6 +286,8 @@ self.assertEqual( | ||
| foo = subunit.RemotedTestCase('foo') | ||
| self.assertEquals( | ||
| self.assertEqual( | ||
| [('startTest', foo), | ||
| ('addSkip', foo, {}), | ||
| ('stopTest', foo), ], result._events) | ||
| ('stopTest', foo), ], | ||
| result._events | ||
| ) | ||
@@ -302,7 +304,8 @@ def test_renames(self): | ||
| self.run_tests(result_filter, input_stream) | ||
| self.assertEquals( | ||
| self.assertEqual( | ||
| [('startTest', 'foo - renamed'), | ||
| ('addSuccess', 'foo - renamed'), | ||
| ('stopTest', 'foo - renamed')], | ||
| [(ev[0], ev[1].id()) for ev in result._events]) | ||
| [(ev[0], ev[1].id()) for ev in result._events] | ||
| ) | ||
@@ -319,3 +322,3 @@ | ||
| if ps.returncode != 0: | ||
| raise RuntimeError("%s failed: %s" % (command, err)) | ||
| raise RuntimeError("{} failed: {}".format(command, err)) | ||
| return out | ||
@@ -331,3 +334,3 @@ | ||
| ByteStreamToStreamResult(BytesIO(output)).run(events) | ||
| ids = set(event[1] for event in events._events) | ||
| {event[1] for event in events._events} | ||
| self.assertEqual([ | ||
@@ -342,11 +345,11 @@ ('status', 'foo', 'inprogress'), | ||
| stream.status( | ||
| test_id="foo", test_status="inprogress", test_tags=set(["a"])) | ||
| test_id="foo", test_status="inprogress", test_tags={"a"}) | ||
| stream.status( | ||
| test_id="foo", test_status="success", test_tags=set(["a"])) | ||
| test_id="foo", test_status="success", test_tags={"a"}) | ||
| stream.status(test_id="bar", test_status="inprogress") | ||
| stream.status(test_id="bar", test_status="inprogress") | ||
| stream.status( | ||
| test_id="baz", test_status="inprogress", test_tags=set(["a"])) | ||
| test_id="baz", test_status="inprogress", test_tags={"a"}) | ||
| stream.status( | ||
| test_id="baz", test_status="success", test_tags=set(["a"])) | ||
| test_id="baz", test_status="success", test_tags={"a"}) | ||
| output = self.run_command( | ||
@@ -356,4 +359,4 @@ ['-s', '--with-tag', 'a'], byte_stream.getvalue()) | ||
| ByteStreamToStreamResult(BytesIO(output)).run(events) | ||
| ids = set(event[1] for event in events._events) | ||
| self.assertEqual(set(['foo', 'baz']), ids) | ||
| ids = {event[1] for event in events._events} | ||
| self.assertEqual({'foo', 'baz'}, ids) | ||
@@ -360,0 +363,0 @@ def test_no_passthrough(self): |
@@ -19,5 +19,4 @@ # | ||
| from io import BytesIO | ||
| from io import StringIO | ||
| import unittest | ||
| from io import BytesIO, StringIO | ||
@@ -69,3 +68,3 @@ from testtools.compat import _b | ||
| self.assertEqual(1, self.result.skipped_tests) | ||
| self.assertEqual(set(["global", "local"]), self.result.seen_tags) | ||
| self.assertEqual({"global", "local"}, self.result.seen_tags) | ||
@@ -72,0 +71,0 @@ def test_stat_formatting(self): |
@@ -31,3 +31,3 @@ # | ||
| def setUp(self): | ||
| super(TestSubUnitTags, self).setUp() | ||
| super().setUp() | ||
| self.original = BytesIO() | ||
@@ -65,9 +65,9 @@ self.filtered = BytesIO() | ||
| stream.status( | ||
| test_id='test', test_status='inprogress', test_tags=set(['foo'])) | ||
| test_id='test', test_status='inprogress', test_tags={'foo'}) | ||
| stream.status( | ||
| test_id='test', test_status='success', test_tags=set(['foo', 'bar'])) | ||
| test_id='test', test_status='success', test_tags={'foo', 'bar'}) | ||
| self.original.seek(0) | ||
| self.assertEqual( | ||
| 0, subunit.tag_stream(self.original, self.filtered, ["quux"])) | ||
| self.assertThat(reference, Contains(self.filtered.getvalue())) | ||
| self.assertThat(reference, Contains(bytes(self.filtered.getvalue()))) | ||
@@ -78,10 +78,10 @@ def test_remove_tag(self): | ||
| stream.status( | ||
| test_id='test', test_status='inprogress', test_tags=set(['foo'])) | ||
| test_id='test', test_status='inprogress', test_tags={'foo'}) | ||
| stream.status( | ||
| test_id='test', test_status='success', test_tags=set(['foo'])) | ||
| test_id='test', test_status='success', test_tags={'foo'}) | ||
| stream = subunit.StreamResultToBytes(self.original) | ||
| stream.status( | ||
| test_id='test', test_status='inprogress', test_tags=set(['foo'])) | ||
| test_id='test', test_status='inprogress', test_tags={'foo'}) | ||
| stream.status( | ||
| test_id='test', test_status='success', test_tags=set(['foo', 'bar'])) | ||
| test_id='test', test_status='success', test_tags={'foo', 'bar'}) | ||
| self.original.seek(0) | ||
@@ -88,0 +88,0 @@ self.assertEqual( |
@@ -20,3 +20,2 @@ # | ||
| from io import BytesIO, StringIO | ||
| import unittest | ||
@@ -43,3 +42,3 @@ from testtools import TestCase | ||
| def setUp(self): | ||
| super(TestTAP2SubUnit, self).setUp() | ||
| super().setUp() | ||
| self.tap = StringIO() | ||
@@ -46,0 +45,0 @@ self.subunit = BytesIO() |
@@ -19,18 +19,16 @@ # | ||
| import io | ||
| from io import BytesIO | ||
| from io import StringIO | ||
| import os | ||
| import tempfile | ||
| import unittest | ||
| from io import BytesIO | ||
| from testtools import PlaceHolder, skipIf, TestCase, TestResult | ||
| from testtools import PlaceHolder, TestCase, TestResult, skipIf | ||
| from testtools.compat import _b, _u | ||
| from testtools.content import Content, TracebackContent, text_content | ||
| from testtools.content_type import ContentType | ||
| try: | ||
| from testtools.testresult.doubles import ( | ||
| Python26TestResult, | ||
| Python27TestResult, | ||
| ExtendedTestResult, | ||
| ) | ||
| from testtools.testresult.doubles import (ExtendedTestResult, | ||
| Python26TestResult, | ||
| Python27TestResult) | ||
| except ImportError: | ||
@@ -42,14 +40,13 @@ from testtools.tests.helpers import ( | ||
| ) | ||
| from testtools.matchers import Contains, Equals, MatchesAny | ||
| import iso8601 | ||
| import subunit | ||
| from subunit.tests import ( | ||
| _remote_exception_repr, | ||
| _remote_exception_repr_chunked, | ||
| _remote_exception_str, | ||
| _remote_exception_str_chunked, | ||
| ) | ||
| import subunit.iso8601 as iso8601 | ||
| from subunit.tests import (_remote_exception_repr, | ||
| _remote_exception_repr_chunked, | ||
| _remote_exception_str, | ||
| _remote_exception_str_chunked) | ||
| tb_prelude = "Traceback (most recent call last):\n" | ||
@@ -95,10 +92,5 @@ | ||
| def test_imports(self): | ||
| from subunit import DiscardStream | ||
| from subunit import TestProtocolServer | ||
| from subunit import RemotedTestCase | ||
| from subunit import RemoteError | ||
| from subunit import ExecTestCase | ||
| from subunit import IsolatedTestCase | ||
| from subunit import TestProtocolClient | ||
| from subunit import ProtocolTestCase | ||
| from subunit import (DiscardStream, ExecTestCase, IsolatedTestCase, # noqa: F401 | ||
| ProtocolTestCase, RemotedTestCase, RemoteError, # noqa: F401 | ||
| TestProtocolClient, TestProtocolServer) # noqa: F401 | ||
@@ -371,3 +363,3 @@ | ||
| self.protocol.lineReceived(_b("test old mcdonald\n")) | ||
| self.protocol.lineReceived(_b("%s old mcdonald %s" % (outcome, opening))) | ||
| self.protocol.lineReceived(_b("{} old mcdonald {}".format(outcome, opening))) | ||
| self.protocol.lostConnection() | ||
@@ -454,4 +446,6 @@ failure = subunit.RemoteError( | ||
| self.assertNotEqual(None, parser) | ||
| self.assertTrue(isinstance(parser, | ||
| subunit.details.MultipartDetailsParser)) | ||
| self.assertIsInstance( | ||
| parser, | ||
| subunit.details.MultipartDetailsParser | ||
| ) | ||
@@ -948,3 +942,3 @@ | ||
| self.assertEqual([ | ||
| ('tags', set(["foo", "bar:baz", "quux"]), set()), | ||
| ('tags', {"foo", "bar:baz", "quux"}, set()), | ||
| ], self.client._events) | ||
@@ -955,3 +949,3 @@ | ||
| self.assertEqual([ | ||
| ('tags', set(["quux"]), set(["bar"])), | ||
| ('tags', {"quux"}, {"bar"}), | ||
| ], self.client._events) | ||
@@ -998,3 +992,3 @@ | ||
| ('time', datetime.datetime(2001, 12, 12, 12, 59, 59, 0, | ||
| iso8601.Utc())) | ||
| iso8601.UTC)) | ||
| ], self.result._events) | ||
@@ -1192,3 +1186,3 @@ | ||
| def setUp(self): | ||
| super(TestTestProtocolClient, self).setUp() | ||
| super().setUp() | ||
| self.io = BytesIO() | ||
@@ -1405,3 +1399,3 @@ self.protocol = subunit.TestProtocolClient(self.io) | ||
| self.protocol.time( | ||
| datetime.datetime(2009,10,11,12,13,14,15, iso8601.Utc())) | ||
| datetime.datetime(2009,10,11,12,13,14,15, iso8601.UTC)) | ||
| self.assertEqual( | ||
@@ -1431,7 +1425,7 @@ _b("time: 2009-10-11 12:13:14.000015Z\n"), | ||
| def test_tags_add(self): | ||
| self.protocol.tags(set(['foo']), set()) | ||
| self.protocol.tags({'foo'}, set()) | ||
| self.assertEqual(_b("tags: foo\n"), self.io.getvalue()) | ||
| def test_tags_both(self): | ||
| self.protocol.tags(set(['quux']), set(['bar'])) | ||
| self.protocol.tags({'quux'}, {'bar'}) | ||
| self.assertThat( | ||
@@ -1442,3 +1436,3 @@ [b"tags: quux -bar\n", b"tags: -bar quux\n"], | ||
| def test_tags_gone(self): | ||
| self.protocol.tags(set(), set(['bar'])) | ||
| self.protocol.tags(set(), {'bar'}) | ||
| self.assertEqual(_b("tags: -bar\n"), self.io.getvalue()) |
@@ -17,10 +17,10 @@ # | ||
| import datetime | ||
| from io import BytesIO | ||
| import datetime | ||
| try: | ||
| from hypothesis import given | ||
| # To debug hypothesis | ||
| # from hypothesis import Settings, Verbosity | ||
| # Settings.default.verbosity = Verbosity.verbose | ||
| # To debug hypothesis | ||
| # from hypothesis import Settings, Verbosity | ||
| # Settings.default.verbosity = Verbosity.verbose | ||
| import hypothesis.strategies as st | ||
@@ -32,7 +32,7 @@ except ImportError: | ||
| from testtools.matchers import Contains, HasLength | ||
| from testtools.testresult.doubles import StreamResult | ||
| from testtools.tests.test_testresult import TestStreamResultContract | ||
| from testtools.testresult.doubles import StreamResult | ||
| import subunit | ||
| import subunit.iso8601 as iso8601 | ||
| import iso8601 | ||
@@ -218,3 +218,3 @@ CONSTANT_ENUM = b'\xb3)\x01\x0c\x03foo\x08U_\x1b' | ||
| result, output = self._make_result() | ||
| result.status(test_id="bar", test_tags=set(['foo', 'bar'])) | ||
| result.status(test_id="bar", test_tags={'foo', 'bar'}) | ||
| self.assertThat(CONSTANT_TAGS, Contains(output.getvalue())) | ||
@@ -224,3 +224,3 @@ | ||
| timestamp = datetime.datetime(2001, 12, 12, 12, 59, 59, 45, | ||
| iso8601.Utc()) | ||
| iso8601.UTC) | ||
| result, output = self._make_result() | ||
@@ -319,6 +319,6 @@ result.status(test_id="bar", test_status='success', timestamp=timestamp) | ||
| self.assertEqual(events, result._events) | ||
| #- any file attachments should be byte contents [as users assume that]. | ||
| #- any file attachments should be bytes equivalent [as users assume that]. | ||
| for event in result._events: | ||
| if event[5] is not None: | ||
| self.assertIsInstance(event[6], bytes) | ||
| bytes(event[6]) | ||
@@ -386,7 +386,7 @@ def check_event(self, source_bytes, test_status=None, test_id="foo", | ||
| self.check_event(CONSTANT_TAGS[0], | ||
| None, tags=set(['foo', 'bar']), test_id="bar") | ||
| None, tags={'foo', 'bar'}, test_id="bar") | ||
| def test_timestamp(self): | ||
| timestamp = datetime.datetime(2001, 12, 12, 12, 59, 59, 45, | ||
| iso8601.Utc()) | ||
| iso8601.UTC) | ||
| self.check_event(CONSTANT_TIMESTAMP, | ||
@@ -393,0 +393,0 @@ 'success', test_id='bar', timestamp=timestamp) |
@@ -19,20 +19,16 @@ # | ||
| import datetime | ||
| from io import StringIO | ||
| import sys | ||
| import unittest | ||
| from io import StringIO | ||
| import testtools | ||
| from testtools import TestCase | ||
| from testtools.content import ( | ||
| text_content, | ||
| TracebackContent, | ||
| ) | ||
| from testtools.content import TracebackContent, text_content | ||
| from testtools.testresult.doubles import ExtendedTestResult | ||
| import subunit | ||
| import subunit.iso8601 as iso8601 | ||
| import iso8601 | ||
| import subunit.test_results | ||
| import testtools | ||
| class LoggingDecorator(subunit.test_results.HookedTestResultDecorator): | ||
@@ -42,3 +38,3 @@ | ||
| self._calls = 0 | ||
| super(LoggingDecorator, self).__init__(decorated) | ||
| super().__init__(decorated) | ||
@@ -54,7 +50,7 @@ def _before_event(self): | ||
| self.test = test | ||
| super(AssertBeforeTestResult, self).__init__(decorated) | ||
| super().__init__(decorated) | ||
| def _before_event(self): | ||
| self.test.assertEqual(1, self.earlier._calls) | ||
| super(AssertBeforeTestResult, self)._before_event() | ||
| super()._before_event() | ||
@@ -65,3 +61,3 @@ | ||
| def __init__(self): | ||
| super(TimeCapturingResult, self).__init__() | ||
| super().__init__() | ||
| self._calls = [] | ||
@@ -185,3 +181,3 @@ self.failfast = False | ||
| # automatically adding one when other methods are called. | ||
| time = datetime.datetime(2009,10,11,12,13,14,15, iso8601.Utc()) | ||
| time = datetime.datetime(2009,10,11,12,13,14,15, iso8601.UTC) | ||
| self.result.time(time) | ||
@@ -194,3 +190,3 @@ self.result.startTest(self) | ||
| def test_calling_time_None_enables_automatic_time(self): | ||
| time = datetime.datetime(2009,10,11,12,13,14,15, iso8601.Utc()) | ||
| time = datetime.datetime(2009,10,11,12,13,14,15, iso8601.UTC) | ||
| self.result.time(time) | ||
@@ -219,9 +215,11 @@ self.assertEqual(1, len(self.decorated._calls)) | ||
| tag_collapser = subunit.test_results.TagCollapsingDecorator(result) | ||
| tag_collapser.tags(set(['a']), set()) | ||
| tag_collapser.tags(set(['b']), set()) | ||
| tag_collapser.tags({'a'}, set()) | ||
| tag_collapser.tags({'b'}, set()) | ||
| tag_collapser.startTest(self) | ||
| self.assertEquals( | ||
| [('tags', set(['a', 'b']), set([])), | ||
| self.assertEqual( | ||
| [('tags', {'a', 'b'}, set()), | ||
| ('startTest', self), | ||
| ], result._events) | ||
| ], | ||
| result._events | ||
| ) | ||
@@ -232,4 +230,4 @@ def test_tags_collapsed_outside_of_tests_are_flushed(self): | ||
| tag_collapser.startTestRun() | ||
| tag_collapser.tags(set(['a']), set()) | ||
| tag_collapser.tags(set(['b']), set()) | ||
| tag_collapser.tags({'a'}, set()) | ||
| tag_collapser.tags({'b'}, set()) | ||
| tag_collapser.startTest(self) | ||
@@ -239,5 +237,5 @@ tag_collapser.addSuccess(self) | ||
| tag_collapser.stopTestRun() | ||
| self.assertEquals( | ||
| self.assertEqual( | ||
| [('startTestRun',), | ||
| ('tags', set(['a', 'b']), set([])), | ||
| ('tags', {'a', 'b'}, set()), | ||
| ('startTest', self), | ||
@@ -247,3 +245,5 @@ ('addSuccess', self), | ||
| ('stopTestRun',), | ||
| ], result._events) | ||
| ], | ||
| result._events | ||
| ) | ||
@@ -258,3 +258,3 @@ def test_tags_forwarded_after_tests(self): | ||
| tag_collapser.stopTest(test) | ||
| tag_collapser.tags(set(['a']), set(['b'])) | ||
| tag_collapser.tags({'a'}, {'b'}) | ||
| tag_collapser.stopTestRun() | ||
@@ -266,3 +266,3 @@ self.assertEqual( | ||
| ('stopTest', test), | ||
| ('tags', set(['a']), set(['b'])), | ||
| ('tags', {'a'}, {'b'}), | ||
| ('stopTestRun',), | ||
@@ -277,11 +277,12 @@ ], | ||
| tag_collapser.startTest(test) | ||
| tag_collapser.tags(set(['a']), set()) | ||
| tag_collapser.tags(set(['b']), set(['a'])) | ||
| tag_collapser.tags(set(['c']), set()) | ||
| tag_collapser.tags({'a'}, set()) | ||
| tag_collapser.tags({'b'}, {'a'}) | ||
| tag_collapser.tags({'c'}, set()) | ||
| tag_collapser.stopTest(test) | ||
| self.assertEquals( | ||
| self.assertEqual( | ||
| [('startTest', test), | ||
| ('tags', set(['b', 'c']), set(['a'])), | ||
| ('tags', {'b', 'c'}, {'a'}), | ||
| ('stopTest', test)], | ||
| result._events) | ||
| result._events | ||
| ) | ||
@@ -293,11 +294,12 @@ def test_tags_collapsed_inside_of_tests_different_ordering(self): | ||
| tag_collapser.startTest(test) | ||
| tag_collapser.tags(set(), set(['a'])) | ||
| tag_collapser.tags(set(['a', 'b']), set()) | ||
| tag_collapser.tags(set(['c']), set()) | ||
| tag_collapser.tags(set(), {'a'}) | ||
| tag_collapser.tags({'a', 'b'}, set()) | ||
| tag_collapser.tags({'c'}, set()) | ||
| tag_collapser.stopTest(test) | ||
| self.assertEquals( | ||
| self.assertEqual( | ||
| [('startTest', test), | ||
| ('tags', set(['a', 'b', 'c']), set()), | ||
| ('tags', {'a', 'b', 'c'}, set()), | ||
| ('stopTest', test)], | ||
| result._events) | ||
| result._events | ||
| ) | ||
@@ -313,11 +315,12 @@ def test_tags_sent_before_result(self): | ||
| tag_collapser.startTest(test) | ||
| tag_collapser.tags(set(['a']), set()) | ||
| tag_collapser.tags({'a'}, set()) | ||
| tag_collapser.addSuccess(test) | ||
| tag_collapser.stopTest(test) | ||
| self.assertEquals( | ||
| self.assertEqual( | ||
| [('startTest', test), | ||
| ('tags', set(['a']), set()), | ||
| ('tags', {'a'}, set()), | ||
| ('addSuccess', test), | ||
| ('stopTest', test)], | ||
| result._events) | ||
| result._events | ||
| ) | ||
@@ -338,3 +341,3 @@ | ||
| tag_collapser.time(a_time) | ||
| self.assertEquals([('time', a_time)], result._events) | ||
| self.assertEqual([('time', a_time)], result._events) | ||
@@ -350,4 +353,6 @@ def test_time_collapsed_to_first_and_last(self): | ||
| tag_collapser.startTest(subunit.RemotedTestCase('foo')) | ||
| self.assertEquals( | ||
| [('time', times[0]), ('time', times[-1])], result._events[:-1]) | ||
| self.assertEqual( | ||
| [('time', times[0]), ('time', times[-1])], | ||
| result._events[:-1] | ||
| ) | ||
@@ -362,3 +367,3 @@ def test_only_one_time_sent(self): | ||
| tag_collapser.startTest(subunit.RemotedTestCase('foo')) | ||
| self.assertEquals([('time', a_time)], result._events[:-1]) | ||
| self.assertEqual([('time', a_time)], result._events[:-1]) | ||
@@ -374,3 +379,3 @@ def test_duplicate_times_not_sent(self): | ||
| tag_collapser.startTest(subunit.RemotedTestCase('foo')) | ||
| self.assertEquals([('time', a_time)], result._events[:-1]) | ||
| self.assertEqual([('time', a_time)], result._events[:-1]) | ||
@@ -386,7 +391,9 @@ def test_no_times_inserted(self): | ||
| tag_collapser.stopTest(foo) | ||
| self.assertEquals( | ||
| self.assertEqual( | ||
| [('time', a_time), | ||
| ('startTest', foo), | ||
| ('addSuccess', foo), | ||
| ('stopTest', foo)], result._events) | ||
| ('stopTest', foo)], | ||
| result._events | ||
| ) | ||
@@ -397,3 +404,3 @@ | ||
| def setUp(self): | ||
| super(TestByTestResultTests, self).setUp() | ||
| super().setUp() | ||
| self.log = [] | ||
@@ -442,3 +449,3 @@ self.result = subunit.test_results.TestByTestResult(self.on_test) | ||
| self.result.stopTest(self) | ||
| self.assertCalled(status='success', tags=set(['foo'])) | ||
| self.assertCalled(status='success', tags={'foo'}) | ||
@@ -445,0 +452,0 @@ def test_add_error(self): |
+12
-39
@@ -17,2 +17,3 @@ # | ||
| import builtins | ||
| import codecs | ||
@@ -25,7 +26,4 @@ import datetime | ||
| from extras import safe_hasattr, try_imports | ||
| builtins = try_imports(['__builtin__', 'builtins']) | ||
| import subunit | ||
| import subunit.iso8601 as iso8601 | ||
| import iso8601 | ||
@@ -53,3 +51,3 @@ utf_8_decode = codecs.utf_8_decode | ||
| FLAG_FILE_CONTENT = 0x0040 | ||
| EPOCH = datetime.datetime.utcfromtimestamp(0).replace(tzinfo=iso8601.Utc()) | ||
| EPOCH = datetime.datetime.utcfromtimestamp(0).replace(tzinfo=iso8601.UTC) | ||
| NUL_ELEMENT = b'\0'[0] | ||
@@ -60,18 +58,2 @@ # Contains True for types for which 'nul in thing' falsely returns false. | ||
| def has_nul(buffer_or_bytes): | ||
| """Return True if a null byte is present in buffer_or_bytes.""" | ||
| # Simple "if NUL_ELEMENT in utf8_bytes:" fails on Python 3.1 and 3.2 with | ||
| # memoryviews. See https://bugs.launchpad.net/subunit/+bug/1216246 | ||
| buffer_type = type(buffer_or_bytes) | ||
| broken = _nul_test_broken.get(buffer_type) | ||
| if broken is None: | ||
| reference = buffer_type(b'\0') | ||
| broken = not NUL_ELEMENT in reference | ||
| _nul_test_broken[buffer_type] = broken | ||
| if broken: | ||
| return b'\0' in buffer_or_bytes | ||
| else: | ||
| return NUL_ELEMENT in buffer_or_bytes | ||
| def read_exactly(stream, size): | ||
@@ -339,3 +321,3 @@ """Read exactly size bytes from stream. | ||
| self.source.fileno() | ||
| except: | ||
| except: # noqa: E722 | ||
| # Won't be able to select, fallback to | ||
@@ -394,11 +376,2 @@ # one-byte-at-a-time. | ||
| def _to_bytes(self, data, pos, length): | ||
| """Return a slice of data from pos for length as bytes.""" | ||
| # memoryview in 2.7.3 and 3.2 isn't directly usable with struct :(. | ||
| # see https://bugs.launchpad.net/subunit/+bug/1216163 | ||
| result = data[pos:pos+length] | ||
| if type(result) is not bytes: | ||
| return result.tobytes() | ||
| return result | ||
| def _parse_varint(self, data, pos, max_3_bytes=False): | ||
@@ -409,3 +382,3 @@ # because the only incremental IO we do is at the start, and the 32 bit | ||
| # error not a normal situation. | ||
| data_0 = struct.unpack(FMT_8, self._to_bytes(data, pos, 1))[0] | ||
| data_0 = struct.unpack(FMT_8, data[pos:pos+1])[0] | ||
| typeenum = data_0 & 0xc0 | ||
@@ -416,6 +389,6 @@ value_0 = data_0 & 0x3f | ||
| elif typeenum == 0x40: | ||
| data_1 = struct.unpack(FMT_8, self._to_bytes(data, pos+1, 1))[0] | ||
| data_1 = struct.unpack(FMT_8, data[pos+1:pos+2])[0] | ||
| return (value_0 << 8) | data_1, 2 | ||
| elif typeenum == 0x80: | ||
| data_1 = struct.unpack(FMT_16, self._to_bytes(data, pos+1, 2))[0] | ||
| data_1 = struct.unpack(FMT_16, data[pos+1:pos+3])[0] | ||
| return (value_0 << 16) | data_1, 3 | ||
@@ -425,3 +398,3 @@ else: | ||
| raise ParseError('3 byte maximum given but 4 byte value found.') | ||
| data_1, data_2 = struct.unpack(FMT_24, self._to_bytes(data, pos+1, 3)) | ||
| data_1, data_2 = struct.unpack(FMT_24, data[pos+1:pos+4]) | ||
| result = (value_0 << 24) | data_1 << 8 | data_2 | ||
@@ -460,3 +433,3 @@ return result, 4 | ||
| if safe_hasattr(builtins, 'memoryview'): | ||
| if hasattr(builtins, 'memoryview'): | ||
| body = memoryview(packet[-1]) | ||
@@ -472,3 +445,3 @@ else: | ||
| if flags & FLAG_TIMESTAMP: | ||
| seconds = struct.unpack(FMT_32, self._to_bytes(body, pos, 4))[0] | ||
| seconds = struct.unpack(FMT_32, body[pos:pos+4])[0] | ||
| nanoseconds, consumed = self._parse_varint(body, pos+4) | ||
@@ -505,3 +478,3 @@ pos = pos + 4 + consumed | ||
| pos += consumed | ||
| file_bytes = self._to_bytes(body, pos, content_length) | ||
| file_bytes = body[pos:pos+content_length] | ||
| if len(file_bytes) != content_length: | ||
@@ -541,3 +514,3 @@ raise ParseError('File content extends past end of packet: ' | ||
| len(utf8_bytes))) | ||
| if has_nul(utf8_bytes): | ||
| if NUL_ELEMENT in utf8_bytes: | ||
| raise ParseError('UTF8 string at offset %d contains NUL byte' % ( | ||
@@ -544,0 +517,0 @@ pos-2,)) |
+1
-4
@@ -18,5 +18,2 @@ | ||
| subunit reuses iso8601 by Michael Twomey, distributed under an MIT style | ||
| licence - see python/iso8601/LICENSE for details. | ||
| Subunit | ||
@@ -491,5 +488,5 @@ ------- | ||
| * Do a make distcheck, which will update Makefile etc. | ||
| * Do a PyPI release: PYTHONPATH=../../python python ../../setup.py sdist bdist_wheel upload -s | ||
| * Do a PyPI release: PYTHONPATH=python python setup.py sdist bdist_wheel; twine upload -s dist/* | ||
| * Upload the regular one to LP. | ||
| * Push a tagged commit. | ||
| git push -t origin master:master |
+4
-3
| #!/usr/bin/env python3 | ||
| import os.path | ||
| from setuptools import setup | ||
@@ -40,3 +41,2 @@ | ||
| 'Programming Language :: Python :: 3', | ||
| 'Programming Language :: Python :: 3.6', | ||
| 'Programming Language :: Python :: 3.7', | ||
@@ -46,2 +46,3 @@ 'Programming Language :: Python :: 3.8', | ||
| 'Programming Language :: Python :: 3.10', | ||
| 'Programming Language :: Python :: 3.11', | ||
| 'Topic :: Software Development :: Testing', | ||
@@ -61,5 +62,5 @@ ], | ||
| package_dir={'subunit': 'python/subunit'}, | ||
| python_requires=">=3.6", | ||
| python_requires=">=3.7", | ||
| install_requires=[ | ||
| 'extras', | ||
| 'iso8601', | ||
| 'testtools>=0.9.34', | ||
@@ -66,0 +67,0 @@ ], |
| Copyright (c) 2007 Michael Twomey | ||
| Permission is hereby granted, free of charge, to any person obtaining a | ||
| copy of this software and associated documentation files (the | ||
| "Software"), to deal in the Software without restriction, including | ||
| without limitation the rights to use, copy, modify, merge, publish, | ||
| distribute, sublicense, and/or sell copies of the Software, and to | ||
| permit persons to whom the Software is furnished to do so, subject to | ||
| the following conditions: | ||
| The above copyright notice and this permission notice shall be included | ||
| in all copies or substantial portions of the Software. | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||
| OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
| IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
| CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
| TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
| SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| A simple package to deal with ISO 8601 date time formats. | ||
| ISO 8601 defines a neutral, unambiguous date string format, which also | ||
| has the property of sorting naturally. | ||
| e.g. YYYY-MM-DDTHH:MM:SSZ or 2007-01-25T12:00:00Z | ||
| Currently this covers only the most common date formats encountered, not | ||
| all of ISO 8601 is handled. | ||
| Currently the following formats are handled: | ||
| * 2006-01-01T00:00:00Z | ||
| * 2006-01-01T00:00:00[+-]00:00 | ||
| I'll add more as I encounter them in my day to day life. Patches with | ||
| new formats and tests will be gratefully accepted of course :) | ||
| References: | ||
| * http://www.cl.cam.ac.uk/~mgk25/iso-time.html - simple overview | ||
| * http://hydracen.com/dx/iso8601.htm - more detailed enumeration of | ||
| valid formats. | ||
| See the LICENSE file for the license this package is released under. |
Sorry, the diff of this file is not supported yet
| try: | ||
| from setuptools import setup | ||
| except ImportError: | ||
| from distutils import setup | ||
| long_description="""Simple module to parse ISO 8601 dates | ||
| This module parses the most common forms of ISO 8601 date strings (e.g. | ||
| 2007-01-14T20:34:22+00:00) into datetime objects. | ||
| >>> import iso8601 | ||
| >>> iso8601.parse_date("2007-01-25T12:00:00Z") | ||
| datetime.datetime(2007, 1, 25, 12, 0, tzinfo=<iso8601.iso8601.Utc ...>) | ||
| >>> | ||
| Changes | ||
| ======= | ||
| 0.1.4 | ||
| ----- | ||
| * The default_timezone argument wasn't being passed through correctly, | ||
| UTC was being used in every case. Fixes issue 10. | ||
| 0.1.3 | ||
| ----- | ||
| * Fixed the microsecond handling, the generated microsecond values were | ||
| way too small. Fixes issue 9. | ||
| 0.1.2 | ||
| ----- | ||
| * Adding ParseError to __all__ in iso8601 module, allows people to import it. | ||
| Addresses issue 7. | ||
| * Be a little more flexible when dealing with dates without leading zeroes. | ||
| This violates the spec a little, but handles more dates as seen in the | ||
| field. Addresses issue 6. | ||
| * Allow date/time separators other than T. | ||
| 0.1.1 | ||
| ----- | ||
| * When parsing dates without a timezone the specified default is used. If no | ||
| default is specified then UTC is used. Addresses issue 4. | ||
| """ | ||
| setup( | ||
| name="iso8601", | ||
| version="0.1.4", | ||
| description=long_description.split("\n")[0], | ||
| long_description=long_description, | ||
| author="Michael Twomey", | ||
| author_email="micktwomey+iso8601@gmail.com", | ||
| url="http://code.google.com/p/pyiso8601/", | ||
| packages=["iso8601"], | ||
| license="MIT", | ||
| ) |
| import iso8601 | ||
| def test_iso8601_regex(): | ||
| assert iso8601.ISO8601_REGEX.match("2006-10-11T00:14:33Z") | ||
| def test_timezone_regex(): | ||
| assert iso8601.TIMEZONE_REGEX.match("+01:00") | ||
| assert iso8601.TIMEZONE_REGEX.match("+00:00") | ||
| assert iso8601.TIMEZONE_REGEX.match("+01:20") | ||
| assert iso8601.TIMEZONE_REGEX.match("-01:00") | ||
| def test_parse_date(): | ||
| d = iso8601.parse_date("2006-10-20T15:34:56Z") | ||
| assert d.year == 2006 | ||
| assert d.month == 10 | ||
| assert d.day == 20 | ||
| assert d.hour == 15 | ||
| assert d.minute == 34 | ||
| assert d.second == 56 | ||
| assert d.tzinfo == iso8601.UTC | ||
| def test_parse_date_fraction(): | ||
| d = iso8601.parse_date("2006-10-20T15:34:56.123Z") | ||
| assert d.year == 2006 | ||
| assert d.month == 10 | ||
| assert d.day == 20 | ||
| assert d.hour == 15 | ||
| assert d.minute == 34 | ||
| assert d.second == 56 | ||
| assert d.microsecond == 123000 | ||
| assert d.tzinfo == iso8601.UTC | ||
| def test_parse_date_fraction_2(): | ||
| """From bug 6 | ||
| """ | ||
| d = iso8601.parse_date("2007-5-7T11:43:55.328Z'") | ||
| assert d.year == 2007 | ||
| assert d.month == 5 | ||
| assert d.day == 7 | ||
| assert d.hour == 11 | ||
| assert d.minute == 43 | ||
| assert d.second == 55 | ||
| assert d.microsecond == 328000 | ||
| assert d.tzinfo == iso8601.UTC | ||
| def test_parse_date_tz(): | ||
| d = iso8601.parse_date("2006-10-20T15:34:56.123+02:30") | ||
| assert d.year == 2006 | ||
| assert d.month == 10 | ||
| assert d.day == 20 | ||
| assert d.hour == 15 | ||
| assert d.minute == 34 | ||
| assert d.second == 56 | ||
| assert d.microsecond == 123000 | ||
| assert d.tzinfo.tzname(None) == "+02:30" | ||
| offset = d.tzinfo.utcoffset(None) | ||
| assert offset.days == 0 | ||
| assert offset.seconds == 60 * 60 * 2.5 | ||
| def test_parse_invalid_date(): | ||
| try: | ||
| iso8601.parse_date(None) | ||
| except iso8601.ParseError: | ||
| pass | ||
| else: | ||
| assert 1 == 2 | ||
| def test_parse_invalid_date2(): | ||
| try: | ||
| iso8601.parse_date("23") | ||
| except iso8601.ParseError: | ||
| pass | ||
| else: | ||
| assert 1 == 2 | ||
| def test_parse_no_timezone(): | ||
| """issue 4 - Handle datetime string without timezone | ||
| This tests what happens when you parse a date with no timezone. While not | ||
| strictly correct this is quite common. I'll assume UTC for the time zone | ||
| in this case. | ||
| """ | ||
| d = iso8601.parse_date("2007-01-01T08:00:00") | ||
| assert d.year == 2007 | ||
| assert d.month == 1 | ||
| assert d.day == 1 | ||
| assert d.hour == 8 | ||
| assert d.minute == 0 | ||
| assert d.second == 0 | ||
| assert d.microsecond == 0 | ||
| assert d.tzinfo == iso8601.UTC | ||
| def test_parse_no_timezone_different_default(): | ||
| tz = iso8601.FixedOffset(2, 0, "test offset") | ||
| d = iso8601.parse_date("2007-01-01T08:00:00", default_timezone=tz) | ||
| assert d.tzinfo == tz | ||
| def test_space_separator(): | ||
| """Handle a separator other than T | ||
| """ | ||
| d = iso8601.parse_date("2007-06-23 06:40:34.00Z") | ||
| assert d.year == 2007 | ||
| assert d.month == 6 | ||
| assert d.day == 23 | ||
| assert d.hour == 6 | ||
| assert d.minute == 40 | ||
| assert d.second == 34 | ||
| assert d.microsecond == 0 | ||
| assert d.tzinfo == iso8601.UTC |
| # Copyright (c) 2007 Michael Twomey | ||
| # | ||
| # Permission is hereby granted, free of charge, to any person obtaining a | ||
| # copy of this software and associated documentation files (the | ||
| # "Software"), to deal in the Software without restriction, including | ||
| # without limitation the rights to use, copy, modify, merge, publish, | ||
| # distribute, sublicense, and/or sell copies of the Software, and to | ||
| # permit persons to whom the Software is furnished to do so, subject to | ||
| # the following conditions: | ||
| # | ||
| # The above copyright notice and this permission notice shall be included | ||
| # in all copies or substantial portions of the Software. | ||
| # | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||
| # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
| # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
| # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
| # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
| # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| """ISO 8601 date time string parsing | ||
| Basic usage: | ||
| >>> import iso8601 | ||
| >>> iso8601.parse_date("2007-01-25T12:00:00Z") | ||
| datetime.datetime(2007, 1, 25, 12, 0, tzinfo=<iso8601.iso8601.Utc ...>) | ||
| >>> | ||
| """ | ||
| from datetime import datetime, timedelta, tzinfo | ||
| import re | ||
| __all__ = ["parse_date", "ParseError"] | ||
| # Adapted from http://delete.me.uk/2005/03/iso8601.html | ||
| ISO8601_REGEX_PATTERN = (r"(?P<year>[0-9]{4})(-(?P<month>[0-9]{1,2})(-(?P<day>[0-9]{1,2})" | ||
| r"((?P<separator>.)(?P<hour>[0-9]{2}):(?P<minute>[0-9]{2})(:(?P<second>[0-9]{2})(\.(?P<fraction>[0-9]+))?)?" | ||
| r"(?P<timezone>Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?" | ||
| ) | ||
| TIMEZONE_REGEX_PATTERN = "(?P<prefix>[+-])(?P<hours>[0-9]{2}).(?P<minutes>[0-9]{2})" | ||
| ISO8601_REGEX = re.compile(ISO8601_REGEX_PATTERN.encode('utf8')) | ||
| TIMEZONE_REGEX = re.compile(TIMEZONE_REGEX_PATTERN.encode('utf8')) | ||
| zulu = "Z".encode('latin-1') | ||
| minus = "-".encode('latin-1') | ||
| class ParseError(Exception): | ||
| """Raised when there is a problem parsing a date string""" | ||
| # Yoinked from python docs | ||
| ZERO = timedelta(0) | ||
| class Utc(tzinfo): | ||
| """UTC | ||
| """ | ||
| def utcoffset(self, dt): | ||
| return ZERO | ||
| def tzname(self, dt): | ||
| return "UTC" | ||
| def dst(self, dt): | ||
| return ZERO | ||
| UTC = Utc() | ||
| class FixedOffset(tzinfo): | ||
| """Fixed offset in hours and minutes from UTC | ||
| """ | ||
| def __init__(self, offset_hours, offset_minutes, name): | ||
| self.__offset = timedelta(hours=offset_hours, minutes=offset_minutes) | ||
| self.__name = name | ||
| def utcoffset(self, dt): | ||
| return self.__offset | ||
| def tzname(self, dt): | ||
| return self.__name | ||
| def dst(self, dt): | ||
| return ZERO | ||
| def __repr__(self): | ||
| return "<FixedOffset %r>" % self.__name | ||
| def parse_timezone(tzstring, default_timezone=UTC): | ||
| """Parses ISO 8601 time zone specs into tzinfo offsets | ||
| """ | ||
| if tzstring == zulu: | ||
| return default_timezone | ||
| # This isn't strictly correct, but it's common to encounter dates without | ||
| # timezones so I'll assume the default (which defaults to UTC). | ||
| # Addresses issue 4. | ||
| if tzstring is None: | ||
| return default_timezone | ||
| m = TIMEZONE_REGEX.match(tzstring) | ||
| prefix, hours, minutes = m.groups() | ||
| hours, minutes = int(hours), int(minutes) | ||
| if prefix == minus: | ||
| hours = -hours | ||
| minutes = -minutes | ||
| return FixedOffset(hours, minutes, tzstring) | ||
| def parse_date(datestring, default_timezone=UTC): | ||
| """Parses ISO 8601 dates into datetime objects | ||
| The timezone is parsed from the date string. However it is quite common to | ||
| have dates without a timezone (not strictly correct). In this case the | ||
| default timezone specified in default_timezone is used. This is UTC by | ||
| default. | ||
| """ | ||
| if not isinstance(datestring, bytes): | ||
| raise ParseError("Expecting bytes %r" % datestring) | ||
| m = ISO8601_REGEX.match(datestring) | ||
| if not m: | ||
| raise ParseError("Unable to parse date string %r" % datestring) | ||
| groups = m.groupdict() | ||
| tz = parse_timezone(groups["timezone"], default_timezone=default_timezone) | ||
| if groups["fraction"] is None: | ||
| groups["fraction"] = 0 | ||
| else: | ||
| groups["fraction"] = int(float("0.%s" % groups["fraction"].decode()) * 1e6) | ||
| return datetime(int(groups["year"]), int(groups["month"]), int(groups["day"]), | ||
| int(groups["hour"]), int(groups["minute"]), int(groups["second"]), | ||
| int(groups["fraction"]), tz) |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
455068
-2.77%61
-7.58%7834
-3.75%