<commit_before>#!/usr/bin/python

# Copyright (c) 2009, Menno Smits
# Released subject to the New BSD License
# Please see http://en.wikipedia.org/wiki/BSD_licenses

import sys
from datetime import datetime
from optparse import OptionParser
import imapclient

#TODO: more fetch() testing

def test_capabilities(client):
    caps = client.capabilities()
    assert isinstance(caps, tuple)
    assert len(caps) > 1
    for cap in caps:
        assert client.has_capability(cap)
    assert not client.has_capability('WONT EXIST')

def test_list_folders(client):
    folders = client.list_folders()
    assert len(folders) > 0, 'No folders visible on server'
    assert 'INBOX' in [f.upper() for f in folders], 'INBOX not returned'
    #TODO: test wildcards
    #TODO: test other folders...

def test_select_and_close(client):
    num_msgs = client.select_folder('INBOX')
    assert isinstance(num_msgs, long)
    assert num_msgs >= 0
    client.close_folder()

def test_subscriptions(client):
    # Start with a clean slate
    clear_folders(client)

    for folder in client.list_sub_folders():
        client.unsubscribe_folder(folder)

    test_folders = ['foobar',
                    'stuff & things',
                    u'test & \u2622']

    for folder in test_folders:
        client.create_folder(folder)

    all_folders = sorted(client.list_folders())

    for folder in all_folders:
        client.subscribe_folder(folder)

    assert all_folders == sorted(client.list_sub_folders())

    for folder in all_folders:
        client.unsubscribe_folder(folder)
    assert client.list_sub_folders() == []

    assert_raises(imapclient.IMAPClient.Error,
                  client.subscribe_folder,
                  'this folder is not likely to exist')

def test_folders(client):
    '''Test folder manipulation
    '''
    clear_folders(client)

    assert client.folder_exists('INBOX')
    assert not client.folder_exists('this is very unlikely to exist')

    test_folders = ['foobar',
                    'stuff & things',
                    u'test & \u2622']

    for folder in test_folders:
        assert not client.folder_exists(folder)

        client.create_folder(folder)

        assert client.folder_exists(folder)
        assert folder in client.list_folders()

        client.select_folder(folder)
        client.close_folder()

        client.delete_folder(folder)
        assert not client.folder_exists(folder)

def test_status(client):
    clear_folders(client)

    # Default behaviour should return 5 keys
    assert len(client.folder_status('INBOX')) == 5

    new_folder = u'test \u2622'
    client.create_folder(new_folder)
    try:
        status = client.folder_status(new_folder)
        assert status['MESSAGES'] == 0
        assert status['RECENT'] == 0
        assert status['UNSEEN'] == 0

        # Add a message to the folder, it should show up now.
        body = 'Subject: something\r\n\r\nFoo'
        client.append(new_folder, body)

        status = client.folder_status(new_folder)
        assert status['MESSAGES'] == 1
        assert status['RECENT'] == 1
        assert status['UNSEEN'] == 1

    finally:
        client.delete_folder(new_folder)

def test_append(client):
    '''Test that appending a message works correctly
    '''
    clear_folder(client, 'INBOX')

    # Message time microseconds are set to 0 because the server will return
    # time with only seconds precision.
    msg_time = datetime.now().replace(microsecond=0)

    # Append message
    body = 'Subject: something\r\n\r\nFoo\r\n'
    resp = client.append('INBOX', body, ('abc', 'def'), msg_time)
    assert isinstance(resp, str)

    # Retrieve the just added message and check that all looks well
    num_msgs = client.select_folder('INBOX')
    assert num_msgs == 1

    resp = client.fetch(
            client.search()[0],
            ('RFC822', 'FLAGS', 'INTERNALDATE')
            )

    assert len(resp) == 1
    msginfo = resp.values()[0]

    # Time should match the time we specified
    returned_msg_time = msginfo['INTERNALDATE']
    assert returned_msg_time.tzinfo is None
    assert returned_msg_time == msg_time

    # Flags should be the same
    assert 'abc' in msginfo['FLAGS']
    assert 'def' in msginfo['FLAGS']

    # Message body should match
    assert msginfo['RFC822'] == body

def test_flags(client):
    '''Test flag manipulations
    '''
    client.select_folder('INBOX')
    msgid = client.search()[0]

    def _flagtest(func, args, expected_flags):
        answer = func(msgid, *args)

        assert answer.has_key(msgid)
        answer_flags = answer[msgid]

        # This is required because the order of the returned flags isn't
        # guaranteed
        answer_flags.sort()
        expected_flags.sort()

        assert answer_flags == expected_flags

    base_flags = ['abc', 'def']
    _flagtest(client.set_flags, [base_flags], base_flags)
    _flagtest(client.get_flags, [], base_flags)
    _flagtest(client.add_flags, ['boo'], base_flags + ['boo'])
    _flagtest(client.remove_flags, ['boo'], base_flags)

def test_search(client):
    clear_folder(client, 'INBOX')

    # Add some test messages
    msg_tmpl = 'Subject: %s\r\n\r\nBody'
    subjects = ('a', 'b', 'c')
    for subject in subjects:
        msg = msg_tmpl % subject
        if subject == 'c':
            flags = (imapclient.DELETED,)
        else:
            flags = ()
        client.append('INBOX', msg, flags)

    messages_all = client.search('ALL')
    assert len(messages_all) == len(subjects)   # Check we see all messages
    assert client.search() == messages_all      # Check default

    # Single criteria
    assert len(client.search('DELETED')) == 1
    assert len(client.search('NOT DELETED')) == len(subjects) - 1
    assert client.search('NOT DELETED') == client.search(['NOT DELETED'])

    # Multiple criteria
    assert len(client.search(['NOT DELETED', 'SMALLER 100'])) == \
            len(subjects) - 1
    assert len(client.search(['NOT DELETED', 'SUBJECT "a"'])) == 1
    assert len(client.search(['NOT DELETED', 'SUBJECT "c"'])) == 0

def assert_raises(exception_class, func, *args, **kwargs):
    try:
        func(*args, **kwargs)
    except exception_class:
        return
    except Exception, e:
        raise AssertionError('expected %r but got %s instead' % (exception_class, type(e)))
    raise AssertionError('no exception raised, expected %r' % exception_class)

def runtests(client):
    '''Run a sequence of tests against the IMAP server
    '''
    # The ordering of these tests is important
    test_capabilities(client)
    test_list_folders(client)
    test_select_and_close(client)
    test_subscriptions(client)
    test_folders(client)
    test_status(client)
    test_append(client)
    test_flags(client)
    test_search(client)

def clear_folder(client, folder):
    client.select_folder(folder)
    client.delete_messages(client.search())
    client.expunge()

def clear_folders(client):
    client.folder_encode = False
    for folder in client.list_folders():
        if folder.upper() != 'INBOX':
            client.delete_folder(folder)
    client.folder_encode = True

def command_line():
    p = OptionParser()
    p.add_option('-H', '--host', dest='host', action='store',
                 help='IMAP host connect to')
    p.add_option('-P', '--port', dest='port', action='store',
                 default=143, help='IMAP port to use (default is 143)')
    p.add_option('-s', '--ssl', dest='ssl', action='store_true', default=False,
                 help='Use SSL connection')
    p.add_option('-u', '--username', dest='username', action='store',
                 help='Username to login with')
    p.add_option('-p', '--password', dest='password', action='store',
                 help='Password to login with')
    p.add_option('', '--clobber', dest='clobber', action='store_true',
                 default=False, help='These tests are destructive. Use this '
                 'option to bypass the confirmation prompt.')
    p.add_option('', '--interact', dest='interact', action='store_true',
                 default=False,
                 help='Instead of running tests, set up the connection & drop into a shell')

    options, args = p.parse_args()

    if args:
        p.error('unexpected arguments %s' % ' '.join(args))

    for opt_name in ('host', 'username', 'password'):
        if not getattr(options, opt_name):
            p.error('%s must be specified' % opt_name)

    return options

def user_confirm():
    print """\
WARNING: These tests are destructive.
Email in the specified account will be lost!
"""
    r = raw_input('Enter "yes" to confirm this is ok: ')
    if r.lower() != 'yes':
        print "Aborting tests."
        sys.exit()

def main():
    options = command_line()

    if not options.clobber:
        user_confirm()

    # Test with use_uid on and off
    for use_uid in (True, False):
        print '-'*60
        print "Testing with use_uid=%r, ssl=%r..." % (use_uid, options.ssl)
        print '-'*60
        client = imapclient.IMAPClient(options.host, use_uid=use_uid, ssl=options.ssl)
        client.login(options.username, options.password)
        if options.interact:
            import code
            code.interact('HAI! IMAPClient instance is "c"', local=dict(c=client))
            break
        else:
            runtests(client)
            print 'SUCCESS'

if __name__ == '__main__':
    main()

<commit_msg>check folders with backslash and dquote in the names in the live test<commit_after>#!/usr/bin/python

# Copyright (c) 2009, Menno Smits
# Released subject to the New BSD License
# Please see http://en.wikipedia.org/wiki/BSD_licenses

import sys
from datetime import datetime
from optparse import OptionParser
import imapclient

#TODO: more fetch() testing

def test_capabilities(client):
    caps = client.capabilities()
    assert isinstance(caps, tuple)
    assert len(caps) > 1
    for cap in caps:
        assert client.has_capability(cap)
    assert not client.has_capability('WONT EXIST')

def test_list_folders(client):
    clear_folders(client)
    some_folders = ['simple', r'foo\bar', r'test"folder"']
    for name in some_folders:
        client.create_folder(name)

    folders = client.list_folders()
    assert len(folders) > 0, 'No folders visible on server'
    assert 'INBOX' in [f.upper() for f in folders], 'INBOX not returned'

    for name in some_folders:
        assert name in folders
    #TODO: test wildcards

def test_select_and_close(client):
    num_msgs = client.select_folder('INBOX')
    assert isinstance(num_msgs, long)
    assert num_msgs >= 0
    client.close_folder()

def test_subscriptions(client):
    # Start with a clean slate
    clear_folders(client)

    for folder in client.list_sub_folders():
        client.unsubscribe_folder(folder)

    test_folders = ['foobar',
                    'stuff & things',
                    u'test & \u2622']

    for folder in test_folders:
        client.create_folder(folder)

    all_folders = sorted(client.list_folders())

    for folder in all_folders:
        client.subscribe_folder(folder)

    assert all_folders == sorted(client.list_sub_folders())

    for folder in all_folders:
        client.unsubscribe_folder(folder)
    assert client.list_sub_folders() == []

    assert_raises(imapclient.IMAPClient.Error,
                  client.subscribe_folder,
                  'this folder is not likely to exist')

