<commit_before># This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

# Copyright 2009 Menno Smits

'''
Unit tests for the FetchTokeniser and FetchParser classes
'''

from datetime import datetime
from textwrap import dedent
import unittest
from imapclient.response_parser import parse_response, parse_fetch_response, ParseError
from imapclient.fixed_offset import FixedOffset
from pprint import pformat

#TODO: tokenising tests
#TODO: test invalid dates and times

CRLF = '\r\n'

class TestParseResponse(unittest.TestCase):

    def test_unquoted(self):
        self._test('FOO', 'FOO')
        self._test('F.O:-O_0;', 'F.O:-O_0;')
        self._test(r'\Seen', r'\Seen')

    def test_string(self):
        self._test('"TEST"', 'TEST')

    def test_int(self):
        self._test('45', 45)

    def test_nil(self):
        self._test('NIL', None)

    def test_empty_tuple(self):
        self._test('()', ())

    def test_tuple(self):
        self._test('(123 "foo" GeE)', (123, 'foo', 'GeE'))

    def test_int_and_tuple(self):
        self._test('1 (123 "foo")', (1, (123, 'foo')), wrap=False)

    def test_nested_tuple(self):
        self._test('(123 "foo" ("more" NIL) 66)',
                   (123, "foo", ("more", None), 66))

    def test_deeper_nest_tuple(self):
        self._test('(123 "foo" ((0 1 2) "more" NIL) 66)',
                   (123, "foo", ((0, 1, 2), "more", None), 66))

    def test_complex_mixed(self):
        self._test('((FOO "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 1152 23)'
                   '("TEXT" "PLAIN" ("CHARSET" "US-ASCII" "NAME" "cc.diff") '
                   '"<hi.there>" "foo" "BASE64" 4554 73) "MIXED")',
                   (('FOO', 'PLAIN', ('CHARSET', 'US-ASCII'), None, None, '7BIT', 1152, 23),
                    ('TEXT', 'PLAIN', ('CHARSET', 'US-ASCII', 'NAME', 'cc.diff'),
                    '<hi.there>', 'foo', 'BASE64', 4554, 73), 'MIXED'))

    def test_envelopey(self):
        self._test('(UID 5 ENVELOPE ("internal_date" "subject" '
                   '(("name" NIL "address1" "domain1.com")) '
                   '((NIL NIL "address2" "domain2.com")) '
                   '(("name" NIL "address3" "domain3.com")) '
                   '((NIL NIL "address4" "domain4.com")) '
                   'NIL NIL "<reply-to-id>" "<msg_id>"))',
                   ('UID',
                    5,
                    'ENVELOPE',
                    ('internal_date',
                     'subject',
                     (('name', None, 'address1', 'domain1.com'),),
                     ((None, None, 'address2', 'domain2.com'),),
                     (('name', None, 'address3', 'domain3.com'),),
                     ((None, None, 'address4', 'domain4.com'),),
                     None,
                     None,
                     '<reply-to-id>',
                     '<msg_id>')))

    def test_envelopey_quoted(self):
        self._test('(UID 5 ENVELOPE ("internal_date" "subject with \\"quotes\\"" '
                   '(("name" NIL "address1" "domain1.com")) '
                   '((NIL NIL "address2" "domain2.com")) '
                   '(("name" NIL "address3" "domain3.com")) '
                   '((NIL NIL "address4" "domain4.com")) '
                   'NIL NIL "<reply-to-id>" "<msg_id>"))',
                   ('UID',
                    5,
                    'ENVELOPE',
                    ('internal_date',
                     'subject with "quotes"',
                     (('name', None, 'address1', 'domain1.com'),),
                     ((None, None, 'address2', 'domain2.com'),),
                     (('name', None, 'address3', 'domain3.com'),),
                     ((None, None, 'address4', 'domain4.com'),),
                     None,
                     None,
                     '<reply-to-id>',
                     '<msg_id>')))

    def test_literal(self):
        literal_text = add_crlf(dedent("""\
            012
            abc def XYZ
            """))
        self._test([('{18}', literal_text)], literal_text)

    def test_literal_with_more(self):
        literal_text = add_crlf(dedent("""\
            012
            abc def XYZ
            """))
        response = [('(12 "foo" {18}', literal_text), ")"]
        self._test(response, (12, 'foo', literal_text))

    def test_quoted_specials(self):
        self._test(r'"\"foo bar\""', '"foo bar"')
        self._test(r'"foo \"bar\""', 'foo "bar"')
        self._test(r'"foo\\bar"', r'foo\bar')

    def test_incomplete_tuple(self):
        self._test_parse_error('abc (1 2', 'Tuple incomplete before "(1 2"')

    def test_bad_literal(self):
        self._test_parse_error([('{99}', 'abc')],
                               'Expecting literal of size 99, got 3')

    def test_bad_quoting(self):
        self._test_parse_error('"abc next', 'No closing quotation:')

    def _test(self, to_parse, expected, wrap=True):
        if wrap:
            # convenience - expected value should be wrapped in another tuple
            expected = (expected,)
        if not isinstance(to_parse, list):
            to_parse = [to_parse]
        output = parse_response(to_parse)
        self.assert_(output == expected,
                     format_error(to_parse, output, expected))

    def _test_parse_error(self, to_parse, expected_msg):
        try:
            parse_response(to_parse)
            self.fail("didn't raise an exception")
        except ParseError, err:
            self.assert_(expected_msg == str(err)[:len(expected_msg)],
                         'got ParseError with wrong msg: %r' % str(err))

class TestParseFetchResponse(unittest.TestCase):

    def test_basic(self):
        self.assertEquals(parse_fetch_response('4 ()'), {4: {'SEQ': 4}})

    def test_bad_msgid(self):
        self.assertRaises(ParseError, parse_fetch_response, ['abc ()'])

    def test_bad_data(self):
        self.assertRaises(ParseError, parse_fetch_response, ['2 WHAT'])

    def test_missing_data(self):
        self.assertRaises(ParseError, parse_fetch_response, ['2'])

    def test_simple_pairs(self):
        self.assertEquals(parse_fetch_response(['23 (ABC 123 StUfF "hello")']),
                          {23: {'ABC': 123,
                                'STUFF': 'hello',
                                'SEQ': 23}})

    def test_odd_pairs(self):
        self.assertRaises(ParseError, parse_fetch_response, ['(ONE)'])
        self.assertRaises(ParseError, parse_fetch_response, ['(ONE TWO THREE)'])

    def test_UID(self):
        self.assertEquals(parse_fetch_response(['23 (UID 76)']),
                          {76: {'SEQ': 23}})
        self.assertEquals(parse_fetch_response(['23 (uiD 76)']),
                          {76: {'SEQ': 23}})

    def test_bad_UID(self):
        self.assertRaises(ParseError, parse_fetch_response, '(UID X)')

    def test_FLAGS(self):
        self.assertEquals(parse_fetch_response(['23 (FLAGS (\Seen Stuff))']),
                          {23: {'SEQ': 23, 'FLAGS': (r'\Seen', 'Stuff')}})

    def test_multiple_messages(self):
        self.assertEquals(parse_fetch_response(
                                    ["2 (FLAGS (Foo Bar)) ",
                                     "7 (FLAGS (Baz Sneeve))"]),
                         {
                            2: {'FLAGS': ('Foo', 'Bar'), 'SEQ': 2},
                            7: {'FLAGS': ('Baz', 'Sneeve'), 'SEQ': 7},
                         })

    def test_literals(self):
        self.assertEquals(parse_fetch_response([('1 (RFC822.TEXT {4}', 'body'),
                                                (' RFC822 {21}', 'Subject: test\r\n\r\nbody'),
                                                ')']),
                          {1: {'RFC822.TEXT': 'body',
                               'RFC822': 'Subject: test\r\n\r\nbody',
                               'SEQ': 1}})

    def test_literals_and_keys_with_square_brackets(self):
        self.assertEquals(parse_fetch_response([('1 (BODY[TEXT] {11}', 'Hi there.\r\n'), ')']),
                          { 1: {'BODY[TEXT]': 'Hi there.\r\n',
                                'SEQ': 1}})

    def test_INTERNALDATE(self):
        def check(date_str, expected_dt):
            output = parse_fetch_response(['3 (INTERNALDATE "%s")' % date_str])
            self.assertEquals(output.keys(), [3])
            self.assertEquals(set(output[3].keys()), set(['INTERNALDATE', 'SEQ']))
            actual_dt = output[3]['INTERNALDATE']
            self.assert_(actual_dt.tzinfo is None)   # Returned date should be in local timezone
            expected_dt = datetime_to_native(expected_dt)
            self.assert_(actual_dt == expected_dt, '%s != %s' % (actual_dt, expected_dt))

        dt = check(' 9-Feb-2007 17:08:08 -0430',
                   datetime(2007, 2, 9, 17, 8, 8, 0, FixedOffset(-4*60 - 30)))

        dt = check('12-Feb-2007 17:08:08 +0200',
                   datetime(2007, 2, 12, 17, 8, 8, 0, FixedOffset(2*60)))

        dt = check(' 9-Dec-2007 17:08:08 +0000',
                   datetime(2007, 12, 9, 17, 8, 8, 0, FixedOffset(0)))

    def test_mixed_types(self):
        self.assertEquals(parse_fetch_response([('1 (INTERNALDATE " 9-Feb-2007 17:08:08 +0100" RFC822 {21}',
                                                 'Subject: test\r\n\r\nbody'),
                                                ')']),
                          {1: {'INTERNALDATE': datetime_to_native(datetime(2007, 2, 9,
                                                                           17, 8, 8, 0,
                                                                           FixedOffset(60))),
                               'RFC822': 'Subject: test\r\n\r\nbody',
                               'SEQ': 1}})

