🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

python-subunit

Package Overview
Dependencies
Maintainers
3
Versions
28
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

python-subunit - pypi Package Compare versions

Comparing version
1.4.2
to
1.4.3
+22
disperse.conf
# 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"
+3
-3

@@ -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 }}

@@ -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 @@ ~~~~~~~~~

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
[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):

@@ -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,))

@@ -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
#!/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)