def test_folders(client):
    '''Test folder manipulation
    '''
    clear_folders(client)

    assert client.folder_exists('INBOX')
    assert not client.folder_exists('this is very unlikely to exist')

    test_folders = ['foobar',
                    'stuff & things',
                    u'test & \u2622']

    for folder in test_folders:
        assert not client.folder_exists(folder)

        client.create_folder(folder)

        assert client.folder_exists(folder)
        assert folder in client.list_folders()

        client.select_folder(folder)
        client.close_folder()

        client.delete_folder(folder)
        assert not client.folder_exists(folder)

def test_status(client):
    clear_folders(client)

    # Default behaviour should return 5 keys
    assert len(client.folder_status('INBOX')) == 5

    new_folder = u'test \u2622'
    client.create_folder(new_folder)
    try:
        status = client.folder_status(new_folder)
        assert status['MESSAGES'] == 0
        assert status['RECENT'] == 0
        assert status['UNSEEN'] == 0

        # Add a message to the folder, it should show up now.
        body = 'Subject: something\r\n\r\nFoo'
        client.append(new_folder, body)

        status = client.folder_status(new_folder)
        assert status['MESSAGES'] == 1
        assert status['RECENT'] == 1
        assert status['UNSEEN'] == 1

    finally:
        client.delete_folder(new_folder)

def test_append(client):
    '''Test that appending a message works correctly
    '''
    clear_folder(client, 'INBOX')

    # Message time microseconds are set to 0 because the server will return
    # time with only seconds precision.
    msg_time = datetime.now().replace(microsecond=0)

    # Append message
    body = 'Subject: something\r\n\r\nFoo\r\n'
    resp = client.append('INBOX', body, ('abc', 'def'), msg_time)
    assert isinstance(resp, str)

    # Retrieve the just added message and check that all looks well
    num_msgs = client.select_folder('INBOX')
    assert num_msgs == 1

    resp = client.fetch(
            client.search()[0],
            ('RFC822', 'FLAGS', 'INTERNALDATE')
            )

    assert len(resp) == 1
    msginfo = resp.values()[0]

    # Time should match the time we specified
    returned_msg_time = msginfo['INTERNALDATE']
    assert returned_msg_time.tzinfo is None
    assert returned_msg_time == msg_time

    # Flags should be the same
    assert 'abc' in msginfo['FLAGS']
    assert 'def' in msginfo['FLAGS']

    # Message body should match
    assert msginfo['RFC822'] == body

def test_flags(client):
    '''Test flag manipulations
    '''
    client.select_folder('INBOX')
    msgid = client.search()[0]

    def _flagtest(func, args, expected_flags):
        answer = func(msgid, *args)

        assert answer.has_key(msgid)
        answer_flags = answer[msgid]

        # This is required because the order of the returned flags isn't
        # guaranteed
        answer_flags.sort()
        expected_flags.sort()

        assert answer_flags == expected_flags

    base_flags = ['abc', 'def']
    _flagtest(client.set_flags, [base_flags], base_flags)
    _flagtest(client.get_flags, [], base_flags)
    _flagtest(client.add_flags, ['boo'], base_flags + ['boo'])
    _flagtest(client.remove_flags, ['boo'], base_flags)

def test_search(client):
    clear_folder(client, 'INBOX')

    # Add some test messages
    msg_tmpl = 'Subject: %s\r\n\r\nBody'
    subjects = ('a', 'b', 'c')
    for subject in subjects:
        msg = msg_tmpl % subject
        if subject == 'c':
            flags = (imapclient.DELETED,)
        else:
            flags = ()
        client.append('INBOX', msg, flags)

    messages_all = client.search('ALL')
    assert len(messages_all) == len(subjects)   # Check we see all messages
    assert client.search() == messages_all      # Check default

    # Single criteria
    assert len(client.search('DELETED')) == 1
    assert len(client.search('NOT DELETED')) == len(subjects) - 1
    assert client.search('NOT DELETED') == client.search(['NOT DELETED'])

    # Multiple criteria
    assert len(client.search(['NOT DELETED', 'SMALLER 100'])) == \
            len(subjects) - 1
    assert len(client.search(['NOT DELETED', 'SUBJECT "a"'])) == 1
    assert len(client.search(['NOT DELETED', 'SUBJECT "c"'])) == 0

def assert_raises(exception_class, func, *args, **kwargs):
    try:
        func(*args, **kwargs)
    except exception_class:
        return
    except Exception, e:
        raise AssertionError('expected %r but got %s instead' % (exception_class, type(e)))
    raise AssertionError('no exception raised, expected %r' % exception_class)

def runtests(client):
    '''Run a sequence of tests against the IMAP server
    '''
    # The ordering of these tests is important
    test_capabilities(client)
    test_list_folders(client)
    test_select_and_close(client)
    test_subscriptions(client)
    test_folders(client)
    test_status(client)
    test_append(client)
    test_flags(client)
    test_search(client)

def clear_folder(client, folder):
    client.select_folder(folder)
    client.delete_messages(client.search())
    client.expunge()

def clear_folders(client):
    client.folder_encode = False
    for folder in client.list_folders():
        if folder.upper() != 'INBOX':
            client.delete_folder(folder)
    client.folder_encode = True

def command_line():
    p = OptionParser()
    p.add_option('-H', '--host', dest='host', action='store',
                 help='IMAP host connect to')
    p.add_option('-P', '--port', dest='port', action='store',
                 default=143, help='IMAP port to use (default is 143)')
    p.add_option('-s', '--ssl', dest='ssl', action='store_true', default=False,
                 help='Use SSL connection')
    p.add_option('-u', '--username', dest='username', action='store',
                 help='Username to login with')
    p.add_option('-p', '--password', dest='password', action='store',
                 help='Password to login with')
    p.add_option('', '--clobber', dest='clobber', action='store_true',
                 default=False, help='These tests are destructive. Use this '
                 'option to bypass the confirmation prompt.')
    p.add_option('', '--interact', dest='interact', action='store_true',
                 default=False,
                 help='Instead of running tests, set up the connection & drop into a shell')

    options, args = p.parse_args()

    if args:
        p.error('unexpected arguments %s' % ' '.join(args))

    for opt_name in ('host', 'username', 'password'):
        if not getattr(options, opt_name):
            p.error('%s must be specified' % opt_name)

    return options

def user_confirm():
    print """\
WARNING: These tests are destructive.
Email in the specified account will be lost!
"""
    r = raw_input('Enter "yes" to confirm this is ok: ')
    if r.lower() != 'yes':
        print "Aborting tests."
        sys.exit()

def main():
    options = command_line()

    if not options.clobber:
        user_confirm()

    # Test with use_uid on and off
    for use_uid in (True, False):
        print '-'*60
        print "Testing with use_uid=%r, ssl=%r..." % (use_uid, options.ssl)
        print '-'*60
        client = imapclient.IMAPClient(options.host, use_uid=use_uid, ssl=options.ssl)
        client.login(options.username, options.password)
        if options.interact:
            import code
            code.interact('HAI! IMAPClient instance is "c"', local=dict(c=client))
            break
        else:
            runtests(client)
            print 'SUCCESS'

if __name__ == '__main__':
    main()

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
import operator
import warnings

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.db.models.query_utils import Q
from django.template import TemplateSyntaxError, NodeList, Variable, Context, Template, engines
from django.template.base import VariableNode
from django.template.loader import get_template
from django.template.loader_tags import BlockNode, ExtendsNode, IncludeNode
from django.utils import six
from django.utils.encoding import force_text

from sekizai.helpers import get_varname, is_variable_extend_node

from cms.exceptions import DuplicatePlaceholderWarning
from cms.utils import get_cms_setting

def _get_nodelist(tpl):
    if hasattr(tpl, 'template'):
        return tpl.template.nodelist
    else:
        return tpl.nodelist

def get_context():
    if engines is not None:
        context = Context()
        context.template = Template('')
        return context
    else:
        return {}

def get_placeholder_conf(setting, placeholder, template=None, default=None):
    """
    Returns the placeholder configuration for a given setting. The key would for
    example be 'plugins' or 'name'.

    Resulting value will be the last from:

    CMS_PLACEHOLDER_CONF[None] (global)
    CMS_PLACEHOLDER_CONF['template'] (if template is given)
    CMS_PLACEHOLDER_CONF['placeholder']
    CMS_PLACEHOLDER_CONF['template placeholder'] (if template is given)
    """

    if placeholder:
        keys = []
        placeholder_conf = get_cms_setting('PLACEHOLDER_CONF')
        # 1st level
        if template:
            keys.append(force_text('%s %s' % (template, placeholder)))
        # 2nd level
        keys.append(force_text(placeholder))
        # 3rd level
        if template:
            keys.append(force_text(template))
        # 4th level
        keys.append(None)
        for key in keys:
            for conf_key, conf in placeholder_conf.items():
                if force_text(conf_key) == force_text(key):
                    if not conf:
                        continue
                    value = conf.get(setting)
                    if value is not None:
                        return value
                    inherit = conf.get('inherit')
                    if inherit:
                        if ' ' in inherit:
                            inherit = inherit.split(' ')
                        else:
                            inherit = (None, inherit)
                        value = get_placeholder_conf(setting, inherit[1], inherit[0], default)
                        if value is not None:
                            return value
    return default

def get_toolbar_plugin_struct(plugins, slot=None, page=None):
    """
    Return the list of plugins to render in the toolbar.
    The dictionary contains the label, the classname and the module for the
    plugin.
    Names and modules can be defined on a per-placeholder basis using
    'plugin_modules' and 'plugin_labels' attributes in CMS_PLACEHOLDER_CONF

    :param plugins: list of plugins
    :param slot: placeholder slot name
    :param page: the page
    :return: list of dictionaries
    """
    template = None

    if page:
        template = page.template

    modules = get_placeholder_conf("plugin_modules", slot, template, default={})
    names = get_placeholder_conf("plugin_labels", slot, template, default={})

    main_list = []

    # plugin.value points to the class name of the plugin
    # It's added on registration. TIL.
    for plugin in plugins:
        main_list.append({'value': plugin.value,
                          'name': force_text(names.get(plugin.value, plugin.name)),
                          'module': force_text(modules.get(plugin.value, plugin.module))})
    return sorted(main_list, key=operator.itemgetter("module"))