def format_error(input_, output, expected):
    return 'failed for:\n%s\ngot:\n%s\nexpected:\n%s' % (
                pformat(input_),
                pformat(output),
                pformat(expected))

def add_crlf(text):
    return CRLF.join(text.splitlines()) + CRLF

system_offset = FixedOffset.for_system()
def datetime_to_native(dt):
    return dt.astimezone(system_offset).replace(tzinfo=None)

if __name__ == '__main__':
    unittest.main()
<commit_msg>Added a test for repeated UIDs (#44)<commit_after># This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

# Copyright 2009 Menno Smits

'''
Unit tests for the FetchTokeniser and FetchParser classes
'''

from datetime import datetime
from textwrap import dedent
import unittest
from imapclient.response_parser import parse_response, parse_fetch_response, ParseError
from imapclient.fixed_offset import FixedOffset
from pprint import pformat

#TODO: tokenising tests
#TODO: test invalid dates and times

CRLF = '\r\n'

class TestParseResponse(unittest.TestCase):

    def test_unquoted(self):
        self._test('FOO', 'FOO')
        self._test('F.O:-O_0;', 'F.O:-O_0;')
        self._test(r'\Seen', r'\Seen')

    def test_string(self):
        self._test('"TEST"', 'TEST')

    def test_int(self):
        self._test('45', 45)

    def test_nil(self):
        self._test('NIL', None)

    def test_empty_tuple(self):
        self._test('()', ())

    def test_tuple(self):
        self._test('(123 "foo" GeE)', (123, 'foo', 'GeE'))

    def test_int_and_tuple(self):
        self._test('1 (123 "foo")', (1, (123, 'foo')), wrap=False)

    def test_nested_tuple(self):
        self._test('(123 "foo" ("more" NIL) 66)',
                   (123, "foo", ("more", None), 66))

    def test_deeper_nest_tuple(self):
        self._test('(123 "foo" ((0 1 2) "more" NIL) 66)',
                   (123, "foo", ((0, 1, 2), "more", None), 66))

    def test_complex_mixed(self):
        self._test('((FOO "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 1152 23)'
                   '("TEXT" "PLAIN" ("CHARSET" "US-ASCII" "NAME" "cc.diff") '
                   '"<hi.there>" "foo" "BASE64" 4554 73) "MIXED")',
                   (('FOO', 'PLAIN', ('CHARSET', 'US-ASCII'), None, None, '7BIT', 1152, 23),
                    ('TEXT', 'PLAIN', ('CHARSET', 'US-ASCII', 'NAME', 'cc.diff'),
                    '<hi.there>', 'foo', 'BASE64', 4554, 73), 'MIXED'))

    def test_envelopey(self):
        self._test('(UID 5 ENVELOPE ("internal_date" "subject" '
                   '(("name" NIL "address1" "domain1.com")) '
                   '((NIL NIL "address2" "domain2.com")) '
                   '(("name" NIL "address3" "domain3.com")) '
                   '((NIL NIL "address4" "domain4.com")) '
                   'NIL NIL "<reply-to-id>" "<msg_id>"))',
                   ('UID',
                    5,
                    'ENVELOPE',
                    ('internal_date',
                     'subject',
                     (('name', None, 'address1', 'domain1.com'),),
                     ((None, None, 'address2', 'domain2.com'),),
                     (('name', None, 'address3', 'domain3.com'),),
                     ((None, None, 'address4', 'domain4.com'),),
                     None,
                     None,
                     '<reply-to-id>',
                     '<msg_id>')))

    def test_envelopey_quoted(self):
        self._test('(UID 5 ENVELOPE ("internal_date" "subject with \\"quotes\\"" '
                   '(("name" NIL "address1" "domain1.com")) '
                   '((NIL NIL "address2" "domain2.com")) '
                   '(("name" NIL "address3" "domain3.com")) '
                   '((NIL NIL "address4" "domain4.com")) '
                   'NIL NIL "<reply-to-id>" "<msg_id>"))',
                   ('UID',
                    5,
                    'ENVELOPE',
                    ('internal_date',
                     'subject with "quotes"',
                     (('name', None, 'address1', 'domain1.com'),),
                     ((None, None, 'address2', 'domain2.com'),),
                     (('name', None, 'address3', 'domain3.com'),),
                     ((None, None, 'address4', 'domain4.com'),),
                     None,
                     None,
                     '<reply-to-id>',
                     '<msg_id>')))

    def test_literal(self):
        literal_text = add_crlf(dedent("""\
            012
            abc def XYZ
            """))
        self._test([('{18}', literal_text)], literal_text)

    def test_literal_with_more(self):
        literal_text = add_crlf(dedent("""\
            012
            abc def XYZ
            """))
        response = [('(12 "foo" {18}', literal_text), ")"]
        self._test(response, (12, 'foo', literal_text))

    def test_quoted_specials(self):
        self._test(r'"\"foo bar\""', '"foo bar"')
        self._test(r'"foo \"bar\""', 'foo "bar"')
        self._test(r'"foo\\bar"', r'foo\bar')

    def test_incomplete_tuple(self):
        self._test_parse_error('abc (1 2', 'Tuple incomplete before "(1 2"')

    def test_bad_literal(self):
        self._test_parse_error([('{99}', 'abc')],
                               'Expecting literal of size 99, got 3')

    def test_bad_quoting(self):
        self._test_parse_error('"abc next', 'No closing quotation:')

    def _test(self, to_parse, expected, wrap=True):
        if wrap:
            # convenience - expected value should be wrapped in another tuple
            expected = (expected,)
        if not isinstance(to_parse, list):
            to_parse = [to_parse]
        output = parse_response(to_parse)
        self.assert_(output == expected,
                     format_error(to_parse, output, expected))

    def _test_parse_error(self, to_parse, expected_msg):
        try:
            parse_response(to_parse)
            self.fail("didn't raise an exception")
        except ParseError, err:
            self.assert_(expected_msg == str(err)[:len(expected_msg)],
                         'got ParseError with wrong msg: %r' % str(err))

class TestParseFetchResponse(unittest.TestCase):

    def test_basic(self):
        self.assertEquals(parse_fetch_response('4 ()'), {4: {'SEQ': 4}})

    def test_bad_msgid(self):
        self.assertRaises(ParseError, parse_fetch_response, ['abc ()'])

    def test_bad_data(self):
        self.assertRaises(ParseError, parse_fetch_response, ['2 WHAT'])

    def test_missing_data(self):
        self.assertRaises(ParseError, parse_fetch_response, ['2'])

    def test_simple_pairs(self):
        self.assertEquals(parse_fetch_response(['23 (ABC 123 StUfF "hello")']),
                          {23: {'ABC': 123,
                                'STUFF': 'hello',
                                'SEQ': 23}})

    def test_odd_pairs(self):
        self.assertRaises(ParseError, parse_fetch_response, ['(ONE)'])
        self.assertRaises(ParseError, parse_fetch_response, ['(ONE TWO THREE)'])

    def test_UID(self):
        self.assertEquals(parse_fetch_response(['23 (UID 76)']),
                          {76: {'SEQ': 23}})
        self.assertEquals(parse_fetch_response(['23 (uiD 76)']),
                          {76: {'SEQ': 23}})

    def test_repeated_UID(self):
        self.assertEquals(parse_fetch_response(['23 (UID 76 FOO 123 UID 76 GOO 321)']),
                          {76: {'FOO': 123,
                                'GOO': 321,
                                'SEQ': 23}})
        self.assertEquals(parse_fetch_response(['23 (UID 76 FOO 123', 'UID 76 GOO 321)']),
                          {76: {'FOO': 123,
                                'GOO': 321,
                                'SEQ': 23}})

    def test_bad_UID(self):
        self.assertRaises(ParseError, parse_fetch_response, '(UID X)')

    def test_FLAGS(self):
        self.assertEquals(parse_fetch_response(['23 (FLAGS (\Seen Stuff))']),
                          {23: {'SEQ': 23, 'FLAGS': (r'\Seen', 'Stuff')}})

    def test_multiple_messages(self):
        self.assertEquals(parse_fetch_response(
                                    ["2 (FLAGS (Foo Bar)) ",
                                     "7 (FLAGS (Baz Sneeve))"]),
                         {
                            2: {'FLAGS': ('Foo', 'Bar'), 'SEQ': 2},
                            7: {'FLAGS': ('Baz', 'Sneeve'), 'SEQ': 7},
                         })

    def test_literals(self):
        self.assertEquals(parse_fetch_response([('1 (RFC822.TEXT {4}', 'body'),
                                                (' RFC822 {21}', 'Subject: test\r\n\r\nbody'),
                                                ')']),
                          {1: {'RFC822.TEXT': 'body',
                               'RFC822': 'Subject: test\r\n\r\nbody',
                               'SEQ': 1}})

    def test_literals_and_keys_with_square_brackets(self):
        self.assertEquals(parse_fetch_response([('1 (BODY[TEXT] {11}', 'Hi there.\r\n'), ')']),
                          { 1: {'BODY[TEXT]': 'Hi there.\r\n',
                                'SEQ': 1}})

    def test_INTERNALDATE(self):
        def check(date_str, expected_dt):
            output = parse_fetch_response(['3 (INTERNALDATE "%s")' % date_str])
            self.assertEquals(output.keys(), [3])
            self.assertEquals(set(output[3].keys()), set(['INTERNALDATE', 'SEQ']))
            actual_dt = output[3]['INTERNALDATE']
            self.assert_(actual_dt.tzinfo is None)   # Returned date should be in local timezone
            expected_dt = datetime_to_native(expected_dt)
            self.assert_(actual_dt == expected_dt, '%s != %s' % (actual_dt, expected_dt))

        dt = check(' 9-Feb-2007 17:08:08 -0430',
                   datetime(2007, 2, 9, 17, 8, 8, 0, FixedOffset(-4*60 - 30)))

        dt = check('12-Feb-2007 17:08:08 +0200',
                   datetime(2007, 2, 12, 17, 8, 8, 0, FixedOffset(2*60)))

        dt = check(' 9-Dec-2007 17:08:08 +0000',
                   datetime(2007, 12, 9, 17, 8, 8, 0, FixedOffset(0)))

    def test_mixed_types(self):
        self.assertEquals(parse_fetch_response([('1 (INTERNALDATE " 9-Feb-2007 17:08:08 +0100" RFC822 {21}',
                                                 'Subject: test\r\n\r\nbody'),
                                                ')']),
                          {1: {'INTERNALDATE': datetime_to_native(datetime(2007, 2, 9,
                                                                           17, 8, 8, 0,
                                                                           FixedOffset(60))),
                               'RFC822': 'Subject: test\r\n\r\nbody',
                               'SEQ': 1}})

def format_error(input_, output, expected):
    return 'failed for:\n%s\ngot:\n%s\nexpected:\n%s' % (
                pformat(input_),
                pformat(output),
                pformat(expected))

def add_crlf(text):
    return CRLF.join(text.splitlines()) + CRLF

system_offset = FixedOffset.for_system()
def datetime_to_native(dt):
    return dt.astimezone(system_offset).replace(tzinfo=None)

if __name__ == '__main__':
    unittest.main()
<|endoftext|>


<commit_before>"""Facility to load plugins."""

import sys
import os

from importlib import import_module
from imp import find_module

class Loader(object):

    def __init__(self, *args, **kwargs):
        self._cache = []

    def load(self, *args, **kwargs):
        self._fill_cache(*args, **kwargs)
        self._post_fill()
        self._order()
        return self._cache

    def _meta(self, plugin):
        meta = getattr(plugin, '__plugin__', None)
        return meta

    def _post_fill(self):
        for plugin in self._cache:
            meta = self._meta(plugin)
            if not getattr(meta, 'load', True):
                self._cache.remove(plugin)
            for implied_namespace in getattr(meta, 'imply_plugins', []):
                plugins = self._cache
                self._cache = self.load(implied_namespace)
                self._post_fill()
                self._cache = plugins + self._cache

    def _order(self):
        self._cache.sort(key=self._plugin_priority, reverse=True)

    def _plugin_priority(self, plugin):
        meta = self._meta(plugin)
        return getattr(meta, 'priority', 0.0)

class ModuleLoader(Loader):
    """Performs the work of locating and loading straight plugins.

    This looks for plugins in every location in the import path.
    """

    def __init__(self, recurse=False):
        super(ModuleLoader, self).__init__()
        self.recurse = recurse

    def _isPackage(self, path):
        pkg_init = os.path.join(path, '__init__.py')
        if os.path.exists(pkg_init):
            return True

        return False

    def _findPluginFilePaths(self, namespace):
        already_seen = set()

        # Look in each location in the path
        for path in sys.path:

            # Within this, we want to look for a package for the namespace
            namespace_rel_path = namespace.replace(".", os.path.sep)
            namespace_path = os.path.join(path, namespace_rel_path)
            if os.path.exists(namespace_path):
                for possible in os.listdir(namespace_path):

                    poss_path = os.path.join(namespace_path, possible)
                    if os.path.isdir(poss_path):
                        if not self._isPackage(poss_path):
                            continue
                        if self.recurse:
                            subns = '.'.join((namespace, possible))
                            for path in self._findPluginFilePaths(subns):
                                yield path
                        base = possible
                    else:
                        base, ext = os.path.splitext(possible)
                        if base == '__init__' or ext != '.py':
                            continue

                    if base not in already_seen:
                        already_seen.add(base)
                        yield os.path.join(namespace, possible)

    def _findPluginModules(self, namespace):
        for filepath in self._findPluginFilePaths(namespace):
            path_segments = list(filepath.split(os.path.sep))
            path_segments = [p for p in path_segments if p]
            path_segments[-1] = os.path.splitext(path_segments[-1])[0]
            import_path = '.'.join(path_segments)

            try:
                module = import_module(import_path)
            except ImportError:
                #raise Exception(import_path)

                module = None

            if module is not None:
                yield module

    def _fill_cache(self, namespace):
        """Load all modules found in a namespace"""

        modules = self._findPluginModules(namespace)

        self._cache = list(modules)

class ObjectLoader(Loader):
    """Loads classes or objects out of modules in a namespace, based on a
    provided criteria.

    The load() method returns all objects exported by the module.
    """

    def __init__(self):
        self.module_loader = ModuleLoader()

    def _fill_cache(self, namespace):
        modules = self.module_loader.load(namespace)
        objects = []

        for module in modules:
            for attr_name in dir(module):
                if not attr_name.startswith('_'):
                    objects.append(getattr(module, attr_name))

        self._cache = objects
        return objects

class ClassLoader(ObjectLoader):

    def _fill_cache(self, namespace, subclasses=None):
        objects = super(ClassLoader, self)._fill_cache(namespace)
        classes = []
        for cls in objects:
            if isinstance(cls, type):
                if subclasses is None:
                    classes.append(cls)
                elif issubclass(cls, subclasses) and cls is not subclasses:
                    classes.append(cls)

        self._cache = classes
        return classes

def unified_load(namespace, subclasses=None, recurse=False):

    if subclasses is not None:
        return ClassLoader(recurse=recurse).load(namespace, subclasses=subclasses)
    else:
        return ModuleLoader(recurse=recurse).load(namespace)

<commit_msg>Allow object and class loaders to recurse<commit_after>"""Facility to load plugins."""

import sys
import os

from importlib import import_module
from imp import find_module

class Loader(object):

    def __init__(self, *args, **kwargs):
        self._cache = []

    def load(self, *args, **kwargs):
        self._fill_cache(*args, **kwargs)
        self._post_fill()
        self._order()
        return self._cache

    def _meta(self, plugin):
        meta = getattr(plugin, '__plugin__', None)
        return meta

    def _post_fill(self):
        for plugin in self._cache:
            meta = self._meta(plugin)
            if not getattr(meta, 'load', True):
                self._cache.remove(plugin)
            for implied_namespace in getattr(meta, 'imply_plugins', []):
                plugins = self._cache
                self._cache = self.load(implied_namespace)
                self._post_fill()
                self._cache = plugins + self._cache

    def _order(self):
        self._cache.sort(key=self._plugin_priority, reverse=True)

    def _plugin_priority(self, plugin):
        meta = self._meta(plugin)
        return getattr(meta, 'priority', 0.0)

class ModuleLoader(Loader):
    """Performs the work of locating and loading straight plugins.

    This looks for plugins in every location in the import path.
    """

    def __init__(self, recurse=False):
        super(ModuleLoader, self).__init__()
        self.recurse = recurse

    def _isPackage(self, path):
        pkg_init = os.path.join(path, '__init__.py')
        if os.path.exists(pkg_init):
            return True

        return False

    def _findPluginFilePaths(self, namespace):
        already_seen = set()

        # Look in each location in the path
        for path in sys.path:

            # Within this, we want to look for a package for the namespace
            namespace_rel_path = namespace.replace(".", os.path.sep)
            namespace_path = os.path.join(path, namespace_rel_path)
            if os.path.exists(namespace_path):
                for possible in os.listdir(namespace_path):

                    poss_path = os.path.join(namespace_path, possible)
                    if os.path.isdir(poss_path):
                        if not self._isPackage(poss_path):
                            continue
                        if self.recurse:
                            subns = '.'.join((namespace, possible))
                            for path in self._findPluginFilePaths(subns):
                                yield path
                        base = possible
                    else:
                        base, ext = os.path.splitext(possible)
                        if base == '__init__' or ext != '.py':
                            continue

                    if base not in already_seen:
                        already_seen.add(base)
                        yield os.path.join(namespace, possible)

    def _findPluginModules(self, namespace):
        for filepath in self._findPluginFilePaths(namespace):
            path_segments = list(filepath.split(os.path.sep))
            path_segments = [p for p in path_segments if p]
            path_segments[-1] = os.path.splitext(path_segments[-1])[0]
            import_path = '.'.join(path_segments)

            try:
                module = import_module(import_path)
            except ImportError:
                #raise Exception(import_path)

                module = None

            if module is not None:
                yield module

    def _fill_cache(self, namespace):
        """Load all modules found in a namespace"""

        modules = self._findPluginModules(namespace)

        self._cache = list(modules)

class ObjectLoader(Loader):
    """Loads classes or objects out of modules in a namespace, based on a
    provided criteria.

    The load() method returns all objects exported by the module.
    """

    def __init__(self, recurse=False):
        self.module_loader = ModuleLoader(recurse=recurse)

    def _fill_cache(self, namespace):
        modules = self.module_loader.load(namespace)
        objects = []

        for module in modules:
            for attr_name in dir(module):
                if not attr_name.startswith('_'):
                    objects.append(getattr(module, attr_name))

        self._cache = objects
        return objects

class ClassLoader(ObjectLoader):

    def _fill_cache(self, namespace, subclasses=None):
        objects = super(ClassLoader, self)._fill_cache(namespace)
        classes = []
        for cls in objects:
            if isinstance(cls, type):
                if subclasses is None:
                    classes.append(cls)
                elif issubclass(cls, subclasses) and cls is not subclasses:
                    classes.append(cls)

        self._cache = classes
        return classes

def unified_load(namespace, subclasses=None, recurse=False):

    if subclasses is not None:
        return ClassLoader(recurse=recurse).load(namespace, subclasses=subclasses)
    else:
        return ModuleLoader(recurse=recurse).load(namespace)

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# pysat documentation build configuration file, created by
# sphinx-quickstart on Mon Apr 27 10:34:46 2015.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import json
import os
import shlex
import sys

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# sys.path.insert(0, os.path.abspath('.'))

sys.path.insert(0, os.path.abspath('..'))
import pysat

# added by RS, trying to get __init__ method documented
autoclass_content = 'both'
# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.doctest',
    'sphinx.ext.todo',
    'sphinx.ext.imgmath',
    'sphinx.ext.autosummary',
    'sphinx.ext.napoleon',
    'sphinx.ext.intersphinx',
    'numpydoc',
    'IPython.sphinxext.ipython_console_highlighting',
    'm2r2'
]

# added by RS
numpydoc_show_class_members = False

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
source_suffix = '.rst'

# The encoding of source files.
# source_encoding = 'utf-8-sig'

# The main toctree document (using required variable name).
master_doc = 'index'

# General information about the project.
zenodo = json.loads(open('../.zenodo.json').read())
project = 'pysat'
author = ', '.join([x['name'] for x in zenodo['creators']])
manual_copyright = ', '.join(['2021', author])
title = '{:s} Documentation'.format(project)
description = ''

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = pysat.__version__[::-1].partition('.')[2][::-1]
# The full version, including alpha/beta/rc tags.
release = pysat.__version__

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['.build', '.dist']

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'sphinx_rtd_theme'
html_theme_path = ["_themes", ]

# Output file base name for HTML help builder.
htmlhelp_basename = '{:s}doc'.format(project)

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
    # The paper size ('letterpaper' or 'a4paper').
    # 'papersize': 'letterpaper',

    # The font size ('10pt', '11pt' or '12pt').
    # 'pointsize': '10pt',

    # Additional stuff for the LaTeX preamble.
    # 'preamble': '',

    # Latex figure (float) alignment
    # 'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
    (master_doc, '{:s}.tex'.format(project), title, author, 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
latex_logo = '../logo.png'

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
# latex_use_parts = False

# If true, show page references after internal links.
# latex_show_pagerefs = False

# If true, show URL addresses after external links.
# latex_show_urls = False

# Documents to append as an appendix to all manuals.
# latex_appendices = []

# If false, no module index is generated.
# latex_domain_indices = True

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, project, title,
     [author], 1)
]