def validate_placeholder_name(name):
    if not isinstance(name, six.string_types):
        raise ImproperlyConfigured("Placeholder identifier names need to be of type string. ")

    if not all(ord(char) < 128 for char in name):
        raise ImproperlyConfigured("Placeholder identifiers names may not "
                                   "contain non-ascii characters. If you wish your placeholder "
                                   "identifiers to contain non-ascii characters when displayed to "
                                   "users, please use the CMS_PLACEHOLDER_CONF setting with the 'name' "
                                   "key to specify a verbose name.")

class PlaceholderNoAction(object):
    can_copy = False

    def copy(self, **kwargs):
        return False

    def get_copy_languages(self, **kwargs):
        return []

class MLNGPlaceholderActions(PlaceholderNoAction):
    can_copy = True

    def copy(self, target_placeholder, source_language, fieldname, model, target_language, **kwargs):
        from cms.utils.copy_plugins import copy_plugins_to
        trgt = model.objects.get(**{fieldname: target_placeholder})
        src = model.objects.get(master=trgt.master, language_code=source_language)

        source_placeholder = getattr(src, fieldname, None)
        if not source_placeholder:
            return False
        return copy_plugins_to(source_placeholder.get_plugins_list(),
                               target_placeholder, target_language)

    def get_copy_languages(self, placeholder, model, fieldname, **kwargs):
        manager = model.objects
        src = manager.get(**{fieldname: placeholder})
        query = Q(master=src.master)
        query &= Q(**{'%s__cmsplugin__isnull' % fieldname: False})
        query &= ~Q(pk=src.pk)

        language_codes = manager.filter(query).values_list('language_code', flat=True).distinct()
        return [(lc, dict(settings.LANGUAGES)[lc]) for lc in language_codes]

def restore_sekizai_context(context, changes):
    varname = get_varname()
    sekizai_container = context.get(varname)
    for key, values in changes.items():
        sekizai_namespace = sekizai_container[key]
        for value in values:
            sekizai_namespace.append(value)

def _scan_placeholders(nodelist, current_block=None, ignore_blocks=None):
    from cms.templatetags.cms_tags import Placeholder

    placeholders = []
    if ignore_blocks is None:
        # List of BlockNode instances to ignore.
        # This is important to avoid processing overriden block nodes.
        ignore_blocks = []

    for node in nodelist:
        # check if this is a placeholder first
        if isinstance(node, Placeholder):
            placeholders.append(node.get_name())
        elif isinstance(node, IncludeNode):
            # if there's an error in the to-be-included template, node.template becomes None
            if node.template:
                # Check if it quacks like a template object, if not
                # presume is a template path and get the object out of it
                if not callable(getattr(node.template, 'render', None)):
                    # If it's a variable there is no way to expand it at this stage so we
                    # need to skip it
                    if isinstance(node.template.var, Variable):
                        continue
                    else:
                        template = get_template(node.template.var)
                else:
                    template = node.template
                placeholders += _scan_placeholders(_get_nodelist(template), current_block)
        # handle {% extends ... %} tags
        elif isinstance(node, ExtendsNode):
            placeholders += _extend_nodelist(node)
        # in block nodes we have to scan for super blocks
        elif isinstance(node, VariableNode) and current_block:
            if node.filter_expression.token == 'block.super':
                if not hasattr(current_block.super, 'nodelist'):
                    raise TemplateSyntaxError("Cannot render block.super for blocks without a parent.")
                placeholders += _scan_placeholders(_get_nodelist(current_block.super), current_block.super)
        # ignore nested blocks which are already handled
        elif isinstance(node, BlockNode) and node.name in ignore_blocks:
            continue
        # if the node has the newly introduced 'child_nodelists' attribute, scan
        # those attributes for nodelists and recurse them
        elif hasattr(node, 'child_nodelists'):
            for nodelist_name in node.child_nodelists:
                if hasattr(node, nodelist_name):
                    subnodelist = getattr(node, nodelist_name)
                    if isinstance(subnodelist, NodeList):
                        if isinstance(node, BlockNode):
                            current_block = node
                        placeholders += _scan_placeholders(subnodelist, current_block, ignore_blocks)
        # else just scan the node for nodelist instance attributes
        else:
            for attr in dir(node):
                obj = getattr(node, attr)
                if isinstance(obj, NodeList):
                    if isinstance(node, BlockNode):
                        current_block = node
                    placeholders += _scan_placeholders(obj, current_block, ignore_blocks)
    return placeholders

def get_placeholders(template):
    compiled_template = get_template(template)
    placeholders = _scan_placeholders(_get_nodelist(compiled_template))
    clean_placeholders = []
    for placeholder in placeholders:
        if placeholder in clean_placeholders:
            warnings.warn("Duplicate {{% placeholder \"{0}\" %}} "
                          "in template {1}."
                          .format(placeholder, template, placeholder),
                          DuplicatePlaceholderWarning)
        else:
            validate_placeholder_name(placeholder)
            clean_placeholders.append(placeholder)
    return clean_placeholders

def _extend_nodelist(extend_node):
    """
    Returns a list of placeholders found in the parent template(s) of this
    ExtendsNode
    """
    # we don't support variable extensions
    if is_variable_extend_node(extend_node):
        return []
        # This is a dictionary mapping all BlockNode instances found in the template that contains extend_node
    blocks = dict(extend_node.blocks)
    _extend_blocks(extend_node, blocks)
    placeholders = []

    for block in blocks.values():
        placeholders += _scan_placeholders(_get_nodelist(block), block, blocks.keys())

    # Scan topmost template for placeholder outside of blocks
    parent_template = _find_topmost_template(extend_node)
    placeholders += _scan_placeholders(_get_nodelist(parent_template), None, blocks.keys())
    return placeholders

def _find_topmost_template(extend_node):
    parent_template = extend_node.get_parent(get_context())
    for node in _get_nodelist(parent_template).get_nodes_by_type(ExtendsNode):
        # Their can only be one extend block in a template, otherwise django raises an exception
        return _find_topmost_template(node)
        # No ExtendsNode
    return extend_node.get_parent(get_context())

def _extend_blocks(extend_node, blocks):
    """
    Extends the dictionary `blocks` with *new* blocks in the parent node (recursive)
    """
    # we don't support variable extensions
    if is_variable_extend_node(extend_node):
        return
    parent = extend_node.get_parent(get_context())
    # Search for new blocks
    for node in _get_nodelist(parent).get_nodes_by_type(BlockNode):
        if not node.name in blocks:
            blocks[node.name] = node
        else:
            # set this node as the super node (for {{ block.super }})
            block = blocks[node.name]
            seen_supers = []
            while hasattr(block.super, 'nodelist') and block.super not in seen_supers:
                seen_supers.append(block.super)
                block = block.super
            block.super = node
        # search for further ExtendsNodes
    for node in _get_nodelist(parent).get_nodes_by_type(ExtendsNode):
        _extend_blocks(node, blocks)
        break
<commit_msg>Remove unneeded force_text<commit_after># -*- coding: utf-8 -*-
import operator
import warnings

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.db.models.query_utils import Q
from django.template import TemplateSyntaxError, NodeList, Variable, Context, Template, engines
from django.template.base import VariableNode
from django.template.loader import get_template
from django.template.loader_tags import BlockNode, ExtendsNode, IncludeNode
from django.utils import six
from django.utils.encoding import force_text

from sekizai.helpers import get_varname, is_variable_extend_node

from cms.exceptions import DuplicatePlaceholderWarning
from cms.utils import get_cms_setting

def _get_nodelist(tpl):
    if hasattr(tpl, 'template'):
        return tpl.template.nodelist
    else:
        return tpl.nodelist

def get_context():
    if engines is not None:
        context = Context()
        context.template = Template('')
        return context
    else:
        return {}

def get_placeholder_conf(setting, placeholder, template=None, default=None):
    """
    Returns the placeholder configuration for a given setting. The key would for
    example be 'plugins' or 'name'.

    Resulting value will be the last from:

    CMS_PLACEHOLDER_CONF[None] (global)
    CMS_PLACEHOLDER_CONF['template'] (if template is given)
    CMS_PLACEHOLDER_CONF['placeholder']
    CMS_PLACEHOLDER_CONF['template placeholder'] (if template is given)
    """

    if placeholder:
        keys = []
        placeholder_conf = get_cms_setting('PLACEHOLDER_CONF')
        # 1st level
        if template:
            keys.append(u'%s %s' % (template, placeholder))
        # 2nd level
        keys.append(placeholder)
        # 3rd level
        if template:
            keys.append(template)
        # 4th level
        keys.append(None)
        for key in keys:
            for conf_key, conf in placeholder_conf.items():
                if force_text(conf_key) == force_text(key):
                    if not conf:
                        continue
                    value = conf.get(setting)
                    if value is not None:
                        return value
                    inherit = conf.get('inherit')
                    if inherit:
                        if ' ' in inherit:
                            inherit = inherit.split(' ')
                        else:
                            inherit = (None, inherit)
                        value = get_placeholder_conf(setting, inherit[1], inherit[0], default)
                        if value is not None:
                            return value
    return default

def get_toolbar_plugin_struct(plugins, slot=None, page=None):
    """
    Return the list of plugins to render in the toolbar.
    The dictionary contains the label, the classname and the module for the
    plugin.
    Names and modules can be defined on a per-placeholder basis using
    'plugin_modules' and 'plugin_labels' attributes in CMS_PLACEHOLDER_CONF

    :param plugins: list of plugins
    :param slot: placeholder slot name
    :param page: the page
    :return: list of dictionaries
    """
    template = None

    if page:
        template = page.template

    modules = get_placeholder_conf("plugin_modules", slot, template, default={})
    names = get_placeholder_conf("plugin_labels", slot, template, default={})

    main_list = []

    # plugin.value points to the class name of the plugin
    # It's added on registration. TIL.
    for plugin in plugins:
        main_list.append({'value': plugin.value,
                          'name': force_text(names.get(plugin.value, plugin.name)),
                          'module': force_text(modules.get(plugin.value, plugin.module))})
    return sorted(main_list, key=operator.itemgetter("module"))

def validate_placeholder_name(name):
    if not isinstance(name, six.string_types):
        raise ImproperlyConfigured("Placeholder identifier names need to be of type string. ")

    if not all(ord(char) < 128 for char in name):
        raise ImproperlyConfigured("Placeholder identifiers names may not "
                                   "contain non-ascii characters. If you wish your placeholder "
                                   "identifiers to contain non-ascii characters when displayed to "
                                   "users, please use the CMS_PLACEHOLDER_CONF setting with the 'name' "
                                   "key to specify a verbose name.")