# If true, show URL addresses after external links.
# man_show_urls = False

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (master_doc, project, title, author, project, description,
     'Space Physics'),
]

# -- Options for Intersphinx connection -----------------------------------

intersphinx_mapping = {'portalocker':
                       ('https://portalocker.readthedocs.io/en/latest',
                        None)}
<commit_msg>BUG: fix copyright<commit_after># -*- coding: utf-8 -*-
#
# pysat documentation build configuration file, created by
# sphinx-quickstart on Mon Apr 27 10:34:46 2015.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import json
import os
import shlex
import sys

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# sys.path.insert(0, os.path.abspath('.'))

sys.path.insert(0, os.path.abspath('..'))
import pysat

# added by RS, trying to get __init__ method documented
autoclass_content = 'both'
# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.doctest',
    'sphinx.ext.todo',
    'sphinx.ext.imgmath',
    'sphinx.ext.autosummary',
    'sphinx.ext.napoleon',
    'sphinx.ext.intersphinx',
    'numpydoc',
    'IPython.sphinxext.ipython_console_highlighting',
    'm2r2'
]

# added by RS
numpydoc_show_class_members = False

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
source_suffix = '.rst'

# The encoding of source files.
# source_encoding = 'utf-8-sig'

# The main toctree document (using required variable name).
master_doc = 'index'

# General information about the project.
zenodo = json.loads(open('../.zenodo.json').read())
project = 'pysat'
author = ', '.join([x['name'] for x in zenodo['creators']])
copyright = ', '.join(['2021', author])
title = '{:s} Documentation'.format(project)
description = 'Supports science analysis across disparate data platforms'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = pysat.__version__[::-1].partition('.')[2][::-1]
# The full version, including alpha/beta/rc tags.
release = pysat.__version__

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['.build', '.dist']

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'sphinx_rtd_theme'
html_theme_path = ["_themes", ]

# Output file base name for HTML help builder.
htmlhelp_basename = '{:s}doc'.format(project)

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
    # The paper size ('letterpaper' or 'a4paper').
    # 'papersize': 'letterpaper',

    # The font size ('10pt', '11pt' or '12pt').
    # 'pointsize': '10pt',

    # Additional stuff for the LaTeX preamble.
    # 'preamble': '',

    # Latex figure (float) alignment
    # 'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
    (master_doc, '{:s}.tex'.format(project), title, author, 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
latex_logo = '../logo.png'

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
# latex_use_parts = False

# If true, show page references after internal links.
# latex_show_pagerefs = False

# If true, show URL addresses after external links.
# latex_show_urls = False

# Documents to append as an appendix to all manuals.
# latex_appendices = []

# If false, no module index is generated.
# latex_domain_indices = True

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, project, title,
     [author], 1)
]

# If true, show URL addresses after external links.
# man_show_urls = False

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (master_doc, project, title, author, project, description,
     'Space Physics'),
]

# -- Options for Intersphinx connection -----------------------------------

intersphinx_mapping = {'portalocker':
                       ('https://portalocker.readthedocs.io/en/latest',
                        None)}
<|endoftext|>


<commit_before># This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

# Copyright 2009 Menno Smits

'''
Unit tests for the FetchTokeniser and FetchParser classes
'''

from textwrap import dedent
import unittest
from imapclient.response_parser import parse_response, parse_fetch_response, ParseError
from imapclient.fixed_offset import FixedOffset
from pprint import pformat

#TODO: tokenising tests
#TODO: test invalid dates and times
#TODO: more response types
#TODO: clean up this file

CRLF = '\r\n'

class TestParseResponse(unittest.TestCase):

    def test_unquoted(self):
        self._test('FOO', 'FOO')
        self._test('F.O:-O_0;', 'F.O:-O_0;')
        self._test(r'\Seen', r'\Seen')

    def test_string(self):
        self._test('"TEST"', 'TEST')

    def test_int(self):
        self._test('45', 45)

    def test_nil(self):
        self._test('NIL', None)

    def test_empty_tuple(self):
        self._test('()', ())

    def test_tuple(self):
        self._test('(123 "foo" GeE)', (123, 'foo', 'GeE'))

    def test_int_and_tuple(self):
        self._test('1 (123 "foo")', (1, (123, 'foo')), wrap=False)

    def test_nested_tuple(self):
        self._test('(123 "foo" ("more" NIL) 66)',
                   (123, "foo", ("more", None), 66))

    def test_deeper_nest_tuple(self):
        self._test('(123 "foo" ((0 1 2) "more" NIL) 66)',
                   (123, "foo", ((0, 1, 2), "more", None), 66))

    def test_complex_mixed(self):
        self._test('((FOO "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 1152 23)'
                   '("TEXT" "PLAIN" ("CHARSET" "US-ASCII" "NAME" "cc.diff") '
                   '"<hi.there>" "foo" "BASE64" 4554 73) "MIXED")',
                   (('FOO', 'PLAIN', ('CHARSET', 'US-ASCII'), None, None, '7BIT', 1152, 23),
                    ('TEXT', 'PLAIN', ('CHARSET', 'US-ASCII', 'NAME', 'cc.diff'),
                    '<hi.there>', 'foo', 'BASE64', 4554, 73), 'MIXED'))

    def test_literal(self):
        literal_text = add_crlf(dedent("""\
            012
            abc def XYZ
            """))
        self._test('{18}' + CRLF + literal_text, literal_text)

    def test_literal_with_more(self):
        literal_text = add_crlf(dedent("""\
            012
            abc def XYZ
            """))
        response = add_crlf(dedent("""\
            (12 "foo" {18}
            %s)
            """) % literal_text)
        self._test(response, (12, 'foo', literal_text))

    def test_incomplete_tuple(self):
        self._test_parse_error('abc (1 2', 'Tuple incomplete before "(1 2"')

    def test_bad_literal(self):
        self._test_parse_error('{99} abc', 'No CRLF after {99}')

    def test_bad_quoting(self):
        self._test_parse_error('"abc next', 'No closing quotation: "abc next')

    def _test(self, to_parse, expected, wrap=True):
        if wrap:
            # convenience - expected value should be wrapped in another tuple
            expected = (expected,)
        output = parse_response(to_parse)
        self.assert_(
                output == expected,
                format_error(to_parse, output, expected),
            )

    def _test_parse_error(self, to_parse, expected_msg):
        try:
            parse_response(to_parse)
            self.fail("didn't raise an exception")
        except ParseError, err:
            self.assert_(expected_msg == str(err),
                         'got ParseError with wrong msg: %r' % str(err))

system_offset = FixedOffset.for_system()
def datetime_to_native(dt):
    return dt.astimezone(system_offset).replace(tzinfo=None)

class TestParseFetchResponse(unittest.TestCase):

    def test_basic(self):
        self.assertEquals(parse_fetch_response('* 4 FETCH ()'), {4: {}})
        self.assertEquals(parse_fetch_response('* 4 fEtCh ()'), {4: {}})

    def test_non_fetch(self):
        self.assertRaises(ParseError, parse_fetch_response, '* 4 OTHER ()')

    def test_bad_msgid(self):
        self.assertRaises(ParseError, parse_fetch_response, '* abc FETCH ()')

    def test_bad_data(self):
        self.assertRaises(ParseError, parse_fetch_response, '* 2 FETCH WHAT')

    def test_missing_data(self):
        self.assertRaises(ParseError, parse_fetch_response, '* 2 FETCH')

    def test_simple_pairs(self):
        self.assertEquals(parse_fetch_response('* 23 FETCH (ABC 123 StUfF "hello")'),
                          {23: {'ABC': 123,
                                'STUFF': 'hello'}})

    def test_odd_pairs(self):
        self.assertRaises(ParseError, parse_fetch_response, '* 2 FETCH (ONE)')
        self.assertRaises(ParseError, parse_fetch_response, '* 2 FETCH (ONE TWO THREE)')

    def test_UID(self):
        self.assertEquals(parse_fetch_response('* 23 FETCH (UID 76)'),
                          {76: {}})
        self.assertEquals(parse_fetch_response('* 23 FETCH (uiD 76)'),
                          {76: {}})

    def test_bad_UID(self):
        self.assertRaises(ParseError, parse_fetch_response, '* 2 FETCH (UID X)')

    def test_FLAGS(self):
        self.assertEquals(parse_fetch_response('* 23 FETCH (FLAGS (\Seen Stuff))'),
                          {23: {'FLAGS': (r'\Seen', 'Stuff')}})

    def test_multiple_messages(self):
        self.fail()

    def test_INTERNALDATE(self):
        self.fail()

#         def check(date_str, expected_dt):
#             output = self.p(['3 (INTERNALDATE "%s")' % date_str])
#             assert output.keys() == [3]
#             assert output[3].keys() == ['INTERNALDATE']
#             actual_dt = output[3]['INTERNALDATE']

#             # Returned date should be in local timezone
#             self.assert_(actual_dt.tzinfo is None)

#             expected_dt = datetime_to_native(expected_dt)
#             self.assert_(actual_dt == expected_dt,
#                          '%s != %s' % (actual_dt, expected_dt))

#         dt = check(' 9-Feb-2007 17:08:08 -0430',
#                    datetime.datetime(2007, 2, 9, 17, 8, 8, 0, FixedOffset(-4*60 - 30)))

#         dt = check('12-Feb-2007 17:08:08 +0200',
#                    datetime.datetime(2007, 2, 12, 17, 8, 8, 0, FixedOffset(2*60)))

#         dt = check(' 9-Dec-2007 17:08:08 +0000',
#                    datetime.datetime(2007, 12, 9, 17, 8, 8, 0, FixedOffset(0)))

#     def testMultipleMessages(self):
#         '''Test with multple messages in the response
#         '''
#         self._parse_test(
#             [
#                 r'2 (FLAGS (Foo Bar))',
#                 r'7 (FLAGS (Baz Sneeve))',
#                 ],
#             {
#                 2: {'FLAGS': ['Foo', 'Bar']},
#                 7: {'FLAGS': ['Baz', 'Sneeve']},
#                 }
#             )

#     def testLiteral(self):
#         '''Test literal handling
#         '''
#         self._parse_test(
#             [('1 (RFC822 {21}', 'Subject: test\r\n\r\nbody'), ')'],
#             { 1: {'RFC822': 'Subject: test\r\n\r\nbody'} }
#             )

#     def testMultipleLiterals(self):
#         self._parse_test(
#             [
#                 ('1 (RFC822.TEXT {4}', 'body'),
#                 (' RFC822 {21}', 'Subject: test\r\n\r\nbody'),
#                 ')'
#                 ],
#             { 1: {
#                     'RFC822.TEXT': 'body',
#                     'RFC822': 'Subject: test\r\n\r\nbody',
#                     }
#                 }
#             )

#     def testMultiTypesWithLiteral(self):
#         self._parse_test(
#             [
#                 ('1 (INTERNALDATE " 9-Feb-2007 17:08:08 +0100" RFC822 {21}',
#                       'Subject: test\r\n\r\nbody'),
#                 ')'
#                 ],
#             {1: {
#                     'INTERNALDATE': datetime_to_native(datetime.datetime(2007, 2, 9,
#                                                                          17, 8, 8, 0,
#                                                                          FixedOffset(60))),
#                     'RFC822': 'Subject: test\r\n\r\nbody',
#                     }
#                 }
#             )

#     def testLiteralsWithSections(self):
#         self._parse_test(
#             [('1 (BODY[TEXT] {11}', 'Hi there.\r\n'), ')'],
#             { 1: {'BODY[TEXT]': 'Hi there.\r\n',} }
#             )

#     def testLiteralsWithSectionsAndOtherParts(self):
#         self._parse_test(
#             [('1 (FLAGS (\\Seen) UID 2 BODY[HEADER.FIELDS (FROM)] {21}',
#               'From: foo@foo.com\r\n'), ')'],
#             {2: { 'BODY[HEADER.FIELDS (FROM)]': 'From: foo@foo.com\r\n',
#                   'FLAGS': ['\\Seen'],
#                    }
#                 }
#             )

def format_error(input_, output, expected):
    return 'failed for:\n%s\ngot:\n%s\nexpected:\n%s' % (
                pformat(input_),
                pformat(output),
                pformat(expected),
            )

def add_crlf(text):
    return CRLF.join(text.splitlines()) + CRLF

if __name__ == '__main__':
    unittest.main()