class PlaceholderNoAction(object):
    can_copy = False

    def copy(self, **kwargs):
        return False

    def get_copy_languages(self, **kwargs):
        return []

class MLNGPlaceholderActions(PlaceholderNoAction):
    can_copy = True

    def copy(self, target_placeholder, source_language, fieldname, model, target_language, **kwargs):
        from cms.utils.copy_plugins import copy_plugins_to
        trgt = model.objects.get(**{fieldname: target_placeholder})
        src = model.objects.get(master=trgt.master, language_code=source_language)

        source_placeholder = getattr(src, fieldname, None)
        if not source_placeholder:
            return False
        return copy_plugins_to(source_placeholder.get_plugins_list(),
                               target_placeholder, target_language)

    def get_copy_languages(self, placeholder, model, fieldname, **kwargs):
        manager = model.objects
        src = manager.get(**{fieldname: placeholder})
        query = Q(master=src.master)
        query &= Q(**{'%s__cmsplugin__isnull' % fieldname: False})
        query &= ~Q(pk=src.pk)

        language_codes = manager.filter(query).values_list('language_code', flat=True).distinct()
        return [(lc, dict(settings.LANGUAGES)[lc]) for lc in language_codes]

def restore_sekizai_context(context, changes):
    varname = get_varname()
    sekizai_container = context.get(varname)
    for key, values in changes.items():
        sekizai_namespace = sekizai_container[key]
        for value in values:
            sekizai_namespace.append(value)

def _scan_placeholders(nodelist, current_block=None, ignore_blocks=None):
    from cms.templatetags.cms_tags import Placeholder

    placeholders = []
    if ignore_blocks is None:
        # List of BlockNode instances to ignore.
        # This is important to avoid processing overriden block nodes.
        ignore_blocks = []

    for node in nodelist:
        # check if this is a placeholder first
        if isinstance(node, Placeholder):
            placeholders.append(node.get_name())
        elif isinstance(node, IncludeNode):
            # if there's an error in the to-be-included template, node.template becomes None
            if node.template:
                # Check if it quacks like a template object, if not
                # presume is a template path and get the object out of it
                if not callable(getattr(node.template, 'render', None)):
                    # If it's a variable there is no way to expand it at this stage so we
                    # need to skip it
                    if isinstance(node.template.var, Variable):
                        continue
                    else:
                        template = get_template(node.template.var)
                else:
                    template = node.template
                placeholders += _scan_placeholders(_get_nodelist(template), current_block)
        # handle {% extends ... %} tags
        elif isinstance(node, ExtendsNode):
            placeholders += _extend_nodelist(node)
        # in block nodes we have to scan for super blocks
        elif isinstance(node, VariableNode) and current_block:
            if node.filter_expression.token == 'block.super':
                if not hasattr(current_block.super, 'nodelist'):
                    raise TemplateSyntaxError("Cannot render block.super for blocks without a parent.")
                placeholders += _scan_placeholders(_get_nodelist(current_block.super), current_block.super)
        # ignore nested blocks which are already handled
        elif isinstance(node, BlockNode) and node.name in ignore_blocks:
            continue
        # if the node has the newly introduced 'child_nodelists' attribute, scan
        # those attributes for nodelists and recurse them
        elif hasattr(node, 'child_nodelists'):
            for nodelist_name in node.child_nodelists:
                if hasattr(node, nodelist_name):
                    subnodelist = getattr(node, nodelist_name)
                    if isinstance(subnodelist, NodeList):
                        if isinstance(node, BlockNode):
                            current_block = node
                        placeholders += _scan_placeholders(subnodelist, current_block, ignore_blocks)
        # else just scan the node for nodelist instance attributes
        else:
            for attr in dir(node):
                obj = getattr(node, attr)
                if isinstance(obj, NodeList):
                    if isinstance(node, BlockNode):
                        current_block = node
                    placeholders += _scan_placeholders(obj, current_block, ignore_blocks)
    return placeholders

def get_placeholders(template):
    compiled_template = get_template(template)
    placeholders = _scan_placeholders(_get_nodelist(compiled_template))
    clean_placeholders = []
    for placeholder in placeholders:
        if placeholder in clean_placeholders:
            warnings.warn("Duplicate {{% placeholder \"{0}\" %}} "
                          "in template {1}."
                          .format(placeholder, template, placeholder),
                          DuplicatePlaceholderWarning)
        else:
            validate_placeholder_name(placeholder)
            clean_placeholders.append(placeholder)
    return clean_placeholders

def _extend_nodelist(extend_node):
    """
    Returns a list of placeholders found in the parent template(s) of this
    ExtendsNode
    """
    # we don't support variable extensions
    if is_variable_extend_node(extend_node):
        return []
        # This is a dictionary mapping all BlockNode instances found in the template that contains extend_node
    blocks = dict(extend_node.blocks)
    _extend_blocks(extend_node, blocks)
    placeholders = []

    for block in blocks.values():
        placeholders += _scan_placeholders(_get_nodelist(block), block, blocks.keys())

    # Scan topmost template for placeholder outside of blocks
    parent_template = _find_topmost_template(extend_node)
    placeholders += _scan_placeholders(_get_nodelist(parent_template), None, blocks.keys())
    return placeholders

def _find_topmost_template(extend_node):
    parent_template = extend_node.get_parent(get_context())
    for node in _get_nodelist(parent_template).get_nodes_by_type(ExtendsNode):
        # Their can only be one extend block in a template, otherwise django raises an exception
        return _find_topmost_template(node)
        # No ExtendsNode
    return extend_node.get_parent(get_context())

def _extend_blocks(extend_node, blocks):
    """
    Extends the dictionary `blocks` with *new* blocks in the parent node (recursive)
    """
    # we don't support variable extensions
    if is_variable_extend_node(extend_node):
        return
    parent = extend_node.get_parent(get_context())
    # Search for new blocks
    for node in _get_nodelist(parent).get_nodes_by_type(BlockNode):
        if not node.name in blocks:
            blocks[node.name] = node
        else:
            # set this node as the super node (for {{ block.super }})
            block = blocks[node.name]
            seen_supers = []
            while hasattr(block.super, 'nodelist') and block.super not in seen_supers:
                seen_supers.append(block.super)
                block = block.super
            block.super = node
        # search for further ExtendsNodes
    for node in _get_nodelist(parent).get_nodes_by_type(ExtendsNode):
        _extend_blocks(node, blocks)
        break
<|endoftext|>


<commit_before>__version__ = '4.0.0-rc.2.dev'
<commit_msg>Mark v4.0.0-rc.3<commit_after>__version__ = '4.0.0-rc.3'
<|endoftext|>


<commit_before>import argparse
import collections

from nltk.corpus import comtrans
import numpy

import chainer
from chainer import cuda
import chainer.functions as F
import chainer.links as L
from chainer import reporter
from chainer import training
from chainer.training import extensions

def sequence_embed(embed, xs):
    x_len = [len(x) for x in xs]
    x_section = numpy.cumsum(x_len[:-1])
    ex = embed(F.concat(xs, axis=0))
    exs = F.split_axis(ex, x_section, 0, force_tuple=True)
    return exs