<commit_msg>Added a (failing) test as a reminder about quoted specials (as per #28)<commit_after># This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

# Copyright 2009 Menno Smits

'''
Unit tests for the FetchTokeniser and FetchParser classes
'''

from textwrap import dedent
import unittest
from imapclient.response_parser import parse_response, parse_fetch_response, ParseError
from imapclient.fixed_offset import FixedOffset
from pprint import pformat

#TODO: tokenising tests
#TODO: test invalid dates and times
#TODO: more response types
#TODO: clean up this file

CRLF = '\r\n'

class TestParseResponse(unittest.TestCase):

    def test_unquoted(self):
        self._test('FOO', 'FOO')
        self._test('F.O:-O_0;', 'F.O:-O_0;')
        self._test(r'\Seen', r'\Seen')

    def test_string(self):
        self._test('"TEST"', 'TEST')

    def test_int(self):
        self._test('45', 45)

    def test_nil(self):
        self._test('NIL', None)

    def test_empty_tuple(self):
        self._test('()', ())

    def test_tuple(self):
        self._test('(123 "foo" GeE)', (123, 'foo', 'GeE'))

    def test_int_and_tuple(self):
        self._test('1 (123 "foo")', (1, (123, 'foo')), wrap=False)

    def test_nested_tuple(self):
        self._test('(123 "foo" ("more" NIL) 66)',
                   (123, "foo", ("more", None), 66))

    def test_deeper_nest_tuple(self):
        self._test('(123 "foo" ((0 1 2) "more" NIL) 66)',
                   (123, "foo", ((0, 1, 2), "more", None), 66))

    def test_complex_mixed(self):
        self._test('((FOO "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 1152 23)'
                   '("TEXT" "PLAIN" ("CHARSET" "US-ASCII" "NAME" "cc.diff") '
                   '"<hi.there>" "foo" "BASE64" 4554 73) "MIXED")',
                   (('FOO', 'PLAIN', ('CHARSET', 'US-ASCII'), None, None, '7BIT', 1152, 23),
                    ('TEXT', 'PLAIN', ('CHARSET', 'US-ASCII', 'NAME', 'cc.diff'),
                    '<hi.there>', 'foo', 'BASE64', 4554, 73), 'MIXED'))

    def test_literal(self):
        literal_text = add_crlf(dedent("""\
            012
            abc def XYZ
            """))
        self._test('{18}' + CRLF + literal_text, literal_text)

    def test_literal_with_more(self):
        literal_text = add_crlf(dedent("""\
            012
            abc def XYZ
            """))
        response = add_crlf(dedent("""\
            (12 "foo" {18}
            %s)
            """) % literal_text)
        self._test(response, (12, 'foo', literal_text))

    def test_quoted_specials(self):
        self._test(r'"foo \"bar\""', ('foo "bar"',))
        self._test(r'"foo\\bar"', (r'foo\bar',))

    def test_incomplete_tuple(self):
        self._test_parse_error('abc (1 2', 'Tuple incomplete before "(1 2"')

    def test_bad_literal(self):
        self._test_parse_error('{99} abc', 'No CRLF after {99}')

    def test_bad_quoting(self):
        self._test_parse_error('"abc next', 'No closing quotation: "abc next')

    def _test(self, to_parse, expected, wrap=True):
        if wrap:
            # convenience - expected value should be wrapped in another tuple
            expected = (expected,)
        output = parse_response(to_parse)
        self.assert_(
                output == expected,
                format_error(to_parse, output, expected),
            )

    def _test_parse_error(self, to_parse, expected_msg):
        try:
            parse_response(to_parse)
            self.fail("didn't raise an exception")
        except ParseError, err:
            self.assert_(expected_msg == str(err),
                         'got ParseError with wrong msg: %r' % str(err))

system_offset = FixedOffset.for_system()
def datetime_to_native(dt):
    return dt.astimezone(system_offset).replace(tzinfo=None)

class TestParseFetchResponse(unittest.TestCase):

    def test_basic(self):
        self.assertEquals(parse_fetch_response('* 4 FETCH ()'), {4: {}})
        self.assertEquals(parse_fetch_response('* 4 fEtCh ()'), {4: {}})

    def test_non_fetch(self):
        self.assertRaises(ParseError, parse_fetch_response, '* 4 OTHER ()')

    def test_bad_msgid(self):
        self.assertRaises(ParseError, parse_fetch_response, '* abc FETCH ()')

    def test_bad_data(self):
        self.assertRaises(ParseError, parse_fetch_response, '* 2 FETCH WHAT')

    def test_missing_data(self):
        self.assertRaises(ParseError, parse_fetch_response, '* 2 FETCH')

    def test_simple_pairs(self):
        self.assertEquals(parse_fetch_response('* 23 FETCH (ABC 123 StUfF "hello")'),
                          {23: {'ABC': 123,
                                'STUFF': 'hello'}})

    def test_odd_pairs(self):
        self.assertRaises(ParseError, parse_fetch_response, '* 2 FETCH (ONE)')
        self.assertRaises(ParseError, parse_fetch_response, '* 2 FETCH (ONE TWO THREE)')

    def test_UID(self):
        self.assertEquals(parse_fetch_response('* 23 FETCH (UID 76)'),
                          {76: {}})
        self.assertEquals(parse_fetch_response('* 23 FETCH (uiD 76)'),
                          {76: {}})

    def test_bad_UID(self):
        self.assertRaises(ParseError, parse_fetch_response, '* 2 FETCH (UID X)')

    def test_FLAGS(self):
        self.assertEquals(parse_fetch_response('* 23 FETCH (FLAGS (\Seen Stuff))'),
                          {23: {'FLAGS': (r'\Seen', 'Stuff')}})

    def test_multiple_messages(self):
        self.fail()

    def test_INTERNALDATE(self):
        self.fail()

#         def check(date_str, expected_dt):
#             output = self.p(['3 (INTERNALDATE "%s")' % date_str])
#             assert output.keys() == [3]
#             assert output[3].keys() == ['INTERNALDATE']
#             actual_dt = output[3]['INTERNALDATE']

#             # Returned date should be in local timezone
#             self.assert_(actual_dt.tzinfo is None)

#             expected_dt = datetime_to_native(expected_dt)
#             self.assert_(actual_dt == expected_dt,
#                          '%s != %s' % (actual_dt, expected_dt))

#         dt = check(' 9-Feb-2007 17:08:08 -0430',
#                    datetime.datetime(2007, 2, 9, 17, 8, 8, 0, FixedOffset(-4*60 - 30)))

#         dt = check('12-Feb-2007 17:08:08 +0200',
#                    datetime.datetime(2007, 2, 12, 17, 8, 8, 0, FixedOffset(2*60)))

#         dt = check(' 9-Dec-2007 17:08:08 +0000',
#                    datetime.datetime(2007, 12, 9, 17, 8, 8, 0, FixedOffset(0)))

#     def testMultipleMessages(self):
#         '''Test with multple messages in the response
#         '''
#         self._parse_test(
#             [
#                 r'2 (FLAGS (Foo Bar))',
#                 r'7 (FLAGS (Baz Sneeve))',
#                 ],
#             {
#                 2: {'FLAGS': ['Foo', 'Bar']},
#                 7: {'FLAGS': ['Baz', 'Sneeve']},
#                 }
#             )

#     def testLiteral(self):
#         '''Test literal handling
#         '''
#         self._parse_test(
#             [('1 (RFC822 {21}', 'Subject: test\r\n\r\nbody'), ')'],
#             { 1: {'RFC822': 'Subject: test\r\n\r\nbody'} }
#             )

#     def testMultipleLiterals(self):
#         self._parse_test(
#             [
#                 ('1 (RFC822.TEXT {4}', 'body'),
#                 (' RFC822 {21}', 'Subject: test\r\n\r\nbody'),
#                 ')'
#                 ],
#             { 1: {
#                     'RFC822.TEXT': 'body',
#                     'RFC822': 'Subject: test\r\n\r\nbody',
#                     }
#                 }
#             )

#     def testMultiTypesWithLiteral(self):
#         self._parse_test(
#             [
#                 ('1 (INTERNALDATE " 9-Feb-2007 17:08:08 +0100" RFC822 {21}',
#                       'Subject: test\r\n\r\nbody'),
#                 ')'
#                 ],
#             {1: {
#                     'INTERNALDATE': datetime_to_native(datetime.datetime(2007, 2, 9,
#                                                                          17, 8, 8, 0,
#                                                                          FixedOffset(60))),
#                     'RFC822': 'Subject: test\r\n\r\nbody',
#                     }
#                 }
#             )

#     def testLiteralsWithSections(self):
#         self._parse_test(
#             [('1 (BODY[TEXT] {11}', 'Hi there.\r\n'), ')'],
#             { 1: {'BODY[TEXT]': 'Hi there.\r\n',} }
#             )

#     def testLiteralsWithSectionsAndOtherParts(self):
#         self._parse_test(
#             [('1 (FLAGS (\\Seen) UID 2 BODY[HEADER.FIELDS (FROM)] {21}',
#               'From: foo@foo.com\r\n'), ')'],
#             {2: { 'BODY[HEADER.FIELDS (FROM)]': 'From: foo@foo.com\r\n',
#                   'FLAGS': ['\\Seen'],
#                    }
#                 }
#             )

def format_error(input_, output, expected):
    return 'failed for:\n%s\ngot:\n%s\nexpected:\n%s' % (
                pformat(input_),
                pformat(output),
                pformat(expected),
            )

def add_crlf(text):
    return CRLF.join(text.splitlines()) + CRLF

if __name__ == '__main__':
    unittest.main()
<|endoftext|>


<commit_before>import stratum.engine

CONFIG = {
    "num_players": 2,
    "player_names": ["X", "O"]
}

class Engine(stratum.engine.BaseEngine):

    def __init__(self, players=[], view_connection=None):
        super(Engine, self).__init__(players=players, view_connection=view_connection)
        self._board = [[None for _ in range(3)] for _ in range(3)]
        self._winner = None
        self._x_turn = True

    def is_game_over(self):
        for row in self._board:
            if all("X" == cell for cell in row):
                self._winner = "X"
                return True
            elif all("O" == cell for cell in row):
                self._winner = "O"
                return True
        for col in range(3):
            col = tuple(self._board[i][col] for i in range(3))
            if all("X" == cell for cell in col):
                self._winner = "X"
                return True
            elif all("O" == cell for cell in col):
                self._winner = "O"
                return True
        for mod in (1, -1):
            diag = tuple(self._board[mod*i][i] for i in range(3))
            if all("X" == cell for cell in diag):
                self._winner = "X"
                return True
            elif all("O" == cell for cell in diag):
                self._winner = "O"
                return True
        return not any(None == cell for row in self._board for cell in row)

    def get_state(self):
        return {
            "type": "state",
            "board": self._board,
            "winner": self._winner
        }

    def play_turn(self):
        cur_player_id, cur_player_letter = (0, "X") if self._x_turn else (1, "O")
        self.send_message_to_player(cur_player_id, {"type": "turn"})
        move = self.receive_message_from_player(cur_player_id)
        self._board[move["row"]][move["column"]] = cur_player_letter
        self._x_turn = not self._x_turn
<commit_msg>fix win condition checking for TicTacToe<commit_after>import stratum.engine

CONFIG = {
    "num_players": 2,
    "player_names": ["X", "O"]
}

def _get_first_if_all_equal(lst):
    first = lst[0]
    for el in lst[1:]:
        if el != first:
            return None
    return first

class Engine(stratum.engine.BaseEngine):

    def __init__(self, players=[], view_connection=None):
        super(Engine, self).__init__(players=players, view_connection=view_connection)
        self._board = [[None for _ in range(3)] for _ in range(3)]
        self._winner = None
        self._x_turn = True

    def is_game_over(self):
        for i in range(3):
            row_val = _get_first_if_all_equal(self._board[i])
            col_val = _get_first_if_all_equal(tuple(self._board[j][i] for j in range(3)))
            if row_val == "X" or col_val == "X":
                self._winner = "X"
                return True
            elif row_val == "O" or col_val == "O":
                self._winner = "O"
                return True
        diag1_val = _get_first_if_all_equal(tuple(self._board[i][i] for i in range(3)))
        diag2_val = _get_first_if_all_equal(tuple(self._board[2-i][i] for i in range(3)))
        if diag1_val == "X" or diag2_val == "X":
            self._winner = "X"
            return True
        elif diag1_val == "O" or diag2_val == "O":
            self._winner = "O"
            return True
        return not any(None == cell for row in self._board for cell in row)

    def get_state(self):
        return {
            "type": "state",
            "board": self._board,
            "winner": self._winner
        }

    def play_turn(self):
        cur_player_id, cur_player_letter = (0, "X") if self._x_turn else (1, "O")
        self.send_message_to_player(cur_player_id, {"type": "turn"})
        move = self.receive_message_from_player(cur_player_id)
        self._board[move["row"]][move["column"]] = cur_player_letter
        self._x_turn = not self._x_turn
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# {{ repo_name }} documentation build configuration file

import os
import re

def get_version():
    """
    Extracts the version number from the version.py file.
    """
    VERSION_FILE = '../{{ project_name }}/version.py'
    mo = re.search(r'^__version__ = [\'"]([^\'"]*)[\'"]', open(VERSION_FILE, 'rt').read(), re.M)
    if mo:
        return mo.group(1)
    else:
        raise RuntimeError('Unable to find version string in {0}.'.format(VERSION_FILE))

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))

# -- General configuration ------------------------------------------------

extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.intersphinx',
    #'sphinx.ext.viewcode',
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix of source filenames.
source_suffix = '.rst'

# The master toctree document.
master_doc = 'toc'

# General information about the project.
project = u'{{ project_name }}'
copyright = u'{{ current_year }}, Ambition Inc.'

# The short X.Y version.
version = get_version()
# The full version, including alpha/beta/rc tags.
release = version

exclude_patterns = ['_build']

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

intersphinx_mapping = {
    'python': ('http://python.readthedocs.org/en/v2.7.2/', None),
    'django': ('http://django.readthedocs.org/en/latest/', None),
    #'celery': ('http://celery.readthedocs.org/en/latest/', None),
}

# -- Options for HTML output ----------------------------------------------

html_theme = 'default'
#html_theme_path = []

on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
if not on_rtd:  # only import and set the theme if we're building docs locally
    import sphinx_rtd_theme
    html_theme = 'sphinx_rtd_theme'
    html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
html_show_sphinx = False

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
html_show_copyright = True

# Output file base name for HTML help builder.
htmlhelp_basename = '{{ repo_name }}doc'

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',

# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',

# Additional stuff for the LaTeX preamble.
#'preamble': '',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
  ('index', '{{ repo_name }}.tex', u'{{ repo_name }} Documentation',
   u'{{ author_name }}', 'manual'),
]

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    ('index', '{{ repo_name }}', u'{{ repo_name }} Documentation',
     [u'{{ author_name }}'], 1)
]

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
  ('index', '{{ repo_name }}', u'{{ repo_name }} Documentation',
   u'{{ author_name }}', '{{ repo_name }}', 'A short description',
   'Miscellaneous'),
]

# -- Django configuration -------------------------------------------------
import sys
sys.path.insert(0, os.path.abspath('..'))
from settings import configure_settings
configure_settings()
<commit_msg>relocated Django settings configuration<commit_after># -*- coding: utf-8 -*-
#
# {{ repo_name }} documentation build configuration file

import os
import re

# -- Django configuration -------------------------------------------------
import sys
sys.path.insert(0, os.path.abspath('..'))
from settings import configure_settings
configure_settings()

def get_version():
    """
    Extracts the version number from the version.py file.
    """
    VERSION_FILE = '../{{ project_name }}/version.py'
    mo = re.search(r'^__version__ = [\'"]([^\'"]*)[\'"]', open(VERSION_FILE, 'rt').read(), re.M)
    if mo:
        return mo.group(1)
    else:
        raise RuntimeError('Unable to find version string in {0}.'.format(VERSION_FILE))

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))

# -- General configuration ------------------------------------------------

extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.intersphinx',
    #'sphinx.ext.viewcode',
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix of source filenames.
source_suffix = '.rst'

# The master toctree document.
master_doc = 'toc'

# General information about the project.
project = u'{{ project_name }}'
copyright = u'{{ current_year }}, Ambition Inc.'

# The short X.Y version.
version = get_version()
# The full version, including alpha/beta/rc tags.
release = version

exclude_patterns = ['_build']

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

intersphinx_mapping = {
    'python': ('http://python.readthedocs.org/en/v2.7.2/', None),
    'django': ('http://django.readthedocs.org/en/latest/', None),
    #'celery': ('http://celery.readthedocs.org/en/latest/', None),
}

# -- Options for HTML output ----------------------------------------------

html_theme = 'default'
#html_theme_path = []

on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
if not on_rtd:  # only import and set the theme if we're building docs locally
    import sphinx_rtd_theme
    html_theme = 'sphinx_rtd_theme'
    html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
html_show_sphinx = False

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
html_show_copyright = True

# Output file base name for HTML help builder.
htmlhelp_basename = '{{ repo_name }}doc'

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',

# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',

# Additional stuff for the LaTeX preamble.
#'preamble': '',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
  ('index', '{{ repo_name }}.tex', u'{{ repo_name }} Documentation',
   u'{{ author_name }}', 'manual'),
]

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    ('index', '{{ repo_name }}', u'{{ repo_name }} Documentation',
     [u'{{ author_name }}'], 1)
]

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
  ('index', '{{ repo_name }}', u'{{ repo_name }} Documentation',
   u'{{ author_name }}', '{{ repo_name }}', 'A short description',
   'Miscellaneous'),
]
<|endoftext|>


<commit_before>#!/usr/bin/env python2
from __future__ import print_function

from stravalib import Client, exc
#from sys import stderr, stdin
import sys
from tempfile import NamedTemporaryFile
import webbrowser
import os.path
import argparse
import requests
import datetime
try:
    import configparser
    cp = configparser.ConfigParser()
except:
    import ConfigParser
    cp = ConfigParser.ConfigParser()
try:
    from lxml import etree
except ImportError:
    import xml.etree.ElementTree as etree

#####

def intro(file):
    file.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
    file.write("<gpx creator=\"stravasmooth\" version=\"0.0.1\" xmlns=\"http://www.topografix.com/GPX/1/1\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd\">\n")
    file.write(" <metadata>\n")
    file.write("  <time>")
    return

def insert_date_stamp(file,date_stamp):
    date_stamp = date_stamp.replace(" ","T")
    date_stamp = date_stamp.replace("+00:00","Z")
    file.write(date_stamp)
    file.write("</time>\n")
    file.close
    return