class Seq2seq(chainer.Chain):

    def __init__(self, n_layers, n_source_vocab, n_target_vocab, n_units):
        super(Seq2seq, self).__init__(
            embed_x=L.EmbedID(n_source_vocab, n_units),
            embed_y=L.EmbedID(n_target_vocab, n_units),
            encoder=L.NStepLSTM(n_layers, n_units, n_units, 0.1),
            decoder=L.NStepLSTM(n_layers, n_units, n_units, 0.1),
            W=L.Linear(n_units, n_target_vocab),
        )
        self.n_layers = n_layers
        self.n_units = n_units

    def __call__(self, *inputs):
        xs = inputs[:len(inputs) // 2]
        ys = inputs[len(inputs) // 2:]

        eos = self.xp.array([0], 'i')
        ys_in = [F.concat([eos, y], axis=0) for y in ys]
        ys_out = [F.concat([y, eos], axis=0) for y in ys]

        exs = sequence_embed(self.embed_x, xs)
        eys = sequence_embed(self.embed_y, ys_in)

        batch = len(xs)
        # Initial hidden variable and cell variable
        zero = self.xp.zeros((self.n_layers, batch, self.n_units), 'f')
        hx, cx, _ = self.encoder(zero, zero, exs)
        _, _, os = self.decoder(hx, cx, eys)
        loss = F.softmax_cross_entropy(
            self.W(F.concat(os, axis=0)), F.concat(ys_out, axis=0))

        reporter.report({'loss': loss}, self)
        return loss

    def translate(self, x):
        exs = sequence_embed(self.embed_x, [x])
        # Initial hidden variable and cell variable
        zero = self.xp.zeros((self.n_layers, 1, self.n_units), 'f')
        h, c, _ = self.encoder(zero, zero, exs)
        y = self.xp.zeros(1, 'i')
        result = []
        for i in range(10):
            ey = self.embed_y(y)
            h, c, ys = self.decoder(h, c, [ey])
            wy = self.W(ys[0])
            # TODO(unno): CuPy does not have argmax method
            yi = numpy.argmax(cuda.to_cpu(wy.data[0]))
            if yi == 0:
                # Found eos
                break
            result.append(yi)
            y = self.xp.array([yi], 'i')

        return result

def convert(batch, device):
    if device is None:
        def to_device(x):
            return x
    elif device < 0:
        to_device = cuda.to_cpu
    else:
        def to_device(x):
            return cuda.to_gpu(x, device, cuda.Stream.null)

    return tuple(
        [to_device(x) for x, _ in batch] + [to_device(y) for _, y in batch])

def main():
    parser = argparse.ArgumentParser(description='Chainer example: seq2seq')
    parser.add_argument('--batchsize', '-b', type=int, default=100,
                        help='Number of images in each mini-batch')
    parser.add_argument('--epoch', '-e', type=int, default=20,
                        help='Number of sweeps over the dataset to train')
    parser.add_argument('--gpu', '-g', type=int, default=-1,
                        help='GPU ID (negative value indicates CPU)')
    parser.add_argument('--out', '-o', default='result',
                        help='Directory to output the result')
    parser.add_argument('--resume', '-r', default='',
                        help='Resume the training from snapshot')
    parser.add_argument('--unit', '-u', type=int, default=256,
                        help='Number of units')
    args = parser.parse_args()

    sentences = comtrans.aligned_sents('alignment-en-fr.txt')
    source_ids = collections.defaultdict(lambda: len(source_ids))
    target_ids = collections.defaultdict(lambda: len(target_ids))
    target_ids['eos']
    data = []
    for sentence in sentences:
        source = numpy.array([source_ids[w] for w in sentence.words], 'i')
        target = numpy.array([target_ids[w] for w in sentence.mots], 'i')
        data.append((source, target))
    print('Source vocabulary: %d' % len(source_ids))
    print('Target vocabulary: %d' % len(target_ids))

    target_words = {i: w for w, i in target_ids.items()}

    model = Seq2seq(3, len(source_ids), len(target_ids), args.unit)
    if args.gpu >= 0:
        model.to_gpu(args.gpu)

    optimizer = chainer.optimizers.Adam()
    optimizer.setup(model)

    train_iter = chainer.iterators.SerialIterator(data, 50)
    updater = training.StandardUpdater(
        train_iter, optimizer, converter=convert, device=args.gpu)
    trainer = training.Trainer(updater, (10, 'epoch'))
    trainer.extend(extensions.LogReport())
    trainer.extend(extensions.PrintReport(
        ['epoch', 'main/loss', 'validation/main/loss',
         'main/accuracy', 'validation/main/accuracy', 'elapsed_time']))

    @chainer.training.make_extension(trigger=(1, 'iteration'))
    def translate(trainer):
        words = ['Resumption', 'of', 'the', 'session']
        x = model.xp.array([source_ids[w] for w in words], 'i')
        ys = model.translate(x)
        words = [target_words[y] for y in ys]
        print(' '.join(words))

    trainer.extend(translate)
    trainer.run()

if __name__ == '__main__':
    main()
<commit_msg>Make an extension to print bleu score<commit_after>import argparse
import collections

from nltk.corpus import comtrans
from nltk.translate import bleu_score
import numpy

import chainer
from chainer import cuda
import chainer.functions as F
import chainer.links as L
from chainer import reporter
from chainer import training
from chainer.training import extensions

def sequence_embed(embed, xs):
    x_len = [len(x) for x in xs]
    x_section = numpy.cumsum(x_len[:-1])
    ex = embed(F.concat(xs, axis=0))
    exs = F.split_axis(ex, x_section, 0, force_tuple=True)
    return exs

class Seq2seq(chainer.Chain):

    def __init__(self, n_layers, n_source_vocab, n_target_vocab, n_units):
        super(Seq2seq, self).__init__(
            embed_x=L.EmbedID(n_source_vocab, n_units),
            embed_y=L.EmbedID(n_target_vocab, n_units),
            encoder=L.NStepLSTM(n_layers, n_units, n_units, 0.1),
            decoder=L.NStepLSTM(n_layers, n_units, n_units, 0.1),
            W=L.Linear(n_units, n_target_vocab),
        )
        self.n_layers = n_layers
        self.n_units = n_units

    def __call__(self, *inputs):
        xs = inputs[:len(inputs) // 2]
        ys = inputs[len(inputs) // 2:]

        eos = self.xp.array([0], 'i')
        ys_in = [F.concat([eos, y], axis=0) for y in ys]
        ys_out = [F.concat([y, eos], axis=0) for y in ys]

        exs = sequence_embed(self.embed_x, xs)
        eys = sequence_embed(self.embed_y, ys_in)

        batch = len(xs)
        # Initial hidden variable and cell variable
        zero = self.xp.zeros((self.n_layers, batch, self.n_units), 'f')
        hx, cx, _ = self.encoder(zero, zero, exs)
        _, _, os = self.decoder(hx, cx, eys)
        loss = F.softmax_cross_entropy(
            self.W(F.concat(os, axis=0)), F.concat(ys_out, axis=0))

        reporter.report({'loss': loss}, self)
        return loss

    def translate(self, x):
        exs = sequence_embed(self.embed_x, [x])
        # Initial hidden variable and cell variable
        zero = self.xp.zeros((self.n_layers, 1, self.n_units), 'f')
        h, c, _ = self.encoder(zero, zero, exs)
        y = self.xp.zeros(1, 'i')
        result = []
        for i in range(10):
            ey = self.embed_y(y)
            h, c, ys = self.decoder(h, c, [ey])
            wy = self.W(ys[0])
            # TODO(unno): CuPy does not have argmax method
            yi = numpy.argmax(cuda.to_cpu(wy.data[0]))
            if yi == 0:
                # Found eos
                break
            result.append(yi)
            y = self.xp.array([yi], 'i')

        return result

def convert(batch, device):
    if device is None:
        def to_device(x):
            return x
    elif device < 0:
        to_device = cuda.to_cpu
    else:
        def to_device(x):
            return cuda.to_gpu(x, device, cuda.Stream.null)

    return tuple(
        [to_device(x) for x, _ in batch] + [to_device(y) for _, y in batch])

def main():
    parser = argparse.ArgumentParser(description='Chainer example: seq2seq')
    parser.add_argument('--batchsize', '-b', type=int, default=100,
                        help='Number of images in each mini-batch')
    parser.add_argument('--epoch', '-e', type=int, default=20,
                        help='Number of sweeps over the dataset to train')
    parser.add_argument('--gpu', '-g', type=int, default=-1,
                        help='GPU ID (negative value indicates CPU)')
    parser.add_argument('--out', '-o', default='result',
                        help='Directory to output the result')
    parser.add_argument('--resume', '-r', default='',
                        help='Resume the training from snapshot')
    parser.add_argument('--unit', '-u', type=int, default=256,
                        help='Number of units')
    args = parser.parse_args()

    sentences = comtrans.aligned_sents('alignment-en-fr.txt')
    source_ids = collections.defaultdict(lambda: len(source_ids))
    target_ids = collections.defaultdict(lambda: len(target_ids))
    target_ids['eos']
    data = []
    for sentence in sentences:
        source = numpy.array([source_ids[w] for w in sentence.words], 'i')
        target = numpy.array([target_ids[w] for w in sentence.mots], 'i')
        data.append((source, target))
    print('Source vocabulary: %d' % len(source_ids))
    print('Target vocabulary: %d' % len(target_ids))

    test_data = data[:len(data) / 10]
    train_data = data[len(data) / 10:]
    target_words = {i: w for w, i in target_ids.items()}

    model = Seq2seq(3, len(source_ids), len(target_ids), args.unit)
    if args.gpu >= 0:
        model.to_gpu(args.gpu)

    optimizer = chainer.optimizers.Adam()
    optimizer.setup(model)

    train_iter = chainer.iterators.SerialIterator(train_data, 50)
    updater = training.StandardUpdater(
        train_iter, optimizer, converter=convert, device=args.gpu)
    trainer = training.Trainer(updater, (10, 'epoch'))
    trainer.extend(extensions.LogReport())
    trainer.extend(extensions.PrintReport(
        ['epoch', 'main/loss', 'validation/main/loss',
         'main/accuracy', 'validation/main/accuracy', 'elapsed_time']))

    @chainer.training.make_extension(trigger=(1, 'iteration'))
    def translate(trainer):
        words = ['Resumption', 'of', 'the', 'session']
        x = model.xp.array([source_ids[w] for w in words], 'i')
        ys = model.translate(x)
        words = [target_words[y] for y in ys]
        print(' '.join(words))

    @chainer.training.make_extension(trigger=(1, 'epoch'))
    def calc_bleu(trainer):
        references = []
        hypotheses = []
        for source, target in test_data:
            references.append([target])

            ys = model.translate(source)
            hypotheses.append(ys)

        bleu = bleu_score.corpus_bleu(
            references, hypotheses,
            smoothing_function=bleu_score.SmoothingFunction().method1)
        print(bleu)

    trainer.extend(translate)
    trainer.extend(calc_bleu)
    trainer.run()

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


<commit_before>#!/usr/bin/env python

from datetime import datetime
from functools import partial
import logging
import re
import sys
from xml.etree import ElementTree

import argparse
from BeautifulSoup import BeautifulSoup, NavigableString
import django

import giraffe.friends.models
from giraffe.publisher.models import Asset

def main(argv=None):
    if argv is None:
        argv = sys.argv[1:]

    parser = argparse.ArgumentParser(description='Import posts from a livejournal XML export.')
    parser.add_argument('source', metavar='FILE', help='The filename of the XML export (or - for stdin)')
    parser.add_argument('--atomid', help='The prefix of the Atom ID to store', default=None)
    parser.add_argument('-v', dest='verbosity', action='append_const', const=1,
        help='Be more verbose (stackable)', default=[2])
    parser.add_argument('-q', dest='verbosity', action='append_const', const=-1,
        help='Be less verbose (stackable)')

    args = parser.parse_args(argv)

    verbosity = sum(args.verbosity)
    verbosity = 0 if verbosity < 0 else verbosity if verbosity < 4 else 4
    log_level = [logging.CRITICAL, logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG][verbosity]
    logging.getLogger().setLevel(level=log_level)
    logging.info('Set log level to %s', logging.getLevelName(log_level))

    import_events(sys.stdin if args.source == '-' else args.source,
        args.atomid)

    return 0

def generate_openid(server_domain, username):
    if username.startswith('_'):
        return 'http://users.%s/%s/' % (server_domain, username)
    username = username.replace('_', '-')
    return 'http://%s.%s/' % (username, server_domain)

def person_for_openid(openid, display_name):
    try:
        ident_obj = giraffe.friends.models.Identity.objects.get(openid=openid)
    except giraffe.friends.models.Identity.DoesNotExist:
        # Who? I guess we need to make a Person too.
        person = giraffe.friends.models.Person()
        person.display_name = display_name
        person.save()
        ident_obj = giraffe.friends.models.Identity(openid=openid)
        ident_obj.person = person
        ident_obj.save()

    return ident_obj.person

def format_soup(content_root):
    for el in content_root.findAll(text=lambda t: '\n' in t):
        if el.findParent(re.compile(r'pre|lj-raw|table')) is None:
            new_content = el.string.replace('\n', '
\n')
            el.replaceWith(BeautifulSoup(new_content))

def import_comment(comment_el, asset, openid_for):
    jtalkid = comment_el.get('jtalkid')
    atom_id = '%s:talk:%s' % (asset.atom_id, jtalkid)
    logging.debug('Yay importing comment %s', jtalkid)

    try:
        comment = Asset.objects.get(atom_id=atom_id)
    except Asset.DoesNotExist:
        comment = Asset(atom_id=atom_id)

    comment_props = {}
    for prop in comment_el.findall('props/prop'):
        key = prop.get('name')
        val = prop.get('value')
        comment_props[key] = val

    comment.title = comment_el.findtext('subject') or ''

    body = comment_el.findtext('body')
    if int(comment_props.get('opt_preformatted') or 0):
        comment.content = body
    else:
        logging.debug("    Oops, comment not preformatted, let's parse it")
        content_root = BeautifulSoup(body)
        format_soup(content_root)
        comment.content = str(content_root)

    comment.in_reply_to = asset
    comment.in_thread_of = asset.in_thread_of or asset

    poster = comment_el.get('poster')
    if poster:
        openid = openid_for(poster)
        logging.debug("    Saving %s as comment author", openid)
        comment.author = person_for_openid(openid, poster)
    else:
        logging.debug("    Oh huh this comment was anonymous, fancy that")

    comment.imported = True
    comment.save()

    for reply_el in comment_el.findall('comments/comment'):
        import_comment(reply_el, comment, openid_for)

def import_events(source, atomid_prefix):
    tree = ElementTree.parse(source)

    username = tree.getroot().get('username')
    server = tree.getroot().get('server')
    server_domain = '.'.join(server.rsplit('.', 2)[1:])
    openid_for = partial(generate_openid, server_domain)
    if atomid_prefix is None:
        atomid_prefix = 'urn:lj:%s:atom1:%s:' % (server_domain, username)

    post_author = person_for_openid(openid_for(username), username)

    # First, update groups and friends, so we can knit the posts together right.
    group_objs = dict()
    for group in tree.findall('/friends/group'):
        id = int(group.findtext('id'))
        name = group.findtext('name')

        tag = '%sgroup:%d' % (atomid_prefix, id)
        group_obj, created = giraffe.friends.models.Group.objects.get_or_create(tag=tag,
            defaults={'display_name': name})
        group_objs[id] = group_obj

    for friend in tree.findall('/friends/friend'):
        friendname = friend.findtext('username')
        openid = openid_for(friendname)

        ident_person = person_for_openid(openid, friend.findtext('fullname'))

        # Update their groups.
        group_ids = tuple(int(groupnode.text) for groupnode in friend.findall('groups/group'))
        logging.debug("Setting %s's groups to %r", friendname, group_ids)
        ident_person.groups = [group_objs[id] for id in group_ids]

    # Import the posts.
    for event in tree.findall('/events/event'):
        ditemid = event.get('ditemid')
        logging.debug('Parsing event %s', ditemid)
        atom_id = '%s%s' % (atomid_prefix, ditemid)

        try:
            post = Asset.objects.get(atom_id=atom_id)
        except Asset.DoesNotExist:
            post = Asset(atom_id=atom_id)

        event_props = {}
        for prop in event.findall('props/prop'):
            key = prop.get('name')
            val = prop.get('value')
            event_props[key] = val

        post.title = event.findtext('subject') or ''
        post.author = post_author

        publ = event.findtext('date')
        assert publ, 'event has no date :('
        publ_dt = datetime.strptime(publ, '%Y-%m-%d %H:%M:%S')
        # TODO: is this in the account's timezone or what?
        post.published = publ_dt

        content_root = BeautifulSoup(event.findtext('event'))
        # Add line breaks to the post if it's not preformatted.
        if not int(event_props.get('opt_preformatted', 0)):
            format_soup(content_root)
        # Remove any lj-raw tags.
        for el in content_root.findAll(re.compile(r'lj-(?:raw|cut)')):
            # Replace it with its children.
            el_parent = el.parent
            el_index = el_parent.contents.index(el)
            el.extract()
            for child in reversed(list(el.contents)):
                el_parent.insert(el_index, child)
        # TODO: handle opt_nocomments prop
        # TODO: put music and mood in the post content
        # TODO: handle taglist prop
        post.content = str(content_root)

        post.imported = True
        post.save()
        logging.info('Saved new post %s (%s) as #%d', ditemid, post.title, post.pk)

        security = event.get('security')
        private_group = giraffe.friends.models.Group.objects.get(tag='private')
        if security == 'private':
            logging.debug('Oh ho post %s is all fancy private', ditemid)
            post.private_to = [private_group]
        elif security == 'usemask':
            bin = lambda s: str(s) if s<=1 else bin(s>>1) + str(s&1)

            mask = int(event.get('allowmask'))
            logging.debug('Post %s has mask %s?', ditemid, bin(mask))
            mask_groups = list()

            for i in range(1, 30):
                mask = mask >> 1
                if mask == 0:
                    break
                logging.debug('    Remaining mask %s', bin(mask))
                if mask & 0x01:
                    logging.debug('    Yay %s has group %d!', ditemid, i)
                    if i in group_objs:
                        logging.debug('    And group %d exists woohoo!!', i)
                        mask_groups.append(group_objs[i])

            logging.debug('So post %s gets %d groups', ditemid, len(mask_groups))
            post.private_to = mask_groups

        # Import the comments.
        for comment in event.findall('comments/comment'):
            import_comment(comment, post, openid_for)

if __name__ == '__main__':
    sys.exit(main())
<commit_msg>Remember to set privacy for LJ comments<commit_after>#!/usr/bin/env python

from datetime import datetime
from functools import partial
import logging
import re
import sys
from xml.etree import ElementTree

import argparse
from BeautifulSoup import BeautifulSoup, NavigableString
import django

import giraffe.friends.models
from giraffe.publisher.models import Asset

def main(argv=None):
    if argv is None:
        argv = sys.argv[1:]

    parser = argparse.ArgumentParser(description='Import posts from a livejournal XML export.')
    parser.add_argument('source', metavar='FILE', help='The filename of the XML export (or - for stdin)')
    parser.add_argument('--atomid', help='The prefix of the Atom ID to store', default=None)
    parser.add_argument('-v', dest='verbosity', action='append_const', const=1,
        help='Be more verbose (stackable)', default=[2])
    parser.add_argument('-q', dest='verbosity', action='append_const', const=-1,
        help='Be less verbose (stackable)')

    args = parser.parse_args(argv)

    verbosity = sum(args.verbosity)
    verbosity = 0 if verbosity < 0 else verbosity if verbosity < 4 else 4
    log_level = [logging.CRITICAL, logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG][verbosity]
    logging.getLogger().setLevel(level=log_level)
    logging.info('Set log level to %s', logging.getLevelName(log_level))

    import_events(sys.stdin if args.source == '-' else args.source,
        args.atomid)

    return 0

def generate_openid(server_domain, username):
    if username.startswith('_'):
        return 'http://users.%s/%s/' % (server_domain, username)
    username = username.replace('_', '-')
    return 'http://%s.%s/' % (username, server_domain)

def person_for_openid(openid, display_name):
    try:
        ident_obj = giraffe.friends.models.Identity.objects.get(openid=openid)
    except giraffe.friends.models.Identity.DoesNotExist:
        # Who? I guess we need to make a Person too.
        person = giraffe.friends.models.Person()
        person.display_name = display_name
        person.save()
        ident_obj = giraffe.friends.models.Identity(openid=openid)
        ident_obj.person = person
        ident_obj.save()

    return ident_obj.person

def format_soup(content_root):
    for el in content_root.findAll(text=lambda t: '\n' in t):
        if el.findParent(re.compile(r'pre|lj-raw|table')) is None:
            new_content = el.string.replace('\n', '
\n')
            el.replaceWith(BeautifulSoup(new_content))

def import_comment(comment_el, asset, openid_for):
    jtalkid = comment_el.get('jtalkid')
    atom_id = '%s:talk:%s' % (asset.atom_id, jtalkid)
    logging.debug('Yay importing comment %s', jtalkid)

    try:
        comment = Asset.objects.get(atom_id=atom_id)
    except Asset.DoesNotExist:
        comment = Asset(atom_id=atom_id)

    comment_props = {}
    for prop in comment_el.findall('props/prop'):
        key = prop.get('name')
        val = prop.get('value')
        comment_props[key] = val

    comment.title = comment_el.findtext('subject') or ''

    body = comment_el.findtext('body')
    if int(comment_props.get('opt_preformatted') or 0):
        comment.content = body
    else:
        logging.debug("    Oops, comment not preformatted, let's parse it")
        content_root = BeautifulSoup(body)
        format_soup(content_root)
        comment.content = str(content_root)

    comment.in_reply_to = asset
    comment.in_thread_of = asset.in_thread_of or asset

    poster = comment_el.get('poster')
    if poster:
        openid = openid_for(poster)
        logging.debug("    Saving %s as comment author", openid)
        comment.author = person_for_openid(openid, poster)
    else:
        logging.debug("    Oh huh this comment was anonymous, fancy that")

    comment.imported = True
    comment.save()

    comment.private_to = asset.private_to.all()

    for reply_el in comment_el.findall('comments/comment'):
        import_comment(reply_el, comment, openid_for)

def import_events(source, atomid_prefix):
    tree = ElementTree.parse(source)

    username = tree.getroot().get('username')
    server = tree.getroot().get('server')
    server_domain = '.'.join(server.rsplit('.', 2)[1:])
    openid_for = partial(generate_openid, server_domain)
    if atomid_prefix is None:
        atomid_prefix = 'urn:lj:%s:atom1:%s:' % (server_domain, username)

    post_author = person_for_openid(openid_for(username), username)

    # First, update groups and friends, so we can knit the posts together right.
    group_objs = dict()
    for group in tree.findall('/friends/group'):
        id = int(group.findtext('id'))
        name = group.findtext('name')

        tag = '%sgroup:%d' % (atomid_prefix, id)
        group_obj, created = giraffe.friends.models.Group.objects.get_or_create(tag=tag,
            defaults={'display_name': name})
        group_objs[id] = group_obj

    for friend in tree.findall('/friends/friend'):
        friendname = friend.findtext('username')
        openid = openid_for(friendname)

        ident_person = person_for_openid(openid, friend.findtext('fullname'))

        # Update their groups.
        group_ids = tuple(int(groupnode.text) for groupnode in friend.findall('groups/group'))
        logging.debug("Setting %s's groups to %r", friendname, group_ids)
        ident_person.groups = [group_objs[id] for id in group_ids]

    # Import the posts.
    for event in tree.findall('/events/event'):
        ditemid = event.get('ditemid')
        logging.debug('Parsing event %s', ditemid)
        atom_id = '%s%s' % (atomid_prefix, ditemid)

        try:
            post = Asset.objects.get(atom_id=atom_id)
        except Asset.DoesNotExist:
            post = Asset(atom_id=atom_id)

        event_props = {}
        for prop in event.findall('props/prop'):
            key = prop.get('name')
            val = prop.get('value')
            event_props[key] = val

        post.title = event.findtext('subject') or ''
        post.author = post_author

        publ = event.findtext('date')
        assert publ, 'event has no date :('
        publ_dt = datetime.strptime(publ, '%Y-%m-%d %H:%M:%S')
        # TODO: is this in the account's timezone or what?
        post.published = publ_dt

        content_root = BeautifulSoup(event.findtext('event'))
        # Add line breaks to the post if it's not preformatted.
        if not int(event_props.get('opt_preformatted', 0)):
            format_soup(content_root)
        # Remove any lj-raw tags.
        for el in content_root.findAll(re.compile(r'lj-(?:raw|cut)')):
            # Replace it with its children.
            el_parent = el.parent
            el_index = el_parent.contents.index(el)
            el.extract()
            for child in reversed(list(el.contents)):
                el_parent.insert(el_index, child)
        # TODO: handle opt_nocomments prop
        # TODO: put music and mood in the post content
        # TODO: handle taglist prop
        post.content = str(content_root)

        post.imported = True
        post.save()
        logging.info('Saved new post %s (%s) as #%d', ditemid, post.title, post.pk)

        security = event.get('security')
        private_group = giraffe.friends.models.Group.objects.get(tag='private')
        if security == 'private':
            logging.debug('Oh ho post %s is all fancy private', ditemid)
            post.private_to = [private_group]
        elif security == 'usemask':
            bin = lambda s: str(s) if s<=1 else bin(s>>1) + str(s&1)

            mask = int(event.get('allowmask'))
            logging.debug('Post %s has mask %s?', ditemid, bin(mask))
            mask_groups = list()

            for i in range(1, 30):
                mask = mask >> 1
                if mask == 0:
                    break
                logging.debug('    Remaining mask %s', bin(mask))
                if mask & 0x01:
                    logging.debug('    Yay %s has group %d!', ditemid, i)
                    if i in group_objs:
                        logging.debug('    And group %d exists woohoo!!', i)
                        mask_groups.append(group_objs[i])

            logging.debug('So post %s gets %d groups', ditemid, len(mask_groups))
            post.private_to = mask_groups

        # Import the comments.
        for comment in event.findall('comments/comment'):
            import_comment(comment, post, openid_for)

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


<commit_before>import sublime
from sublime_plugin import WindowCommand

from ..git_command import GitCommand
from ...common import util
from ..ui_mixins.quick_panel import show_remote_panel, show_branch_panel

START_PUSH_MESSAGE = "Starting push..."
END_PUSH_MESSAGE = "Push complete."
PUSH_TO_BRANCH_NAME_PROMPT = "Enter remote branch name:"
SET_UPSTREAM_PROMPT = ("You have not set an upstream for the active branch.  "
                       "Would you like to set one?")
CONFIRM_FORCE_PUSH = ("You are about to `git push --force`. Would you  "
                      "like to proceed?")

class PushBase(GitCommand):
    set_upstream = False

    def do_push(self, remote, branch, force=False, remote_branch=None):
        """
        Perform `git push remote branch`.
        """
        savvy_settings = sublime.load_settings("GitSavvy.sublime-settings")
        if force and savvy_settings.get("confirm_force_push"):
            if not sublime.ok_cancel_dialog(CONFIRM_FORCE_PUSH):
                return

        sublime.status_message(START_PUSH_MESSAGE)
        self.push(
            remote,
            branch,
            set_upstream=self.set_upstream,
            force=force,
            remote_branch=remote_branch
            )
        sublime.status_message(END_PUSH_MESSAGE)
        util.view.refresh_gitsavvy(self.window.active_view())

class GsPushCommand(WindowCommand, PushBase):

    """
    Push current branch.
    """

    def run(self, local_branch_name=None, force=False):
        self.force = force
        self.local_branch_name = local_branch_name
        sublime.set_timeout_async(self.run_async)

    def run_async(self):
        savvy_settings = sublime.load_settings("GitSavvy.sublime-settings")
        if self.local_branch_name:
            upstream = self.get_local_branch(self.local_branch_name).tracking
        else:
            self.local_branch_name = self.get_current_branch_name()
            upstream = self.get_upstream_for_active_branch()

        if upstream:
            remote, remote_branch = upstream.split("/", 1)
            self.do_push(
                remote, self.local_branch_name, remote_branch=remote_branch, force=self.force)
        elif savvy_settings.get("prompt_for_tracking_branch"):
            if sublime.ok_cancel_dialog(SET_UPSTREAM_PROMPT):
                self.window.run_command("gs_push_to_branch_name", {
                    "set_upstream": True,
                    "force": self.force
                    })
        else:
            # if `prompt_for_tracking_branch` is false, ask for a remote and perform
            # push current branch to a remote branch with the same name
            self.window.run_command("gs_push_to_branch_name", {
                                "branch_name": self.local_branch_name,
                                "set_upstream": False,
                                "force": self.force
                                })

class GsPushToBranchCommand(WindowCommand, PushBase):

    """
    Through a series of panels, allow the user to push to a specific remote branch.
    """

    def run(self):
        sublime.set_timeout_async(self.run_async)

    def run_async(self):
        show_branch_panel(self.on_branch_selection, ask_remote_first=True)

    def on_branch_selection(self, branch):
        if not branch:
            return
        current_local_branch = self.get_current_branch_name()
        selected_branch = branch.split("/", 1)[1]
        sublime.set_timeout_async(
            lambda: self.do_push(
                self.selected_remote, current_local_branch, remote_branch=selected_branch))

class GsPushToBranchNameCommand(WindowCommand, PushBase):

    """
    Prompt for remote and remote branch name, then push.
    """

    def run(self, branch_name=None, set_upstream=False, force=False):
        self.branch_name = branch_name
        self.set_upstream = set_upstream
        self.force = force
        sublime.set_timeout_async(self.run_async)

    def run_async(self):
        show_remote_panel(self.on_remote_selection)

    def on_remote_selection(self, remote):
        """
        After the user selects a remote, prompt the user for a branch name.
        """
        # If the user pressed `esc` or otherwise cancelled.
        if not remote:
            return

        self.selected_remote = remote

        if self.branch_name:
            self.on_entered_branch_name(self.branch_name)
        else:
            current_local_branch = self.get_current_branch_name()
            self.window.show_input_panel(
                PUSH_TO_BRANCH_NAME_PROMPT,
                current_local_branch,
                self.on_entered_branch_name,
                None,
                None
                )

    def on_entered_branch_name(self, branch):
        """
        Push to the remote that was previously selected and provided branch
        name.
        """
        sublime.set_timeout_async(lambda: self.do_push(
            self.selected_remote,
            self.get_current_branch_name(),
            self.force,
            remote_branch=branch))
<commit_msg>Fix: "push to existing branch" was not working<commit_after>import sublime
from sublime_plugin import WindowCommand

from ..git_command import GitCommand
from ...common import util
from ..ui_mixins.quick_panel import show_remote_panel, show_branch_panel

START_PUSH_MESSAGE = "Starting push..."
END_PUSH_MESSAGE = "Push complete."
PUSH_TO_BRANCH_NAME_PROMPT = "Enter remote branch name:"
SET_UPSTREAM_PROMPT = ("You have not set an upstream for the active branch.  "
                       "Would you like to set one?")
CONFIRM_FORCE_PUSH = ("You are about to `git push --force`. Would you  "
                      "like to proceed?")

class PushBase(GitCommand):
    set_upstream = False

    def do_push(self, remote, branch, force=False, remote_branch=None):
        """
        Perform `git push remote branch`.
        """
        savvy_settings = sublime.load_settings("GitSavvy.sublime-settings")
        if force and savvy_settings.get("confirm_force_push"):
            if not sublime.ok_cancel_dialog(CONFIRM_FORCE_PUSH):
                return

        sublime.status_message(START_PUSH_MESSAGE)
        self.push(
            remote,
            branch,
            set_upstream=self.set_upstream,
            force=force,
            remote_branch=remote_branch
            )
        sublime.status_message(END_PUSH_MESSAGE)
        util.view.refresh_gitsavvy(self.window.active_view())

class GsPushCommand(WindowCommand, PushBase):

    """
    Push current branch.
    """

    def run(self, local_branch_name=None, force=False):
        self.force = force
        self.local_branch_name = local_branch_name
        sublime.set_timeout_async(self.run_async)

    def run_async(self):
        savvy_settings = sublime.load_settings("GitSavvy.sublime-settings")
        if self.local_branch_name:
            upstream = self.get_local_branch(self.local_branch_name).tracking
        else:
            self.local_branch_name = self.get_current_branch_name()
            upstream = self.get_upstream_for_active_branch()

        if upstream:
            remote, remote_branch = upstream.split("/", 1)
            self.do_push(
                remote, self.local_branch_name, remote_branch=remote_branch, force=self.force)
        elif savvy_settings.get("prompt_for_tracking_branch"):
            if sublime.ok_cancel_dialog(SET_UPSTREAM_PROMPT):
                self.window.run_command("gs_push_to_branch_name", {
                    "set_upstream": True,
                    "force": self.force
                    })
        else:
            # if `prompt_for_tracking_branch` is false, ask for a remote and perform
            # push current branch to a remote branch with the same name
            self.window.run_command("gs_push_to_branch_name", {
                                "branch_name": self.local_branch_name,
                                "set_upstream": False,
                                "force": self.force
                                })

class GsPushToBranchCommand(WindowCommand, PushBase):

    """
    Through a series of panels, allow the user to push to a specific remote branch.
    """

    def run(self):
        sublime.set_timeout_async(self.run_async)

    def run_async(self):
        show_branch_panel(self.on_branch_selection, ask_remote_first=True)

    def on_branch_selection(self, branch):
        if not branch:
            return
        current_local_branch = self.get_current_branch_name()
        selected_remote, selected_branch = branch.split("/", 1)
        sublime.set_timeout_async(
            lambda: self.do_push(
                selected_remote, current_local_branch, remote_branch=selected_branch))

class GsPushToBranchNameCommand(WindowCommand, PushBase):

    """
    Prompt for remote and remote branch name, then push.
    """

    def run(self, branch_name=None, set_upstream=False, force=False):
        self.branch_name = branch_name
        self.set_upstream = set_upstream
        self.force = force
        sublime.set_timeout_async(self.run_async)

    def run_async(self):
        show_remote_panel(self.on_remote_selection)

    def on_remote_selection(self, remote):
        """
        After the user selects a remote, prompt the user for a branch name.
        """
        # If the user pressed `esc` or otherwise cancelled.
        if not remote:
            return

        self.selected_remote = remote

        if self.branch_name:
            self.on_entered_branch_name(self.branch_name)
        else:
            current_local_branch = self.get_current_branch_name()
            self.window.show_input_panel(
                PUSH_TO_BRANCH_NAME_PROMPT,
                current_local_branch,
                self.on_entered_branch_name,
                None,
                None
                )

    def on_entered_branch_name(self, branch):
        """
        Push to the remote that was previously selected and provided branch
        name.
        """
        sublime.set_timeout_async(lambda: self.do_push(
            self.selected_remote,
            self.get_current_branch_name(),
            self.force,
            remote_branch=branch))
<|endoftext|>


<commit_before>import unittest
import p4lib
from mock import Mock
from test_utils import change_stdout, change_stdout_list
from p4lib_opened_test import PX_AND_PX2_DEFAULT_CHANGE

TEMPORARY_FILENAME = "tempFile"
CHANGELIST_DESCRIPTION = "A description"

CHANGE_CREATED = r"Change 1234 created."
WHERE_RESULT = r"//depot/foo/file.py //trent/foo/file.py c:\trent\foo\file.py"

class ChangeTestCase(unittest.TestCase):
    def setUp(self):
        p4lib._run = Mock(spec='p4lib._run', return_value=("", "", 0))
        p4lib._writeTemporaryForm = Mock(spec='p4lib._writeTemporaryForm',
                                         return_value=TEMPORARY_FILENAME)
        p4lib._removeTemporaryForm = Mock(spec='p4lib._removeTemporaryForm')

    def __assert_change_for_creation(self, result):
        p4lib._run.assert_called_with(['p4',
                                       'change',
                                       '-i',
                                       '<',
                                       TEMPORARY_FILENAME])

        self.assertIn("change", result)
        self.assertEqual(1234, result["change"])
        self.assertIn("action", result)
        self.assertEqual("created", result["action"])

    def __get_form(self):
        args, _ = p4lib._writeTemporaryForm.call_args
        return args[0]

    def test_creates_a_change_for_no_file(self):
        change_stdout(CHANGE_CREATED)

        p4 = p4lib.P4()

        result = p4.change(files=[], description=CHANGELIST_DESCRIPTION)

        self.__assert_change_for_creation(result)

        form = self.__get_form()
        self.assertIn("Change:\tnew", form)
        self.assertIn(CHANGELIST_DESCRIPTION, form)
        self.assertNotIn("//depot/apps/px/px.py", form)
        self.assertNotIn("//depot/apps/px/px2.py", form)

    def test_creates_a_change_without_specified_file(self):
        change_stdout_list([PX_AND_PX2_DEFAULT_CHANGE, CHANGE_CREATED])

        p4 = p4lib.P4()

        result = p4.change(description="A description")

        self.__assert_change_for_creation(result)

        form = self.__get_form()
        self.assertIn("Change:\tnew", form)
        self.assertIn(CHANGELIST_DESCRIPTION, form)
        self.assertIn("//depot/apps/px/px.py", form)
        self.assertIn("//depot/apps/px/px2.py", form)

    def test_creates_a_change_with_specified_file(self):
        change_stdout_list([WHERE_RESULT, CHANGE_CREATED])

        p4 = p4lib.P4()

        result = p4.change(files=["//depot/foo/file.py"],
                           description=CHANGELIST_DESCRIPTION)

        self.__assert_change_for_creation(result)

        form = self.__get_form()
        self.assertIn("Change:\tnew", form)
        self.assertIn(CHANGELIST_DESCRIPTION, form)
        self.assertIn("//depot/foo/file.py", form)

<commit_msg>Tests for Change on every situation.<commit_after>import unittest
import p4lib
from mock import Mock
from test_utils import change_stdout, change_stdout_list
from p4lib_opened_test import PX_AND_PX2_DEFAULT_CHANGE

# Note: a better way should be to mock where and opened functions called
# by change() when it needs information
# At now, we're feeding _run() side_effects.
# That's not really unit testing, as it's based on opened() and where()
# functioning.

TEMPORARY_FILENAME = "tempFile"
CHANGELIST_DESCRIPTION = "A description"
CHANGELIST_NEW_DESCRIPTION = "A new description"

CHANGE_CREATED = r"Change 1234 created."
CHANGE_UPDATED = r"Change 1234 updated."
CHANGE_UPDATED_WITH_COMMENTS = r"Change 1234 updated, and blah."
CHANGE_DELETED = r"Change 1234 deleted."
CHANGE_INFORMATION = """Change:\t1234
Date:\t2002/05/08 23:24:54
User:\ttrent
Status:\tpending
Description:
\tA description

Files:
\t//depot/file.txt\t# edit
"""

WHERE_RESULT = r"//depot/foo/file.py //trent/foo/file.py c:\trent\foo\file.py"

class ChangeTestCase(unittest.TestCase):
    def setUp(self):
        p4lib._run = Mock(spec='p4lib._run', return_value=("", "", 0))
        p4lib._writeTemporaryForm = Mock(spec='p4lib._writeTemporaryForm',
                                         return_value=TEMPORARY_FILENAME)
        p4lib._removeTemporaryForm = Mock(spec='p4lib._removeTemporaryForm')

    def __assert_called_with_tempfile(self):
        p4lib._run.assert_called_with(['p4',
                                       'change',
                                       '-i',
                                       '<',
                                       TEMPORARY_FILENAME])

    def __assert_change_for_creation(self, result):
        self.__assert_called_with_tempfile()

        self.assertIn("change", result)
        self.assertEqual(1234, result["change"])
        self.assertIn("action", result)
        self.assertEqual("created", result["action"])

    def __get_form(self):
        args, _ = p4lib._writeTemporaryForm.call_args
        return args[0]

    def test_creates_a_change_for_no_file(self):
        change_stdout(CHANGE_CREATED)

        p4 = p4lib.P4()

        result = p4.change(files=[], description=CHANGELIST_DESCRIPTION)

        self.__assert_change_for_creation(result)

        form = self.__get_form()
        self.assertIn("Change:\tnew", form)
        self.assertIn(CHANGELIST_DESCRIPTION, form)
        self.assertNotIn("//depot/apps/px/px.py", form)
        self.assertNotIn("//depot/apps/px/px2.py", form)

    def test_creates_a_change_without_specified_file(self):
        change_stdout_list([PX_AND_PX2_DEFAULT_CHANGE, CHANGE_CREATED])

        p4 = p4lib.P4()

        result = p4.change(description="A description")

        self.__assert_change_for_creation(result)

        form = self.__get_form()
        self.assertIn("Change:\tnew", form)
        self.assertIn(CHANGELIST_DESCRIPTION, form)
        self.assertIn("//depot/apps/px/px.py", form)
        self.assertIn("//depot/apps/px/px2.py", form)

    def test_creates_a_change_with_specified_file(self):
        change_stdout_list([WHERE_RESULT, CHANGE_CREATED])

        p4 = p4lib.P4()

        result = p4.change(files=["//depot/foo/file.py"],
                           description=CHANGELIST_DESCRIPTION)

        self.__assert_change_for_creation(result)

        form = self.__get_form()
        self.assertIn("Change:\tnew", form)
        self.assertIn(CHANGELIST_DESCRIPTION, form)
        self.assertIn("//depot/foo/file.py", form)

    def test_can_get_information(self):
        change_stdout(CHANGE_INFORMATION)

        p4 = p4lib.P4()

        result = p4.change(change=1234)
        p4lib._run.assert_called_with(['p4', 'change', '-o', '1234'])

        self.assertEqual(1234, result["change"])
        self.assertEqual("pending", result["status"])
        self.assertEqual(CHANGELIST_DESCRIPTION, result["description"])
        self.assertEqual("trent", result["user"])
        self.assertEqual("2002/05/08 23:24:54", result["date"])

    def test_can_change_description(self):
        change_stdout_list([CHANGE_INFORMATION, CHANGE_UPDATED])

        p4 = p4lib.P4()

        result = p4.change(change=1234,
                           description=CHANGELIST_NEW_DESCRIPTION)

        self.__assert_called_with_tempfile()

        self.assertEqual("updated", result["action"])
        self.assertEqual(1234, result["change"])

        form = self.__get_form()
        self.assertIn(CHANGELIST_NEW_DESCRIPTION, form)

    def test_can_change_files(self):
        change_stdout_list([CHANGE_INFORMATION,
                            WHERE_RESULT,
                            CHANGE_UPDATED_WITH_COMMENTS])

        p4 = p4lib.P4()

        result = p4.change(change=1234,
                           files=["//depot/foo/file.py"])

        self.__assert_called_with_tempfile()

        self.assertEqual("updated", result["action"])
        self.assertEqual(1234, result["change"])
        self.assertEqual("and blah", result["comment"])

        form = self.__get_form()
        self.assertIn(CHANGELIST_DESCRIPTION, form)
        self.assertIn("//depot/foo/file.py", form)

    def test_can_be_deleted(self):
        change_stdout(CHANGE_DELETED)

        p4 = p4lib.P4()

        result = p4.change(change=1234, delete=True)

        p4lib._run.assert_called_with(['p4', 'change', '-d', '1234'])

        self.assertEqual("deleted", result["action"])
        self.assertEqual(1234, result["change"])

    def test_cannot_specify_delete_with_files_or_description(self):
        p4 = p4lib.P4()

        self.assertRaises(p4lib.P4LibError, p4.change,
                          change=1234,
                          delete=True,
                          files=[])

        self.assertRaises(p4lib.P4LibError, p4.change,
                          change=1234,
                          delete=True,
                          descritpion="")

    def test_raw_result(self):
        change_stdout(CHANGE_DELETED)

        p4 = p4lib.P4()
        raw_result = p4.change(change=1234, delete=True, _raw=True)

        self.assertIn('stdout', raw_result)
        self.assertIn('stderr', raw_result)
        self.assertIn('retval', raw_result)

        self.assertEqual(CHANGE_DELETED, raw_result['stdout'])

    def test_with_options(self):
        change_stdout(CHANGE_DELETED)

        p4 = p4lib.P4()
        p4.change(change=1234, delete=True, user='other')

        p4lib._run.assert_called_with(['p4',
                                       '-u',
                                       'other',
                                       'change', '-d', '1234'])
<|endoftext|>