def start_segment(file,name,desc):
    file.write(" </metadata>\n")
    file.write(" <trk>\n")
    file.write('  <name>{}</name>\n'.format(name))
    file.write('  <desc>{}</desc>\n'.format(desc))
    file.write("  <trkseg>\n")
    return

def insert_datapoint(file,lat,lon,ele,time):
    file.write('   <trkpt lat=\"{0:.7f}\"'.format(lat))
    file.write('  lon=\"{0:.7f}\">\n'.format(lon))
    file.write('    <ele>{0:.2f}</ele>\n'.format(ele))
    file.write("    ")
    ts = '<time>{}Z</time>\n'.format(time)
    ts = ts.replace(" ","T")
    file.write(ts)
    #file.write('    <time>{}Z</time>\n'.format(time))
    file.write("   </trkpt>\n")
    return

def end_segment(file):
    file.write("  </trkseg>\n")
    file.write(" </trk>\n")
    file.write("</gpx>\n")

    return

#####

# Authorize Strava

def strava_connect():
    cid = 3163 # CLIENT_ID
    cp.read(os.path.expanduser('~/.stravacli'))
    cat = None
    if cp.has_section('API'):
        cat = cp.get('API', 'ACCESS_TOKEN') if 'access_token' in cp.options('API') else None

    while True:
        client = Client(cat)
        try:
            athlete = client.get_athlete()
        except requests.exceptions.ConnectionError:
            print("Could not connect to Strava API")
        except Exception as e:
            print("NOT AUTHORIZED, GET YOUR TOKEN FROM https://stravacli-dlenski.rhcloud.com/ AND SAVE THE THREE LINES TO FILE ~/.stravacli", file=stderr)
            return 0
        else:
            if not cp.has_section('API'):
                cp.add_section('API')
            if not 'ACCESS_TOKEN' in cp.options('API') or cp.get('API', 'ACCESS_TOKEN', None)!=cat:
                cp.set('API', 'ACCESS_TOKEN', cat)
                cp.write(open(os.path.expanduser('~/.stravacli'),"w"))
            break

    print("Authorized to access account of {} {} (id {:d}).".format(athlete.firstname, athlete.lastname, athlete.id))
    return client

def extractgpx(activity_id=0):

    client = strava_connect()
    if client == 0 :
        return ""

    if activity_id == 0 :
        activities = client.get_activities(limit=1)
        for i in activities :
            activity = i

    else :
        activity = client.get_activity(activity_id)

    #assert len(list(activities)) == 1

    types = ['time', 'latlng', 'altitude' ]

    name = activity.name
    name = name.replace(" ","_")

    with open(name+".gpx", 'w') as file :

        intro(file)

        timestamp=str(activity.start_date)

        insert_date_stamp(file,timestamp)

        timestamp = timestamp.replace("+00:00","")

        datestring=timestamp.split(" ")[0].split("-")
        timestring=timestamp.split(" ")[1].split(":")

        start_segment(file,activity.name,activity.description)

        streams = client.get_activity_streams(activity.id, types=types, resolution='high')
        latlng = streams['latlng'].data
        time = streams['time'].data
        altitude = streams['altitude'].data

        start_date = datetime.datetime(int(datestring[0]), int(datestring[1]), int(datestring[2]), int(timestring[0]), int(timestring[1]), int(timestring[2]))

        print("Processing activity \"",name,"\" from date",start_date)

        info = zip(*latlng)
        #info = np.transpose(latlng)

        for it in range(0,len(info[0])):
            lat = info[0][it]
            lon = info[1][it]
            t = start_date + datetime.timedelta(seconds=time[it])
            alt = altitude[it]
            insert_datapoint(file,lat,lon,alt,t)

        end_segment(file)

        return activity

if __name__ == "__main__":

    activity_id = 0

    if len(sys.argv) == 2 :
        activity_id = sys.argv[1]

    if activity_id == 0 :
        print("Retreiving last activity")
    else :
        print("Retreiving activity",activity_id)

    extractgpx(activity_id)

#####

<commit_msg>list gps fix<commit_after>#!/usr/bin/env python2
from __future__ import print_function

from stravalib import Client, exc
#from sys import stderr, stdin
import sys
from tempfile import NamedTemporaryFile
import webbrowser
import os.path
import argparse
import requests
import datetime
try:
    import configparser
    cp = configparser.ConfigParser()
except:
    import ConfigParser
    cp = ConfigParser.ConfigParser()
try:
    from lxml import etree
except ImportError:
    import xml.etree.ElementTree as etree

#####

def intro(file):
    file.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
    file.write("<gpx creator=\"stravasmooth\" version=\"0.0.1\" xmlns=\"http://www.topografix.com/GPX/1/1\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd\">\n")
    file.write(" <metadata>\n")
    file.write("  <time>")
    return

def insert_date_stamp(file,date_stamp):
    date_stamp = date_stamp.replace(" ","T")
    date_stamp = date_stamp.replace("+00:00","Z")
    file.write(date_stamp)
    file.write("</time>\n")
    file.close
    return

def start_segment(file,name,desc):
    file.write(" </metadata>\n")
    file.write(" <trk>\n")
    file.write('  <name>{}</name>\n'.format(name))
    file.write('  <desc>{}</desc>\n'.format(desc))
    file.write("  <trkseg>\n")
    return

def insert_datapoint(file,lat,lon,ele,time):
    file.write('   <trkpt lat=\"{0:.7f}\"'.format(lat))
    file.write('  lon=\"{0:.7f}\">\n'.format(lon))
    file.write('    <ele>{0:.2f}</ele>\n'.format(ele))
    file.write("    ")
    ts = '<time>{}Z</time>\n'.format(time)
    ts = ts.replace(" ","T")
    file.write(ts)
    #file.write('    <time>{}Z</time>\n'.format(time))
    file.write("   </trkpt>\n")
    return

def end_segment(file):
    file.write("  </trkseg>\n")
    file.write(" </trk>\n")
    file.write("</gpx>\n")

    return

#####

# Authorize Strava

def strava_connect():
    cid = 3163 # CLIENT_ID
    cp.read(os.path.expanduser('~/.stravacli'))
    cat = None
    if cp.has_section('API'):
        cat = cp.get('API', 'ACCESS_TOKEN') if 'access_token' in cp.options('API') else None

    while True:
        client = Client(cat)
        try:
            athlete = client.get_athlete()
        except requests.exceptions.ConnectionError:
            print("Could not connect to Strava API")
        except Exception as e:
            print("NOT AUTHORIZED, GET YOUR TOKEN FROM https://stravacli-dlenski.rhcloud.com/ AND SAVE THE THREE LINES TO FILE ~/.stravacli", file=stderr)
            return 0
        else:
            if not cp.has_section('API'):
                cp.add_section('API')
            if not 'ACCESS_TOKEN' in cp.options('API') or cp.get('API', 'ACCESS_TOKEN', None)!=cat:
                cp.set('API', 'ACCESS_TOKEN', cat)
                cp.write(open(os.path.expanduser('~/.stravacli'),"w"))
            break

    print("Authorized to access account of {} {} (id {:d}).".format(athlete.firstname, athlete.lastname, athlete.id))
    return client

def extractgpx(activity_id=0):

    client = strava_connect()
    if client == 0 :
        return ""

    if activity_id == 0 :
        activities = client.get_activities(limit=1)
        for i in activities :
            activity = i

    else :
        activity = client.get_activity(activity_id)

    #assert len(list(activities)) == 1

    types = ['time', 'latlng', 'altitude' ]

    name = activity.name
    name = name.replace(" ","_")

    with open(name+".gpx", 'w') as file :

        intro(file)

        timestamp=str(activity.start_date)

        insert_date_stamp(file,timestamp)

        timestamp = timestamp.replace("+00:00","")

        datestring=timestamp.split(" ")[0].split("-")
        timestring=timestamp.split(" ")[1].split(":")

        start_segment(file,activity.name,activity.description)

        streams = client.get_activity_streams(activity.id, types=types, resolution='high')
        latlng = streams['latlng'].data
        time = streams['time'].data
        altitude = streams['altitude'].data

        start_date = datetime.datetime(int(datestring[0]), int(datestring[1]), int(datestring[2]), int(timestring[0]), int(timestring[1]), int(timestring[2]))

        print("Processing activity \"",name,"\" from date",start_date)

        for it in range(0,len(latlng)):
            lat = latlng[it][0]
            lon = latlng[it][1]
            t = start_date + datetime.timedelta(seconds=time[it])
            alt = altitude[it]
            insert_datapoint(file,lat,lon,alt,t)

        end_segment(file)

        return activity

if __name__ == "__main__":

    activity_id = 0

    if len(sys.argv) == 2 :
        activity_id = sys.argv[1]

    if activity_id == 0 :
        print("Retreiving last activity")
    else :
        print("Retreiving activity",activity_id)

    extractgpx(activity_id)

#####

<|endoftext|>