<commit_before>'''Plotting tools

'''

import model
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

from simulation import SimulatorResult

def get_parameters_size():
    '''Get a list of parameters for the size plot.'''
    rows_per_server = 2000
    rows_per_partition = 10
    code_rate = 2/3
    muq = 2
    num_columns = int(1e4)
    parameters = list()
    num_servers = [5, 8, 20, 50, 80, 125, 200, 500, 2000]
    for servers in num_servers:
        par = model.SystemParameters.fixed_complexity_parameters(
            rows_per_server=rows_per_server,
            rows_per_partition=rows_per_partition,
            min_num_servers=servers,
            code_rate=code_rate,
            muq=muq,
            num_columns=num_columns
        )
        parameters.append(par)
    return parameters

def get_parameters_size_2():
    '''Get a list of parameters for the size plot.'''
    rows_per_server = 200
    rows_per_partition = 10
    code_rate = 2/3
    muq = 2
    num_columns = None
    num_outputs_factor=100
    parameters = list()
    num_servers = [2, 5, 8, 20, 50]#, 200]
    for servers in num_servers:
        par = model.SystemParameters.fixed_complexity_parameters(
            rows_per_server=rows_per_server,
            rows_per_partition=rows_per_partition,
            min_num_servers=servers,
            code_rate=code_rate,
            muq=muq,
            num_columns=num_columns,
            num_outputs_factor=num_outputs_factor
        )
        parameters.append(par)
    return parameters

def get_parameters_N():
    '''Get a list of parameters for the N over n ratio plot.'''
    rows_per_batch = 50
    num_servers = 9
    q = 6
    num_outputs = q
    server_storage = 1/3
    num_partitions = 50
    parameters = list()
    for i in range(1, 11):
        num_outputs = i * q * 10
        par = model.SystemParameters(
            rows_per_batch=rows_per_batch,
            num_servers=num_servers,
            q=q,
            num_outputs=num_outputs,
            server_storage=server_storage,
            num_partitions=num_partitions,
        )
        parameters.append(par)

    return parameters

def get_parameters_partitioning():
    '''Get a list of parameters for the partitioning plot.'''
    rows_per_batch = 250
    num_servers = 9
    q = 6
    num_outputs = q
    server_storage = 1/3
    num_partitions = [2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 24, 25, 30,
                      40, 50, 60, 75, 100, 120, 125, 150, 200, 250,
                      300, 375, 500, 600, 750, 1000, 1500, 3000]

    parameters = list()
    for partitions in num_partitions:
        par = model.SystemParameters(
            rows_per_batch=rows_per_batch,
            num_servers=num_servers,
            q=q,
            num_outputs=num_outputs,
            server_storage=server_storage,
            num_partitions=partitions,
        )
        parameters.append(par)

    return parameters

def get_parameters_partitioning_2():
    '''Constant system size, increasing partitions, num_outputs=num_columns'''
    rows_per_batch = 250
    num_servers = 9
    q = 6
    num_outputs = 6000
    server_storage = 1/3
    num_partitions = [2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 24, 25, 30,
                      40, 50, 60, 75, 100, 120, 125, 150, 200, 250,
                      300, 375, 500, 600, 750, 1000, 1500, 3000]

    parameters = list()
    for partitions in num_partitions:
        par = model.SystemParameters(
            rows_per_batch=rows_per_batch,
            num_servers=num_servers,
            q=q,
            num_outputs=num_outputs,
            server_storage=server_storage,
            num_partitions=partitions,
        )
        parameters.append(par)

    return parameters

def load_delay_plot(results, plot_settings, xdata, xlabel='', normalize=None, legend='load', show=True):
    '''Create a plot with two subplots for load and delay respectively.

    Args:

    results: SimulatorResult to plot.

    plot_settings: List of dicts with plot settings.

    xdata: Label of the X axis data ('partitions' or 'servers').

    xlabel: X axis label

    normalize: If a SimulatorResult is provided, all ploted results
    are normalized by this one.

    legend: Place the legend in the load or delay plot by setting this
    argument to 'load' or 'delay'.

    show: show the plots if True.

    '''
    assert isinstance(results, list)
    assert isinstance(plot_settings, list)
    # assert isinstance(normalize, SimulatorResult) or normalize is None
    assert isinstance(show, bool)

    plt.rc('pgf',  texsystem='pdflatex')
    plt.rc('text', usetex=True)
    # plt.rc('font', family='serif')
    plt.rcParams['text.latex.preamble'] = [r'\usepackage{lmodern}']
    _ = plt.figure(figsize=(10,6))

    # Plot load
    plt.autoscale(enable=True)
    plt.tight_layout()
    ax1 = plt.subplot(211)
    plt.setp(ax1.get_xticklabels(), fontsize=25, visible=False)
    plt.setp(ax1.get_yticklabels(), fontsize=25)
    for result, plot_setting in zip(results, plot_settings):
        plot_result(result, plot_setting, xdata, 'load',
                    ylabel='Load', subplot=True, normalize=normalize)

    plt.margins(y=0.1)
    if legend == 'load':
        plt.legend(
            numpoints=1,
            shadow=True,
            labelspacing=0,
            fontsize=24,
            loc='best',
            fancybox=False,
            borderaxespad=0.1,
        )

    # Plot delay
    ax2 = plt.subplot(212, sharex=ax1)
    plt.setp(ax2.get_xticklabels(), fontsize=25)
    plt.setp(ax2.get_yticklabels(), fontsize=25)
    for result, plot_setting in zip(results, plot_settings):
        plot_result(result, plot_setting, xdata, 'delay', xlabel=xlabel,
                    ylabel='Delay', subplot=True, normalize=normalize)

    if legend == 'delay':
        plt.legend(numpoints=1, shadow=True, labelspacing=0,
                   fontsize=24, loc='best')

    plt.autoscale(enable=True)
    plt.tight_layout()
    plt.subplots_adjust(wspace=0, hspace=0.2)
    plt.margins(y=0.1)
    if show:
        plt.show()
    return

def complexity_plot(results, plot_settings, xdata, xlabel='', normalize=None, phase='reduce'):
    '''Plot the encoding or decoding delay.

    Args:

    results: SimulatorResult to plot.

    plot_settings: List of dicts with plot settings.

    xdata: Label of the X axis data ('partitions' or 'servers').

    xlabel: X axis label

    normalize: If a SimulatorResult is provided, all ploted results
    are normalized by this one.

    phase: Phase to plot the delay of (encode or reduce)

    '''
    assert isinstance(results, list)
    assert isinstance(plot_settings, list)
    # assert isinstance(normalize, SimulatorResult) or normalize is None
    assert phase == 'encode' or phase == 'reduce'

    # Create plot window
    _ = plt.figure(figsize=(8,5))

    plt.rc('pgf',  texsystem='pdflatex')
    plt.rc('text', usetex=True)
    # plt.rc('font', family='serif')
    plt.rcParams['text.latex.preamble'] = [r'\usepackage{lmodern}']

    # Plot complexity
    fig, ax = plt.subplots(figsize=(6,6))
    plt.setp(ax.get_xticklabels(), fontsize=25)
    plt.setp(ax.get_yticklabels(), fontsize=25)
    for result, plot_setting in zip(results, plot_settings):
        plot_result(result, plot_setting, xdata, phase, xlabel=xlabel,
                    ylabel=r'$D_{\mathsf{' + phase + r'}}$', subplot=True, normalize=normalize,
                    plot_type='loglog')

    plt.legend(numpoints=1, shadow=True, labelspacing=0,
               fontsize=24, loc='best')
    plt.autoscale(enable=True)
    plt.tight_layout()
    plt.subplots_adjust(wspace=0, hspace=0.2)
    plt.show()
    return

def plot_result(result, plot_settings, xdata, ydata, xlabel='',
                ylabel='', subplot=False, normalize=None,
                errorbars=False, plot_type='semilogx'):
    '''Plot simulated results.

    Args:

    result: A SimulatorResult.

    plot_settings: A dict with plot settings.

    xdata: Label of the X axis data ('partitions' or 'servers').

    ydata: Label of the Y axis data ('load' or 'delay').

    xlabel: X axis label.

    ylabel: Y axis label.

    subplot: Set to True if the plot q is intended to be a subplot.
    This will keep it from creating a new plot window, creating a
    legend, and automatically showing the plot.

    normalize: Normalize the plotted data by that of these results.
    Must be a list of SimulationResults of length equal to results.

    errorbars: Plot error bars.

    '''
    # assert isinstance(result, SimulatorResult)
    assert isinstance(plot_settings, dict)
    # assert xdata == 'partitions' or xdata == 'servers'
    assert ydata == 'load' or ydata == 'delay' or ydata == 'reduce' or ydata == 'encode'
    assert isinstance(xlabel, str)
    assert isinstance(ylabel, str)
    assert isinstance(subplot, bool)
    # assert isinstance(normalize, SimulatorResult) or normalize is None

    if not subplot:
        _ = plt.figure()

    plt.grid(True, which='both')
    plt.ylabel(ylabel, fontsize=28)
    plt.xlabel(xlabel, fontsize=28)
    plt.autoscale()

    label = plot_settings['label']
    color = plot_settings['color']
    style = color + plot_settings['marker']
    linewidth = plot_settings['linewidth']
    size = plot_settings['size']

    xarray = result[xdata]
    if np.size(result[ydata], 0) == 3:
        ymean = result[ydata][0, :].copy()
        ymin = result[ydata][1, :].copy()
        ymax = result[ydata][2, :].copy()
    else:
        ymean = result[ydata].copy()
        ymin = ymean.copy()
        ymax = ymean.copy()
    yerr = np.zeros([2, len(ymean)])
    yerr[0, :] = ymean - ymin
    yerr[1, :] = ymax - ymean
    if normalize is not None:
        ymean /= normalize[ydata][0, :]
        yerr[0, :] /= normalize[ydata][0, :]
        yerr[1, :] /= normalize[ydata][0, :]

    if plot_type == 'semilogx':
        plt.semilogx(xarray, ymean, style, label=label,
                     linewidth=linewidth, markersize=size)
    elif plot_type == 'loglog':
        plt.loglog(xarray, ymean, style, label=label,
                   linewidth=linewidth, markersize=size)

    if errorbars:
        plt.errorbar(xarray, ymean, yerr=yerr, fmt='none', ecolor=color)

    if not subplot:
        plt.legend(numpoints=1, fontsize=25, loc='best', prop={'weight': 'bold'})
        plt.show()

    return
<commit_msg>encoding/decoding delay plot, plot parameter updates, cleanup<commit_after>'''Plotting tools

'''

import model
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

from simulation import SimulatorResult

def get_parameters_size():
    '''Get a list of parameters for the size plot.'''
    rows_per_server = 2000
    rows_per_partition = 10
    code_rate = 2/3
    muq = 2
    num_columns = int(1e4)
    parameters = list()
    num_servers = [5, 8, 20, 50, 80, 125, 200, 500, 2000]
    for servers in num_servers:
        par = model.SystemParameters.fixed_complexity_parameters(
            rows_per_server=rows_per_server,
            rows_per_partition=rows_per_partition,
            min_num_servers=servers,
            code_rate=code_rate,
            muq=muq,
            num_columns=num_columns
        )
        parameters.append(par)
    return parameters

def get_parameters_size_2():
    '''Get a list of parameters for the size plot.'''
    rows_per_server = 200
    rows_per_partition = 10
    code_rate = 2/3
    muq = 2
    num_columns = None
    num_outputs_factor=100
    parameters = list()
    num_servers = [2, 5, 8, 20, 50]#, 200]
    for servers in num_servers:
        par = model.SystemParameters.fixed_complexity_parameters(
            rows_per_server=rows_per_server,
            rows_per_partition=rows_per_partition,
            min_num_servers=servers,
            code_rate=code_rate,
            muq=muq,
            num_columns=num_columns,
            num_outputs_factor=num_outputs_factor
        )
        parameters.append(par)
    return parameters

def get_parameters_N():
    '''Get a list of parameters for the N to n ratio plot.'''
    rows_per_batch = 100
    num_servers = 9
    q = 6
    num_outputs = q
    server_storage = 1/3
    num_partitions = 240
    num_outputs = q
    # num_columns = 20000
    parameters = list()
    for i in range(1, 11):
        # num_outputs = i * q
        num_columns = i * 4
        par = model.SystemParameters(
            rows_per_batch=rows_per_batch,
            num_servers=num_servers,
            q=q,
            num_outputs=num_outputs,
            server_storage=server_storage,
            num_partitions=num_partitions,
            num_columns=num_columns,
        )
        parameters.append(par)

    return parameters

def get_parameters_partitioning():
    '''Get a list of parameters for the partitioning plot.'''
    rows_per_batch = 250
    num_servers = 9
    q = 6
    num_outputs = q
    server_storage = 1/3
    num_partitions = [2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 24, 25, 30,
                      40, 50, 60, 75, 100, 120, 125, 150, 200, 250,
                      300, 375, 500, 600, 750, 1000, 1500, 3000]

    parameters = list()
    for partitions in num_partitions:
        par = model.SystemParameters(
            rows_per_batch=rows_per_batch,
            num_servers=num_servers,
            q=q,
            num_outputs=num_outputs,
            server_storage=server_storage,
            num_partitions=partitions,
        )
        parameters.append(par)

    return parameters

def get_parameters_partitioning_2():
    '''Constant system size, increasing partitions, num_outputs=num_columns'''
    rows_per_batch = 250
    num_servers = 9
    q = 6
    num_outputs = 6000
    server_storage = 1/3
    num_partitions = [2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 24, 25, 30,
                      40, 50, 60, 75, 100, 120, 125, 150, 200, 250,
                      300, 375, 500, 600, 750, 1000, 1500, 3000]

    parameters = list()
    for partitions in num_partitions:
        par = model.SystemParameters(
            rows_per_batch=rows_per_batch,
            num_servers=num_servers,
            q=q,
            num_outputs=num_outputs,
            server_storage=server_storage,
            num_partitions=partitions,
        )
        parameters.append(par)

    return parameters

def load_delay_plot(results, plot_settings, xdata, xlabel='',
                    normalize=None, legend='load', show=True):
    '''Create a plot with two subplots for load and delay respectively.

    Args:

    results: SimulatorResult to plot.

    plot_settings: List of dicts with plot settings.

    xdata: Label of the X axis data ('partitions' or 'servers').

    xlabel: X axis label

    normalize: If a SimulatorResult is provided, all ploted results
    are normalized by this one.

    legend: Place the legend in the load or delay plot by setting this
    argument to 'load' or 'delay'.

    show: show the plots if True.

    '''
    assert isinstance(results, list)
    assert isinstance(plot_settings, list)
    # assert isinstance(normalize, SimulatorResult) or normalize is None
    assert isinstance(show, bool)

    plt.rc('pgf',  texsystem='pdflatex')
    plt.rc('text', usetex=True)
    # plt.rc('font', family='serif')
    plt.rcParams['text.latex.preamble'] = [r'\usepackage{lmodern}']
    _ = plt.figure(figsize=(10,6))

    # Plot load
    plt.autoscale(enable=True)
    plt.tight_layout()
    ax1 = plt.subplot(211)
    plt.setp(ax1.get_xticklabels(), fontsize=25, visible=False)
    plt.setp(ax1.get_yticklabels(), fontsize=25)
    for result, plot_setting in zip(results, plot_settings):
        plot_result(result, plot_setting, xdata, 'load',
                    ylabel='Load', subplot=True, normalize=normalize)

    plt.margins(y=0.1)
    if legend == 'load':
        plt.legend(
            numpoints=1,
            shadow=True,
            labelspacing=0,
            fontsize=24,
            loc='best',
            fancybox=False,
            borderaxespad=0.1,
        )

    # Plot delay
    ax2 = plt.subplot(212, sharex=ax1)
    plt.setp(ax2.get_xticklabels(), fontsize=25)
    plt.setp(ax2.get_yticklabels(), fontsize=25)
    for result, plot_setting in zip(results, plot_settings):
        plot_result(result, plot_setting, xdata, 'delay', xlabel=xlabel,
                    ylabel='Delay', subplot=True, normalize=normalize)

    if legend == 'delay':
        plt.legend(numpoints=1, shadow=True, labelspacing=0,
                   fontsize=24, loc='best')

    plt.autoscale(enable=True)
    plt.tight_layout()
    plt.subplots_adjust(wspace=0, hspace=0.2)
    plt.margins(y=0.1)
    if show:
        plt.show()
    return

def encode_decode_plot(results, plot_settings, xdata, xlabel='',
                       normalize=None, legend='load', show=True):
    '''Create a plot with two subplots for encoding and decoding delay respectively.

    args:

    results: SimulatorResult to plot.

    plot_settings: List of dicts with plot settings.

    xdata: Label of the X axis data ('partitions' or 'servers').

    xlabel: X axis label

    normalize: If a SimulatorResult is provided, all ploted results
    are normalized by this one.

    legend: Place the legend in the load or delay plot by setting this
    argument to 'load' or 'delay'.

    show: show the plots if True.

    '''
    assert isinstance(results, list)
    assert isinstance(plot_settings, list)
    assert isinstance(show, bool)

    plt.rc('pgf',  texsystem='pdflatex')
    plt.rc('text', usetex=True)
    # plt.rc('font', family='serif')
    plt.rcParams['text.latex.preamble'] = [r'\usepackage{lmodern}']
    _ = plt.figure(figsize=(10,6))

    # Plot load
    plt.autoscale(enable=True)
    plt.tight_layout()
    ax1 = plt.subplot(211)
    plt.setp(ax1.get_xticklabels(), fontsize=25, visible=False)
    plt.setp(ax1.get_yticklabels(), fontsize=25)
    for result, plot_setting in zip(results, plot_settings):
        plot_result(
            result,
            plot_setting,
            xdata,
            'encode',
            ylabel='Encoding',
            subplot=True,
            normalize=normalize
        )

    plt.margins(y=0.1)
    if legend == 'encode':
        plt.legend(
            numpoints=1,
            shadow=True,
            labelspacing=0,
            fontsize=24,
            loc='best',
            fancybox=False,
            borderaxespad=0.1,
        )

    # Plot delay
    ax2 = plt.subplot(212, sharex=ax1)
    plt.setp(ax2.get_xticklabels(), fontsize=25)
    plt.setp(ax2.get_yticklabels(), fontsize=25)
    for result, plot_setting in zip(results, plot_settings):
        plot_result(
            result,
            plot_setting,
            xdata,
            'reduce',
            xlabel=xlabel,
            ylabel='Decode',
            subplot=True,
            normalize=normalize
        )

    if legend == 'decode':
        plt.legend(
            numpoints=1,
            shadow=True,
            labelspacing=0,
            fontsize=24,
            loc='best',
            fancybox=False,
            borderaxespad=0.1,
        )

    plt.autoscale(enable=True)
    plt.tight_layout()
    plt.subplots_adjust(wspace=0, hspace=0.2)
    plt.margins(y=0.1)
    if show:
        plt.show()
    return

# def complexity_plot(results, plot_settings, xdata, xlabel='',
#                     normalize=None, phase='reduce'):
#     '''Plot the encoding or decoding delay.

#     Args:

#     results: SimulatorResult to plot.

#     plot_settings: List of dicts with plot settings.

#     xdata: Label of the X axis data ('partitions' or 'servers').

#     xlabel: X axis label

#     normalize: If a SimulatorResult is provided, all ploted results
#     are normalized by this one.

#     phase: Phase to plot the delay of (encode or reduce)

#     '''
#     assert isinstance(results, list)
#     assert isinstance(plot_settings, list)
#     # assert isinstance(normalize, SimulatorResult) or normalize is None
#     assert phase == 'encode' or phase == 'reduce'

#     # Create plot window
#     _ = plt.figure(figsize=(8,5))

#     plt.rc('pgf',  texsystem='pdflatex')
#     plt.rc('text', usetex=True)
#     # plt.rc('font', family='serif')
#     plt.rcParams['text.latex.preamble'] = [r'\usepackage{lmodern}']

#     # Plot complexity
#     fig, ax = plt.subplots(figsize=(6,6))
#     plt.setp(ax.get_xticklabels(), fontsize=25)
#     plt.setp(ax.get_yticklabels(), fontsize=25)
#     for result, plot_setting in zip(results, plot_settings):
#         plot_result(result, plot_setting, xdata, phase, xlabel=xlabel,
#                     ylabel=r'$D_{\mathsf{' + phase + r'}}$', subplot=True, normalize=normalize,
#                     plot_type='loglog')

#     plt.legend(numpoints=1, shadow=True, labelspacing=0,
#                fontsize=24, loc='best')
#     plt.autoscale(enable=True)
#     plt.tight_layout()
#     plt.subplots_adjust(wspace=0, hspace=0.2)
#     plt.show()
#     return

def plot_result(result, plot_settings, xdata, ydata, xlabel='',
                ylabel='', subplot=False, normalize=None,
                errorbars=False, plot_type='semilogx'):
    '''Plot simulated results.

    Args:

    result: A SimulatorResult.

    plot_settings: A dict with plot settings.

    xdata: Label of the X axis data ('partitions' or 'servers').

    ydata: Label of the Y axis data ('load' or 'delay').

    xlabel: X axis label.

    ylabel: Y axis label.

    subplot: Set to True if the plot q is intended to be a subplot.
    This will keep it from creating a new plot window, creating a
    legend, and automatically showing the plot.

    normalize: Normalize the plotted data by that of these results.
    Must be a list of SimulationResults of length equal to results.

    errorbars: Plot error bars.

    '''
    # assert isinstance(result, SimulatorResult)
    assert isinstance(plot_settings, dict)
    # assert xdata == 'partitions' or xdata == 'servers'
    assert ydata == 'load' or ydata == 'delay' or ydata == 'reduce' or ydata == 'encode'
    assert isinstance(xlabel, str)
    assert isinstance(ylabel, str)
    assert isinstance(subplot, bool)
    # assert isinstance(normalize, SimulatorResult) or normalize is None

    if not subplot:
        _ = plt.figure()

    plt.grid(True, which='both')
    plt.ylabel(ylabel, fontsize=28)
    plt.xlabel(xlabel, fontsize=28)
    plt.autoscale()

    label = plot_settings['label']
    color = plot_settings['color']
    style = color + plot_settings['marker']
    linewidth = plot_settings['linewidth']
    size = plot_settings['size']

    xarray = result[xdata]
    if np.size(result[ydata], 0) == 3:
        ymean = result[ydata][0, :].copy()
        ymin = result[ydata][1, :].copy()
        ymax = result[ydata][2, :].copy()
    else:
        ymean = result[ydata].copy()
        ymin = ymean.copy()
        ymax = ymean.copy()
    yerr = np.zeros([2, len(ymean)])
    yerr[0, :] = ymean - ymin
    yerr[1, :] = ymax - ymean
    if normalize is not None:
        ymean /= normalize[ydata][0, :]
        yerr[0, :] /= normalize[ydata][0, :]
        yerr[1, :] /= normalize[ydata][0, :]

    if plot_type == 'semilogx':
        plt.semilogx(xarray, ymean, style, label=label,
                     linewidth=linewidth, markersize=size)
    elif plot_type == 'loglog':
        plt.loglog(xarray, ymean, style, label=label,
                   linewidth=linewidth, markersize=size)

    if errorbars:
        plt.errorbar(xarray, ymean, yerr=yerr, fmt='none', ecolor=color)

    if not subplot:
        plt.legend(numpoints=1, fontsize=25, loc='best', prop={'weight': 'bold'})
        plt.show()

    return
<|endoftext|>


<commit_before>'''Plotting tools

'''

import model
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

from simulation import SimulatorResult

def get_parameters_size():
    '''Get a list of parameters for the size plot.'''
    rows_per_server = 2000
    rows_per_partition = 10
    code_rate = 2/3
    muq = 2
    num_columns = int(1e4)
    parameters = list()
    num_servers = [5, 8, 20, 50, 80, 125, 200, 500, 2000]
    for servers in num_servers:
        par = model.SystemParameters.fixed_complexity_parameters(
            rows_per_server=rows_per_server,
            rows_per_partition=rows_per_partition,
            min_num_servers=servers,
            code_rate=code_rate,
            muq=muq,
            num_columns=num_columns
        )
        parameters.append(par)
    return parameters

def get_parameters_size_2():
    '''Get a list of parameters for the size plot.'''
    rows_per_server = 200
    rows_per_partition = 10
    code_rate = 2/3
    muq = 2
    num_columns = None
    num_outputs_factor=100
    parameters = list()
    num_servers = [2, 5, 8, 20, 50]#, 200]
    for servers in num_servers:
        par = model.SystemParameters.fixed_complexity_parameters(
            rows_per_server=rows_per_server,
            rows_per_partition=rows_per_partition,
            min_num_servers=servers,
            code_rate=code_rate,
            muq=muq,
            num_columns=num_columns,
            num_outputs_factor=num_outputs_factor
        )
        parameters.append(par)
    return parameters

def get_parameters_partitioning():
    '''Get a list of parameters for the partitioning plot.'''
    rows_per_batch = 250
    num_servers = 9
    q = 6
    num_outputs = q
    server_storage = 1/3
    num_partitions = [2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 24, 25, 30,
                      40, 50, 60, 75, 100, 120, 125, 150, 200, 250,
                      300, 375, 500, 600, 750, 1000, 1500, 3000]

    parameters = list()
    for partitions in num_partitions:
        par = model.SystemParameters(rows_per_batch=rows_per_batch, num_servers=num_servers, q=q,
                                     num_outputs=num_outputs, server_storage=server_storage,
                                     num_partitions=partitions)
        parameters.append(par)

    return parameters

def load_delay_plot(results, plot_settings, xdata, xlabel='', normalize=None, legend='load', show=True):
    '''Create a plot with two subplots for load and delay respectively.

    Args:

    results: SimulatorResult to plot.

    plot_settings: List of dicts with plot settings.

    xdata: Label of the X axis data ('partitions' or 'servers').

    xlabel: X axis label

    normalize: If a SimulatorResult is provided, all ploted results
    are normalized by this one.

    legend: Place the legend in the load or delay plot by setting this
    argument to 'load' or 'delay'.

    show: show the plots if True.

    '''
    assert isinstance(results, list)
    assert isinstance(plot_settings, list)
    assert isinstance(normalize, SimulatorResult) or normalize is None
    assert isinstance(show, bool)

    plt.rc('pgf',  texsystem='pdflatex')
    plt.rc('text', usetex=True)
    plt.rc('font', family='serif')
    _ = plt.figure(figsize=(10,6))

    # Plot load
    ax1 = plt.subplot(211)
    plt.setp(ax1.get_xticklabels(), fontsize=25, visible=False)
    plt.setp(ax1.get_yticklabels(), fontsize=25)
    for result, plot_setting in zip(results, plot_settings):
        plot_result(result, plot_setting, xdata, 'load',
                    ylabel='$\mathsf{Load}$', subplot=True, normalize=normalize)

    plt.margins(y=0.1)
    if legend == 'load':
        plt.legend(
            numpoints=1,
            shadow=True,
            labelspacing=0,
            fontsize=24,
            loc='best',
            fancybox=False,
            borderaxespad=0.1,
        )

    # Plot delay
    ax2 = plt.subplot(212, sharex=ax1)
    plt.setp(ax2.get_xticklabels(), fontsize=25)
    plt.setp(ax2.get_yticklabels(), fontsize=25)
    for result, plot_setting in zip(results, plot_settings):
        plot_result(result, plot_setting, xdata, 'delay', xlabel=xlabel,
                    ylabel='$\mathsf{Delay}$', subplot=True, normalize=normalize)

    if legend == 'delay':
        plt.legend(numpoints=1, shadow=True, labelspacing=0,
                   fontsize=24, loc='best')

    plt.autoscale(enable=True)
    plt.tight_layout()
    plt.subplots_adjust(wspace=0, hspace=0.2)
    plt.margins(y=0.1)
    if show:
        plt.show()
    return

def complexity_plot(results, plot_settings, xdata, xlabel='', normalize=None, phase='reduce'):
    '''Plot the encoding or decoding delay.

    Args:

    results: SimulatorResult to plot.

    plot_settings: List of dicts with plot settings.

    xdata: Label of the X axis data ('partitions' or 'servers').

    xlabel: X axis label

    normalize: If a SimulatorResult is provided, all ploted results
    are normalized by this one.

    phase: Phase to plot the delay of (encode or reduce)

    '''
    assert isinstance(results, list)
    assert isinstance(plot_settings, list)
    assert isinstance(normalize, SimulatorResult) or normalize is None
    assert phase == 'encode' or phase == 'reduce'

    # Create plot window
    _ = plt.figure(figsize=(8,5))

    plt.rc('pgf',  texsystem='pdflatex')
    plt.rc('text', usetex=True)
    plt.rc('font', family='serif')

    # Plot complexity
    fig, ax = plt.subplots(figsize=(6,6))
    plt.setp(ax.get_xticklabels(), fontsize=25)
    plt.setp(ax.get_yticklabels(), fontsize=25)
    for result, plot_setting in zip(results, plot_settings):
        plot_result(result, plot_setting, xdata, phase, xlabel=xlabel,
                    ylabel=r'$D_{\mathsf{' + phase + r'}}$', subplot=True, normalize=normalize,
                    plot_type='loglog')

    plt.legend(numpoints=1, shadow=True, labelspacing=0,
               fontsize=24, loc='best')
    plt.autoscale(enable=True)
    plt.tight_layout()
    plt.subplots_adjust(wspace=0, hspace=0.2)
    plt.show()
    return

def plot_result(result, plot_settings, xdata, ydata, xlabel='',
                ylabel='', subplot=False, normalize=None,
                errorbars=False, plot_type='semilogx'):
    '''Plot simulated results.

    Args:

    result: A SimulatorResult.

    plot_settings: A dict with plot settings.

    xdata: Label of the X axis data ('partitions' or 'servers').

    ydata: Label of the Y axis data ('load' or 'delay').

    xlabel: X axis label.

    ylabel: Y axis label.

    subplot: Set to True if the plot q is intended to be a subplot.
    This will keep it from creating a new plot window, creating a
    legend, and automatically showing the plot.

    normalize: Normalize the plotted data by that of these results.
    Must be a list of SimulationResults of length equal to results.

    errorbars: Plot error bars.

    '''
    assert isinstance(result, SimulatorResult)
    assert isinstance(plot_settings, dict)
    assert xdata == 'partitions' or xdata == 'servers'
    assert ydata == 'load' or ydata == 'delay' or ydata == 'reduce' or ydata == 'encode'
    assert isinstance(xlabel, str)
    assert isinstance(ylabel, str)
    assert isinstance(subplot, bool)
    assert isinstance(normalize, SimulatorResult) or normalize is None

    if not subplot:
        _ = plt.figure()

    plt.grid(True, which='both')
    plt.ylabel(ylabel, fontsize=28)
    plt.xlabel(xlabel, fontsize=28)
    plt.autoscale()

    label = plot_settings['label']
    color = plot_settings['color']
    style = color + plot_settings['marker']
    linewidth = plot_settings['linewidth']
    size = plot_settings['size']

    xarray = result[xdata]
    ymean = result[ydata][0, :]
    ymin = result[ydata][1, :]
    ymax = result[ydata][2, :]
    yerr = np.zeros([2, len(ymean)])
    yerr[0, :] = ymean - ymin
    yerr[1, :] = ymax - ymean
    if normalize is not None:
        ymean /= normalize[ydata][0, :]
        yerr[0, :] /= normalize[ydata][0, :]
        yerr[1, :] /= normalize[ydata][0, :]

    if plot_type == 'semilogx':
        plt.semilogx(xarray, ymean, style, label=label,
                     linewidth=linewidth, markersize=size)
    elif plot_type == 'loglog':
        plt.loglog(xarray, ymean, style, label=label,
                   linewidth=linewidth, markersize=size)

    if errorbars:
        plt.errorbar(xarray, ymean, yerr=yerr, fmt='none', ecolor=color)

    if not subplot:
        plt.legend(numpoints=1, fontsize=25, loc='best')
        plt.show()

    return
<commit_msg>fonts, cleanup, new parameters<commit_after>'''Plotting tools

'''

import model
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

from simulation import SimulatorResult

def get_parameters_size():
    '''Get a list of parameters for the size plot.'''
    rows_per_server = 2000
    rows_per_partition = 10
    code_rate = 2/3
    muq = 2
    num_columns = int(1e4)
    parameters = list()
    num_servers = [5, 8, 20, 50, 80, 125, 200, 500, 2000]
    for servers in num_servers:
        par = model.SystemParameters.fixed_complexity_parameters(
            rows_per_server=rows_per_server,
            rows_per_partition=rows_per_partition,
            min_num_servers=servers,
            code_rate=code_rate,
            muq=muq,
            num_columns=num_columns
        )
        parameters.append(par)
    return parameters

def get_parameters_size_2():
    '''Get a list of parameters for the size plot.'''
    rows_per_server = 200
    rows_per_partition = 10
    code_rate = 2/3
    muq = 2
    num_columns = None
    num_outputs_factor=100
    parameters = list()
    num_servers = [2, 5, 8, 20, 50]#, 200]
    for servers in num_servers:
        par = model.SystemParameters.fixed_complexity_parameters(
            rows_per_server=rows_per_server,
            rows_per_partition=rows_per_partition,
            min_num_servers=servers,
            code_rate=code_rate,
            muq=muq,
            num_columns=num_columns,
            num_outputs_factor=num_outputs_factor
        )
        parameters.append(par)
    return parameters

def get_parameters_partitioning():
    '''Get a list of parameters for the partitioning plot.'''
    rows_per_batch = 250
    num_servers = 9
    q = 6
    num_outputs = q
    server_storage = 1/3
    num_partitions = [2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 24, 25, 30,
                      40, 50, 60, 75, 100, 120, 125, 150, 200, 250,
                      300, 375, 500, 600, 750, 1000, 1500, 3000]

    parameters = list()
    for partitions in num_partitions:
        par = model.SystemParameters(rows_per_batch=rows_per_batch, num_servers=num_servers, q=q,
                                     num_outputs=num_outputs, server_storage=server_storage,
                                     num_partitions=partitions)
        parameters.append(par)

    return parameters

def get_parameters_partitioning_2():
    '''Constant system size, increasing partitions, num_outputs=num_columns'''
    rows_per_batch = 250
    num_servers = 9
    q = 6
    num_outputs = 6000
    server_storage = 1/3
    num_partitions = [2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 24, 25, 30,
                      40, 50, 60, 75, 100, 120, 125, 150, 200, 250,
                      300, 375, 500, 600, 750, 1000, 1500, 3000]

    parameters = list()
    for partitions in num_partitions:
        par = model.SystemParameters(rows_per_batch=rows_per_batch, num_servers=num_servers, q=q,
                                     num_outputs=num_outputs, server_storage=server_storage,
                                     num_partitions=partitions)
        parameters.append(par)

    return parameters

def load_delay_plot(results, plot_settings, xdata, xlabel='', normalize=None, legend='load', show=True):
    '''Create a plot with two subplots for load and delay respectively.

    Args:

    results: SimulatorResult to plot.

    plot_settings: List of dicts with plot settings.

    xdata: Label of the X axis data ('partitions' or 'servers').

    xlabel: X axis label

    normalize: If a SimulatorResult is provided, all ploted results
    are normalized by this one.

    legend: Place the legend in the load or delay plot by setting this
    argument to 'load' or 'delay'.

    show: show the plots if True.

    '''
    assert isinstance(results, list)
    assert isinstance(plot_settings, list)
    # assert isinstance(normalize, SimulatorResult) or normalize is None
    assert isinstance(show, bool)

    plt.rc('pgf',  texsystem='pdflatex')
    plt.rc('text', usetex=True)
    # plt.rc('font', family='serif')
    plt.rcParams['text.latex.preamble'] = [r'\usepackage{lmodern}']
    _ = plt.figure(figsize=(10,6))

    # Plot load
    plt.autoscale(enable=True)
    plt.tight_layout()
    ax1 = plt.subplot(211)
    plt.setp(ax1.get_xticklabels(), fontsize=25, visible=False)
    plt.setp(ax1.get_yticklabels(), fontsize=25)
    for result, plot_setting in zip(results, plot_settings):
        plot_result(result, plot_setting, xdata, 'load',
                    ylabel='Load', subplot=True, normalize=normalize)

    plt.margins(y=0.1)
    if legend == 'load':
        plt.legend(
            numpoints=1,
            shadow=True,
            labelspacing=0,
            fontsize=24,
            loc='best',
            fancybox=False,
            borderaxespad=0.1,
        )

    # Plot delay
    ax2 = plt.subplot(212, sharex=ax1)
    plt.setp(ax2.get_xticklabels(), fontsize=25)
    plt.setp(ax2.get_yticklabels(), fontsize=25)
    for result, plot_setting in zip(results, plot_settings):
        plot_result(result, plot_setting, xdata, 'delay', xlabel=xlabel,
                    ylabel='Delay', subplot=True, normalize=normalize)

    if legend == 'delay':
        plt.legend(numpoints=1, shadow=True, labelspacing=0,
                   fontsize=24, loc='best')

    plt.autoscale(enable=True)
    plt.tight_layout()
    plt.subplots_adjust(wspace=0, hspace=0.2)
    plt.margins(y=0.1)
    if show:
        plt.show()
    return

def complexity_plot(results, plot_settings, xdata, xlabel='', normalize=None, phase='reduce'):
    '''Plot the encoding or decoding delay.

    Args:

    results: SimulatorResult to plot.

    plot_settings: List of dicts with plot settings.

    xdata: Label of the X axis data ('partitions' or 'servers').

    xlabel: X axis label

    normalize: If a SimulatorResult is provided, all ploted results
    are normalized by this one.

    phase: Phase to plot the delay of (encode or reduce)

    '''
    assert isinstance(results, list)
    assert isinstance(plot_settings, list)
    # assert isinstance(normalize, SimulatorResult) or normalize is None
    assert phase == 'encode' or phase == 'reduce'

    # Create plot window
    _ = plt.figure(figsize=(8,5))

    plt.rc('pgf',  texsystem='pdflatex')
    plt.rc('text', usetex=True)
    # plt.rc('font', family='serif')
    plt.rcParams['text.latex.preamble'] = [r'\usepackage{lmodern}']

    # Plot complexity
    fig, ax = plt.subplots(figsize=(6,6))
    plt.setp(ax.get_xticklabels(), fontsize=25)
    plt.setp(ax.get_yticklabels(), fontsize=25)
    for result, plot_setting in zip(results, plot_settings):
        plot_result(result, plot_setting, xdata, phase, xlabel=xlabel,
                    ylabel=r'$D_{\mathsf{' + phase + r'}}$', subplot=True, normalize=normalize,
                    plot_type='loglog')

    plt.legend(numpoints=1, shadow=True, labelspacing=0,
               fontsize=24, loc='best')
    plt.autoscale(enable=True)
    plt.tight_layout()
    plt.subplots_adjust(wspace=0, hspace=0.2)
    plt.show()
    return

def plot_result(result, plot_settings, xdata, ydata, xlabel='',
                ylabel='', subplot=False, normalize=None,
                errorbars=False, plot_type='semilogx'):
    '''Plot simulated results.

    Args:

    result: A SimulatorResult.

    plot_settings: A dict with plot settings.

    xdata: Label of the X axis data ('partitions' or 'servers').

    ydata: Label of the Y axis data ('load' or 'delay').

    xlabel: X axis label.

    ylabel: Y axis label.

    subplot: Set to True if the plot q is intended to be a subplot.
    This will keep it from creating a new plot window, creating a
    legend, and automatically showing the plot.

    normalize: Normalize the plotted data by that of these results.
    Must be a list of SimulationResults of length equal to results.

    errorbars: Plot error bars.

    '''
    # assert isinstance(result, SimulatorResult)
    assert isinstance(plot_settings, dict)
    assert xdata == 'partitions' or xdata == 'servers'
    assert ydata == 'load' or ydata == 'delay' or ydata == 'reduce' or ydata == 'encode'
    assert isinstance(xlabel, str)
    assert isinstance(ylabel, str)
    assert isinstance(subplot, bool)
    # assert isinstance(normalize, SimulatorResult) or normalize is None

    if not subplot:
        _ = plt.figure()

    plt.grid(True, which='both')
    plt.ylabel(ylabel, fontsize=28)
    plt.xlabel(xlabel, fontsize=28)
    plt.autoscale()

    label = plot_settings['label']
    color = plot_settings['color']
    style = color + plot_settings['marker']
    linewidth = plot_settings['linewidth']
    size = plot_settings['size']

    xarray = result[xdata]
    if np.size(result[ydata], 0) == 3:
        ymean = result[ydata][0, :]
        ymin = result[ydata][1, :]
        ymax = result[ydata][2, :]
    else:
        ymean = result[ydata]
        ymin = ymean
        ymax = ymean
    yerr = np.zeros([2, len(ymean)])
    yerr[0, :] = ymean - ymin
    yerr[1, :] = ymax - ymean
    if normalize is not None:
        ymean /= normalize[ydata][0, :]
        yerr[0, :] /= normalize[ydata][0, :]
        yerr[1, :] /= normalize[ydata][0, :]

    if plot_type == 'semilogx':
        plt.semilogx(xarray, ymean, style, label=label,
                     linewidth=linewidth, markersize=size)
    elif plot_type == 'loglog':
        plt.loglog(xarray, ymean, style, label=label,
                   linewidth=linewidth, markersize=size)

    if errorbars:
        plt.errorbar(xarray, ymean, yerr=yerr, fmt='none', ecolor=color)

    if not subplot:
        plt.legend(numpoints=1, fontsize=25, loc='best', prop={'weight': 'bold'})
        plt.show()

    return
<|endoftext|>


<commit_before># Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2011 Isaku Yamahata <yamahata at valinux co jp>
#
# 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, version 3 of the License.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
import copy
import logging
import weakref

from gevent.queue import Queue

LOG = logging.getLogger('ryu.controller.dispatcher')

class EventQueue(object):
    # WeakSet: This set is populated by __init__().
    #          So need to use weak reference in order to make instances
    #          freeable by avoiding refrence count.
    #          Otherwise, instances can't be freed.
    event_queues = weakref.WeakSet()

    def __init__(self, name, dispatcher):
        self.event_queues.add(self)

        self.name = name
        self.dispatcher = dispatcher
        self.is_dispatching = False
        self.ev_q = Queue()

    def set_dispatcher(self, dispatcher):
        self.dispatcher = dispatcher

    def queue_raw(self, ev):
        self.ev_q.put(ev)

    class _EventQueueGuard(object):
        def __init__(self, ev_q):
            self.ev_q = ev_q

        def __enter__(self):
            self.ev_q.is_dispatching = True

        def __exit__(self, type_, value, traceback):
            self.ev_q.is_dispatching = False
            return False

    def queue(self, ev):
        if self.is_dispatching:
            self.queue_raw(ev)
            return

        with self._EventQueueGuard(self):
            assert self.ev_q.empty()

            self.dispatcher(ev)
            while not self.ev_q.empty():
                ev = self.ev_q.get()
                self.dispatcher(ev)

class EventDispatcher(object):
    # WeakSet: This set is populated by __init__().
    #          So need to use weak reference in order to make instances
    #          freeable by avoiding refrence count.
    #          Otherwise, instances can't be freed.
    event_dispatchers = weakref.WeakSet()

    def __init__(self, name):
        self.event_dispatchers.add(self)

        self.name = name
        self.events = {}
        self.all_handlers = []

    def register_all_handler(self, all_handler):
        self.all_handlers.append(all_handler)

    def unregister_all_handler(self, all_handler):
        del self.all_handlers[all_handler]

    def register_handler(self, ev_cls, handler):
        assert callable(handler)
        self.events.setdefault(ev_cls, [])
        self.events[ev_cls].append(handler)

    def register_handlers(self, handlers):
        for ev_cls, h in handlers:
            self.register_handler(ev_cls, h)

    def unregister_handler(self, ev_cls, handler):
        del self.events[ev_cls][handler]

    def register_static(self, ev_cls):
        '''helper decorator to statically register handler for event class'''
        def register(handler):
            '''helper decorator to register handler statically '''
            if isinstance(handler, staticmethod):
                # class staticmethod is not callable.
                handler = handler.__func__
            self.register_handler(ev_cls, handler)
            return handler
        return register

    def __call__(self, ev):
        self.dispatch(ev)

    def dispatch(self, ev):
        #LOG.debug('dispatch %s', ev)

        # copy handler list because the list is not stable.
        # event handler may block/switch thread execution
        # and un/register other handlers. And more,
        # handler itself may un/register handlers.
        all_handlers = copy.copy(self.all_handlers)
        for h in all_handlers:
            ret = h(ev)
            if ret is False:
                break

        if ev.__class__ not in self.events:
            LOG.info('unhandled event %s', ev)
            return

        # Is this necessary?
        #
        # copy handler list because the list is not stable.
        # event handler may block/switch thread execution
        # and un/register other handlers.
        #
        handlers = copy.copy(self.events[ev.__class__])

        for h in handlers:
            ret = h(ev)
            if ret is False:
                break
<commit_msg>controller/dispatcher: introduce events on event queue itself<commit_after># Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2011, 2012 Isaku Yamahata <yamahata at valinux co jp>
#
# 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, version 3 of the License.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
import copy
import logging
import weakref

from gevent.queue import Queue

from . import event

LOG = logging.getLogger('ryu.controller.dispatcher')

class EventQueue(object):
    # WeakSet: This set is populated by __init__().
    #          So need to use weak reference in order to make instances
    #          freeable by avoiding refrence count.
    #          Otherwise, instances can't be freed.
    event_queues = weakref.WeakSet()

    # weakref: break circular reference
    #          self._ev_q_weakref == weakref.ref(self)
    _ev_q_weakref = None

    def set_ev_q(self):
        self.__class__._ev_q_weakref = weakref.ref(self)

    def _get_ev_q(self):
        ev_q = self._ev_q_weakref
        if ev_q is not None:
            ev_q = ev_q()
        return ev_q

    def _queue_q_ev(self, ev):
        ev_q = self._get_ev_q()
        if ev_q is not None:
            ev_q.queue(ev)

    def __init__(self, name, dispatcher, aux=None):
        self.name = name
        self.dispatcher = dispatcher.clone()
        self.is_dispatching = False
        self.ev_q = Queue()
        self.aux = aux  # for EventQueueCreate event

        self.event_queues.add(self)
        self._queue_q_ev(EventQueueCreate(self, True))

    def __del__(self):
        # This can be called when python interpreter exiting.
        # At that time, other object like EventQueueCreate can be
        # already destructed. So we can't call it blindly.
        ev_q = self._get_ev_q()
        if ev_q is not None and self != ev_q:
            self._queue_q_ev(EventQueueCreate(self, False))

    def set_dispatcher(self, dispatcher):
        old = self.dispatcher
        new = dispatcher.clone()
        self.dispatcher = new
        self._queue_q_ev(EventDispatcherChange(self, old, new))

    def queue_raw(self, ev):
        self.ev_q.put(ev)

    class _EventQueueGuard(object):
        def __init__(self, ev_q):
            self.ev_q = ev_q

        def __enter__(self):
            self.ev_q.is_dispatching = True

        def __exit__(self, type_, value, traceback):
            self.ev_q.is_dispatching = False
            return False

    def queue(self, ev):
        if self.is_dispatching:
            self.queue_raw(ev)
            return

        with self._EventQueueGuard(self):
            assert self.ev_q.empty()

            self.dispatcher(ev)
            while not self.ev_q.empty():
                ev = self.ev_q.get()
                self.dispatcher(ev)

class EventDispatcher(object):
    # WeakSet: This set is populated by __init__().
    #          So need to use weak reference in order to make instances
    #          freeable by avoiding refrence count.
    #          Otherwise, instances can't be freed.
    event_dispatchers = weakref.WeakSet()

    def __init__(self, name):
        # WeakSet: In order to let child to go away.
        #          We are interested only in alive children.
        self.children = weakref.WeakSet()
        self.name = name
        self.events = {}
        self.all_handlers = []

        self.event_dispatchers.add(self)

    def clone(self):
        cloned = EventDispatcher(self.name)
        for ev_cls, h in self.events.items():
            cloned.events[ev_cls] = copy.copy(h)
        cloned.all_handlers = copy.copy(self.all_handlers)
        self.children.add(cloned)
        return cloned

    def _foreach_children(self, call, *args, **kwargs):
        for c in self.children:
            call(c, *args, **kwargs)

    def register_all_handler(self, all_handler):
        self.all_handlers.append(all_handler)
        self._foreach_children(EventDispatcher.register_all_handler,
                               all_handler)

    def unregister_all_handler(self, all_handler):
        self._foreach_children(EventDispatcher.unregister_all_handler,
                               all_handler)
        del self.all_handlers[all_handler]

    def register_handler(self, ev_cls, handler):
        assert callable(handler)
        self.events.setdefault(ev_cls, [])
        self.events[ev_cls].append(handler)
        self._foreach_children(EventDispatcher.register_handler,
                               ev_cls, handler)

    def register_handlers(self, handlers):
        for ev_cls, h in handlers:
            self.register_handler(ev_cls, h)

    def unregister_handler(self, ev_cls, handler):
        self._foreach_children(EventDispatcher.unregister_handler,
                               ev_cls, handler)
        del self.events[ev_cls][handler]

    def register_static(self, ev_cls):
        '''helper decorator to statically register handler for event class'''
        def register(handler):
            '''helper decorator to register handler statically '''
            if isinstance(handler, staticmethod):
                # class staticmethod is not callable.
                handler = handler.__func__
            self.register_handler(ev_cls, handler)
            return handler
        return register

    def __call__(self, ev):
        self.dispatch(ev)

    def dispatch(self, ev):
        #LOG.debug('dispatch %s', ev)

        # copy handler list because the list is not stable.
        # event handler may block/switch thread execution
        # and un/register other handlers. And more,
        # handler itself may un/register handlers.
        all_handlers = copy.copy(self.all_handlers)
        for h in all_handlers:
            ret = h(ev)
            if ret is False:
                break

        if ev.__class__ not in self.events:
            LOG.info('unhandled event %s', ev)
            return

        # Is this necessary?
        #
        # copy handler list because the list is not stable.
        # event handler may block/switch thread execution
        # and un/register other handlers.
        #
        handlers = copy.copy(self.events[ev.__class__])

        for h in handlers:
            ret = h(ev)
            if ret is False:
                break

class EventQueueBase(event.EventBase):
    def __init__(self, ev_q):
        super(EventQueueBase, self).__init__()
        self.ev_q = ev_q
        self.aux = ev_q.aux

class EventQueueCreate(EventQueueBase):
    def __init__(self, ev_q, create):
        super(EventQueueCreate, self).__init__(ev_q)
        self.create = bool(create)    # True:  queue is created
                                      # False: queue is destroyed
        self.dispatcher = ev_q.dispatcher

class EventDispatcherChange(EventQueueBase):
    def __init__(self, ev_q, old_dispatcher, new_dispatcher):
        super(EventDispatcherChange, self).__init__(ev_q)
        self.old_dispatcher = old_dispatcher
        self.new_dispatcher = new_dispatcher

DISPATCHER_NAME_QUEUE_EV = 'queue_event'
QUEUE_EV_DISPATCHER = EventDispatcher(DISPATCHER_NAME_QUEUE_EV)
QUEUE_NAME_QEV_Q = 'queue_event'
QUEUE_EV_Q = EventQueue(QUEUE_NAME_QEV_Q, QUEUE_EV_DISPATCHER)
QUEUE_EV_Q.set_ev_q()
<|endoftext|>


<commit_before>from time import sleep
from string import ascii_letters
from random import choice, randint
from selenium.webdriver.support.ui import Select
from lettuce import step, world
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.remote.errorhandler import StaleElementReferenceException
from salad.steps.browser.finders import (PICK_EXPRESSION, ELEMENT_FINDERS,
                                         ELEMENT_THING_STRING,
                                         _get_visible_element)
from salad.tests.util import assert_equals_with_negate

# What's happening here? We're generating steps for every possible permuation of the element finder

world.random_strings = dict()

def _generate_content(type_of_fill, length):
    if type_of_fill == 'email':
        return _generate_random_string(length) + '@mailinator.com'
    elif type_of_fill == 'string':
        return _generate_random_string(length)
    elif type_of_fill == 'name':
        name = _generate_random_string(length)
        if length <= 3:
            return name
        index = randint(1, len(name)-2)
        return name[:index] + ' ' + name[index+1:]

def _generate_random_string(length):
    lst = [choice(ascii_letters) for n in xrange(length)]
    return "".join(lst)

for finder_string, finder_function in ELEMENT_FINDERS.iteritems():

    def _type_generator(finder_string, finder_function):
        @step(r'(slowly )?type "([^"]*)" into the%s %s %s' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, slowly, text, pick, find_pattern):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            if slowly and slowly != "":
                _type_slowly(ele, text)
            else:
                ele.value = text

        return _this_step

    globals()["form_type_%s" % (finder_function,)] = _type_generator(finder_string, finder_function)

    def _select_generator(finder_string, finder_function):
        @step(r'select the option (named|with the value)? "([^"]*)" (?:from|in) the%s %s %s' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, named_or_with_value, field_value, pick, find_pattern):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            if (named_or_with_value == "named"):
                # this does not work properly, it will click the first match
                # to field_value by default. it does not select the element we
                # are actually looking for.
                option = world.browser.find_option_by_text(field_value)
            else:
                option = ele.find_by_value(field_value)

            option.click()

        return _this_step

    globals()["form_select_%s" % (finder_function,)] = _select_generator(finder_string, finder_function)

    def _fill_generator(finder_string, finder_function):
        @step(r'fill in the%s %s %s with "([^"]*)"' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, pick, find_pattern, text):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            try:
                ele.value = text
            except:
                ele._control.value = text

        return _this_step

    globals()["form_fill_%s" % (finder_function,)] = _fill_generator(finder_string, finder_function)

    def _fill_with_stored_generator(finder_string, finder_function):
        @step(r'I fill in the%s %s %s with the stored value of "([^"]*)"' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, pick, find_pattern, name):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            assert(world.random_strings[name])
            try:
                ele.value = world.random_strings[name]
            except:
                ele._control.value = world.random_strings[name]

        return _this_step

    globals()["form_fill_with_stored_%s" % (finder_function,)] = _fill_with_stored_generator(finder_string, finder_function)

    def _remember_generator(finder_string, finder_function):
        @step(r'should see the stored value of "([^"]*)" in the%s %s %s' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, name, pick, find_pattern):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            random_value =  world.random_strings[name]
            if not (ele.value == random_value or
                    ele.text == random_value):
                raise AssertionError("random string %s not found in "
                                     "%s / %s" %
                                     (random_value,
                                      finder_string, finder_function))

        return _this_step

    globals()["form_remember_%s" % (finder_function,)] = _remember_generator(finder_string, finder_function)

    def _attach_generator(finder_string, finder_function):
        @step(r'attach "([^"]*)" onto the%s %s %s' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, file_name, pick, find_pattern):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            try:
                ele.value = file_name
            except:  # Zope
                ele._control.value = file_name

        return _this_step

    globals()["form_attach_%s" % (finder_function,)] = _attach_generator(finder_string, finder_function)

    def _focus_generator(finder_string, finder_function):
        @step(r'focus on the%s %s %s' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, pick, find_pattern):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            ele.focus()

        return _this_step

    globals()["form_focus_%s" % (finder_function,)] = _focus_generator(finder_string, finder_function)

    def _blur_generator(finder_string, finder_function):
        @step(r'(?:blur|move) from the%s %s %s' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, pick, find_pattern):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            ele.blur()

        return _this_step

    globals()["form_blur_%s" % (finder_function,)] = _blur_generator(finder_string, finder_function)

    def _value_generator(finder_string, finder_function):
        @step(r'(?:should see that the)? value of the%s %s %s is( not)? "([^"]*)"' % (
            PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, pick, find_pattern, negate, value):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            assert_equals_with_negate(ele.value, value, negate)

        return _this_step

    globals()["form_value_%s" % (finder_function,)] = _value_generator(finder_string, finder_function)

    def _key_generator(finder_string, finder_function):
        @step(r'hit the ([^"]*) key in the%s %s %s' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, key_string, pick, find_pattern):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            key = transform_key_string(key_string)
            ele.type(key)

        return _this_step

    globals()["form_key_%s" % (finder_function,)] = _key_generator(finder_string, finder_function)

@step(r'hit the ([^"]*) key')
def hit_key(step, key_string):
    key = transform_key_string(key_string)
    try:
        world.browser.driver.switch_to_active_element().send_keys(key)
    except StaleElementReferenceException:
        world.browser.find_by_css("body").type(key)

@step(r'store a random (string|email|name)(?: of length (\d+))?(?: with suffix "([^"]*)")? as "([^"]*)"')
def store_value(step, type_of_fill, length, suffix, name):
    if not length:
        length = 9
    if not suffix:
        suffix = ""
    random_value = _generate_content(type_of_fill, int(length)) + suffix
    world.random_strings[name] = random_value

def transform_key_string(key_string):
    key_string = key_string.upper().replace(' ', '_')
    if key_string == 'BACKSPACE':
        key_string = 'BACK_SPACE'
    elif key_string == 'SPACEBAR':
        key_string = 'SPACE'
    key = Keys.__getattribute__(Keys, key_string)
    return key

def _type_slowly(driver_ele, text):
    for c in text:
        driver_ele.value += c
        sleep(0.5)
<commit_msg>renamed some variables and functions<commit_after>from time import sleep
from string import ascii_letters
from random import choice, randint
from selenium.webdriver.support.ui import Select
from lettuce import step, world
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.remote.errorhandler import StaleElementReferenceException
from salad.steps.browser.finders import (PICK_EXPRESSION, ELEMENT_FINDERS,
                                         ELEMENT_THING_STRING,
                                         _get_visible_element)
from salad.tests.util import assert_equals_with_negate

# What's happening here? We're generating steps for every possible permuation of the element finder

world.stored_values = dict()

def _generate_content(type_of_fill, length):
    if type_of_fill == 'email':
        return _generate_random_string(length) + '@mailinator.com'
    elif type_of_fill == 'string':
        return _generate_random_string(length)
    elif type_of_fill == 'name':
        name = _generate_random_string(length)
        if length <= 3:
            return name
        index = randint(1, len(name)-2)
        return name[:index] + ' ' + name[index+1:]

def _generate_random_string(length):
    lst = [choice(ascii_letters) for n in xrange(length)]
    return "".join(lst)

for finder_string, finder_function in ELEMENT_FINDERS.iteritems():

    def _type_generator(finder_string, finder_function):
        @step(r'(slowly )?type "([^"]*)" into the%s %s %s' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, slowly, text, pick, find_pattern):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            if slowly and slowly != "":
                _type_slowly(ele, text)
            else:
                ele.value = text

        return _this_step

    globals()["form_type_%s" % (finder_function,)] = _type_generator(finder_string, finder_function)

    def _select_generator(finder_string, finder_function):
        @step(r'select the option (named|with the value)? "([^"]*)" (?:from|in) the%s %s %s' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, named_or_with_value, field_value, pick, find_pattern):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            if (named_or_with_value == "named"):
                # this does not work properly, it will click the first match
                # to field_value by default. it does not select the element we
                # are actually looking for.
                option = world.browser.find_option_by_text(field_value)
            else:
                option = ele.find_by_value(field_value)

            option.click()

        return _this_step

    globals()["form_select_%s" % (finder_function,)] = _select_generator(finder_string, finder_function)

    def _fill_generator(finder_string, finder_function):
        @step(r'fill in the%s %s %s with "([^"]*)"' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, pick, find_pattern, text):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            try:
                ele.value = text
            except:
                ele._control.value = text

        return _this_step

    globals()["form_fill_%s" % (finder_function,)] = _fill_generator(finder_string, finder_function)

    def _fill_with_stored_generator(finder_string, finder_function):
        @step(r'fill in the%s %s %s with the stored value of "([^"]*)"' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, pick, find_pattern, name):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            assert(world.stored_values[name])
            try:
                ele.value = world.stored_values[name]
            except:
                ele._control.value = world.stored_values[name]

        return _this_step

    globals()["form_fill_with_stored_%s" % (finder_function,)] = _fill_with_stored_generator(finder_string, finder_function)

    def _see_stored_value_generator(finder_string, finder_function):
        @step(r'should see the stored value of "([^"]*)" in the%s %s %s' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, name, pick, find_pattern):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            random_value = world.stored_values[name]
            if not (ele.value == random_value or
                    ele.text == random_value):
                raise AssertionError("random string %s not found in "
                                     "%s / %s" %
                                     (random_value,
                                      finder_string, finder_function))

        return _this_step

    globals()["form_see_stored_%s" % (finder_function,)] = _see_stored_value_generator(finder_string, finder_function)

    def _attach_generator(finder_string, finder_function):
        @step(r'attach "([^"]*)" onto the%s %s %s' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, file_name, pick, find_pattern):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            try:
                ele.value = file_name
            except:  # Zope
                ele._control.value = file_name

        return _this_step

    globals()["form_attach_%s" % (finder_function,)] = _attach_generator(finder_string, finder_function)

    def _focus_generator(finder_string, finder_function):
        @step(r'focus on the%s %s %s' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, pick, find_pattern):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            ele.focus()

        return _this_step

    globals()["form_focus_%s" % (finder_function,)] = _focus_generator(finder_string, finder_function)

    def _blur_generator(finder_string, finder_function):
        @step(r'(?:blur|move) from the%s %s %s' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, pick, find_pattern):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            ele.blur()

        return _this_step

    globals()["form_blur_%s" % (finder_function,)] = _blur_generator(finder_string, finder_function)

    def _value_generator(finder_string, finder_function):
        @step(r'(?:should see that the)? value of the%s %s %s is( not)? "([^"]*)"' % (
            PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, pick, find_pattern, negate, value):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            assert_equals_with_negate(ele.value, value, negate)

        return _this_step

    globals()["form_value_%s" % (finder_function,)] = _value_generator(finder_string, finder_function)

    def _key_generator(finder_string, finder_function):
        @step(r'hit the ([^"]*) key in the%s %s %s' % (PICK_EXPRESSION, ELEMENT_THING_STRING, finder_string))
        def _this_step(step, key_string, pick, find_pattern):
            ele = _get_visible_element(finder_function, pick, find_pattern)
            key = transform_key_string(key_string)
            ele.type(key)

        return _this_step

    globals()["form_key_%s" % (finder_function,)] = _key_generator(finder_string, finder_function)

@step(r'hit the ([^"]*) key')
def hit_key(step, key_string):
    key = transform_key_string(key_string)
    try:
        world.browser.driver.switch_to_active_element().send_keys(key)
    except StaleElementReferenceException:
        world.browser.find_by_css("body").type(key)

@step(r'store a random (string|email|name)(?: of length (\d+))?(?: with suffix "([^"]*)")? as "([^"]*)"')
def store_value(step, type_of_fill, length, suffix, name):
    if not length:
        length = 9
    if not suffix:
        suffix = ""
    random_value = _generate_content(type_of_fill, int(length)) + suffix
    world.stored_values[name] = random_value

def transform_key_string(key_string):
    key_string = key_string.upper().replace(' ', '_')
    if key_string == 'BACKSPACE':
        key_string = 'BACK_SPACE'
    elif key_string == 'SPACEBAR':
        key_string = 'SPACE'
    key = Keys.__getattribute__(Keys, key_string)
    return key

def _type_slowly(driver_ele, text):
    for c in text:
        driver_ele.value += c
        sleep(0.5)
<|endoftext|>


<commit_before>from decimal import Decimal
from typing import TYPE_CHECKING, Any, List, Optional, Union

from django.conf import settings
from django.utils.module_loading import import_string
from django_countries.fields import Country
from prices import Money, MoneyRange, TaxedMoney, TaxedMoneyRange

from ..core.payments import PaymentInterface
from ..core.taxes import TaxType, quantize_price
from .models import PluginConfiguration

if TYPE_CHECKING:
    from .base_plugin import BasePlugin
    from ..checkout.models import Checkout, CheckoutLine
    from ..product.models import Product
    from ..account.models import Address
    from ..order.models import OrderLine, Order
    from ..payment.interface import PaymentData, GatewayResponse, CustomerSource

class ExtensionsManager(PaymentInterface):
    """Base manager for handling plugins logic."""

    plugins = None

    def __init__(self, plugins: List[str]):
        self.plugins = []
        for plugin_path in plugins:
            plugin_class = import_string(plugin_path)
            self.plugins.append(plugin_class())

    def __run_method_on_plugins(
        self, method_name: str, default_value: Any, *args, **kwargs
    ):
        """Try to run a method with the given name on each declared plugin."""
        value = default_value
        for plugin in self.plugins:
            value = self.__run_method_on_single_plugin(
                plugin, method_name, value, *args, **kwargs
            )
        return value

    def __run_method_on_single_plugin(
        self,
        plugin: "BasePlugin",
        method_name: str,
        previous_value: Any,
        *args,
        **kwargs,
    ) -> Any:
        """Run method_name on plugin.

        Method will return value returned from plugin's
        method. If plugin doesn't have own implementation of expected method_name, it
        will return previous_value.
        """
        plugin_method = getattr(plugin, method_name, NotImplemented)
        if plugin_method == NotImplemented:
            return previous_value

        returned_value = plugin_method(*args, **kwargs, previous_value=previous_value)
        if returned_value == NotImplemented:
            return previous_value
        return returned_value

    def calculate_checkout_total(
        self, checkout: "Checkout", discounts: List["DiscountInfo"]
    ) -> TaxedMoney:
        total = checkout.get_total(discounts)
        default_value = quantize_price(
            TaxedMoney(net=total, gross=total), total.currency
        )
        return self.__run_method_on_plugins(
            "calculate_checkout_total", default_value, checkout, discounts
        )

    def calculate_checkout_subtotal(
        self, checkout: "Checkout", discounts: List["DiscountInfo"]
    ) -> TaxedMoney:
        subtotal = checkout.get_subtotal(discounts)
        default_value = quantize_price(
            TaxedMoney(net=subtotal, gross=subtotal), subtotal.currency
        )
        return self.__run_method_on_plugins(
            "calculate_checkout_subtotal", default_value, checkout, discounts
        )

    def calculate_checkout_shipping(
        self, checkout: "Checkout", discounts: List["DiscountInfo"]
    ) -> TaxedMoney:
        total = checkout.get_shipping_price()
        total = TaxedMoney(net=total, gross=total)
        default_value = quantize_price(total, total.currency)
        return self.__run_method_on_plugins(
            "calculate_checkout_shipping", default_value, checkout, discounts
        )

    def calculate_order_shipping(self, order: "Order") -> TaxedMoney:
        shipping_price = order.shipping_method.price
        default_value = quantize_price(
            TaxedMoney(net=shipping_price, gross=shipping_price),
            shipping_price.currency,
        )
        return self.__run_method_on_plugins(
            "calculate_order_shipping", default_value, order
        )

    def calculate_checkout_line_total(
        self, checkout_line: "CheckoutLine", discounts: List["DiscountInfo"]
    ):
        total = checkout_line.get_total(discounts)
        default_value = quantize_price(
            TaxedMoney(net=total, gross=total), total.currency
        )
        return self.__run_method_on_plugins(
            "calculate_checkout_line_total", default_value, checkout_line, discounts
        )

    def calculate_order_line_unit(self, order_line: "OrderLine") -> TaxedMoney:
        unit_price = order_line.unit_price
        default_value = quantize_price(unit_price, unit_price.currency)
        return self.__run_method_on_plugins(
            "calculate_order_line_unit", default_value, order_line
        )

    def get_tax_rate_type_choices(self) -> List[TaxType]:
        default_value = []
        return self.__run_method_on_plugins("get_tax_rate_type_choices", default_value)

    def show_taxes_on_storefront(self) -> bool:
        default_value = False
        return self.__run_method_on_plugins("show_taxes_on_storefront", default_value)

    def taxes_are_enabled(self) -> bool:
        default_value = False
        return self.__run_method_on_plugins("taxes_are_enabled", default_value)

    def apply_taxes_to_product(
        self, product: "Product", price: Money, country: Country
    ):
        default_value = quantize_price(
            TaxedMoney(net=price, gross=price), price.currency
        )
        return self.__run_method_on_plugins(
            "apply_taxes_to_product", default_value, product, price, country
        )

    def apply_taxes_to_shipping(
        self, price: Money, shipping_address: "Address"
    ) -> TaxedMoney:
        default_value = quantize_price(
            TaxedMoney(net=price, gross=price), price.currency
        )
        return self.__run_method_on_plugins(
            "apply_taxes_to_shipping", default_value, price, shipping_address
        )

    def apply_taxes_to_shipping_price_range(self, prices: MoneyRange, country: Country):
        start = TaxedMoney(net=prices.start, gross=prices.start)
        stop = TaxedMoney(net=prices.stop, gross=prices.stop)
        default_value = quantize_price(
            TaxedMoneyRange(start=start, stop=stop), start.currency
        )
        return self.__run_method_on_plugins(
            "apply_taxes_to_shipping_price_range", default_value, prices, country
        )

    def preprocess_order_creation(
        self, checkout: "Checkout", discounts: List["DiscountInfo"]
    ):
        default_value = None
        return self.__run_method_on_plugins(
            "preprocess_order_creation", default_value, checkout, discounts
        )

    def postprocess_order_creation(self, order: "Order"):
        default_value = None
        return self.__run_method_on_plugins(
            "postprocess_order_creation", default_value, order
        )

    def authorize_payment(
        self, gateway: str, payment_information: "PaymentData"
    ) -> "GatewayResponse":
        method_name = "authorize_payment"
        return self.__run_payment_method(gateway, method_name, payment_information)

    def capture_payment(
        self, gateway: str, payment_information: "PaymentData"
    ) -> "GatewayResponse":
        method_name = "capture_payment"
        return self.__run_payment_method(gateway, method_name, payment_information)

    def refund_payment(
        self, gateway: str, payment_information: "PaymentData"
    ) -> "GatewayResponse":
        method_name = "refund_payment"
        return self.__run_payment_method(gateway, method_name, payment_information)

    def void_payment(
        self, gateway: str, payment_information: "PaymentData"
    ) -> "GatewayResponse":
        method_name = "void_payment"
        return self.__run_payment_method(gateway, method_name, payment_information)

    def confirm_payment(
        self, gateway: str, payment_information: "PaymentData"
    ) -> "GatewayResponse":
        method_name = "confirm_payment"
        return self.__run_payment_method(gateway, method_name, payment_information)

    def process_payment(
        self, gateway: str, payment_information: "PaymentData"
    ) -> "GatewayResponse":
        method_name = "process_payment"
        return self.__run_payment_method(gateway, method_name, payment_information)

    def list_payment_sources(self, customer_id: str) -> List["CustomerSource"]:
        default_value = []
        return self.__run_method_on_plugins(
            "list_payment_sources", default_value, customer_id=customer_id
        )

    def __run_payment_method(
        self, gateway: str, method_name: str, payment_information: "PaymentData"
    ) -> Optional["GatewayResposne"]:
        default_value = None
        gateway_name = gateway.value
        gtw = self.get_plugin(gateway_name)
        if gtw is not None and gtw.active:
            return self.__run_method_on_single_plugin(
                gtw, method_name, default_value, payment_information=payment_information
            )

        raise Exception(f"Payment plugin {gateway_name} is inaccessible!")

    # FIXME these methods should be more generic

    def assign_tax_code_to_object_meta(
        self, obj: Union["Product", "ProductType"], tax_code: str
    ):
        default_value = None
        return self.__run_method_on_plugins(
            "assign_tax_code_to_object_meta", default_value, obj, tax_code
        )

    def get_tax_code_from_object_meta(
        self, obj: Union["Product", "ProductType"]
    ) -> TaxType:
        default_value = TaxType(code="", description="")
        return self.__run_method_on_plugins(
            "get_tax_code_from_object_meta", default_value, obj
        )

    def get_tax_rate_percentage_value(
        self, obj: Union["Product", "ProductType"], country: Country
    ) -> Decimal:
        default_value = Decimal("0").quantize(Decimal("1."))
        return self.__run_method_on_plugins(
            "get_tax_rate_percentage_value", default_value, obj, country
        ).quantize(Decimal("1."))

    def save_plugin_configuration(self, plugin_name, cleaned_data: dict):
        plugin_configuration = PluginConfiguration.objects.get(name=plugin_name)
        for plugin in self.plugins:
            if plugin.PLUGIN_NAME == plugin_name:
                return plugin.save_plugin_configuration(
                    plugin_configuration, cleaned_data
                )

    def get_plugin(self, plugin_name: str) -> Optional["BasePlugin"]:
        for plugin in self.plugins:
            if plugin.PLUGIN_NAME == plugin_name:
                return plugin

    def get_plugin_configuration(self, plugin_name) -> Optional["PluginConfiguration"]:
        plugin = self.get_plugin(plugin_name)
        if plugin is not None:
            plugin_configurations_qs = PluginConfiguration.objects.all()
            return plugin.get_plugin_configuration(plugin_configurations_qs)

    def get_plugin_configurations(self) -> List["PluginConfiguration"]:
        plugin_configuration_ids = []
        plugin_configurations_qs = PluginConfiguration.objects.all()
        for plugin in self.plugins:
            plugin_configuration = plugin.get_plugin_configuration(
                plugin_configurations_qs
            )
            plugin_configuration_ids.append(plugin_configuration.pk)
        return PluginConfiguration.objects.filter(pk__in=plugin_configuration_ids)

def get_extensions_manager(
    manager_path: str = None, plugins: List[str] = None
) -> ExtensionsManager:
    if not manager_path:
        manager_path = settings.EXTENSIONS_MANAGER
    if plugins is None:
        plugins = settings.PLUGINS
    manager = import_string(manager_path)
    return manager(plugins)
<commit_msg>Fix finding gateway before config is loaded<commit_after>from decimal import Decimal
from typing import TYPE_CHECKING, Any, List, Optional, Union

from django.conf import settings
from django.utils.module_loading import import_string
from django_countries.fields import Country
from prices import Money, MoneyRange, TaxedMoney, TaxedMoneyRange

from ..core.payments import Gateway, PaymentInterface
from ..core.taxes import TaxType, quantize_price
from .models import PluginConfiguration

if TYPE_CHECKING:
    from .base_plugin import BasePlugin
    from ..checkout.models import Checkout, CheckoutLine
    from ..product.models import Product
    from ..account.models import Address
    from ..order.models import OrderLine, Order
    from ..payment.interface import PaymentData, GatewayResponse, CustomerSource

class ExtensionsManager(PaymentInterface):
    """Base manager for handling plugins logic."""

    plugins = None

    def __init__(self, plugins: List[str]):
        self.plugins = []
        for plugin_path in plugins:
            plugin_class = import_string(plugin_path)
            self.plugins.append(plugin_class())

    def __run_method_on_plugins(
        self, method_name: str, default_value: Any, *args, **kwargs
    ):
        """Try to run a method with the given name on each declared plugin."""
        value = default_value
        for plugin in self.plugins:
            value = self.__run_method_on_single_plugin(
                plugin, method_name, value, *args, **kwargs
            )
        return value

    def __run_method_on_single_plugin(
        self,
        plugin: "BasePlugin",
        method_name: str,
        previous_value: Any,
        *args,
        **kwargs,
    ) -> Any:
        """Run method_name on plugin.

        Method will return value returned from plugin's
        method. If plugin doesn't have own implementation of expected method_name, it
        will return previous_value.
        """
        plugin_method = getattr(plugin, method_name, NotImplemented)
        if plugin_method == NotImplemented:
            return previous_value

        returned_value = plugin_method(*args, **kwargs, previous_value=previous_value)
        if returned_value == NotImplemented:
            return previous_value
        return returned_value

    def calculate_checkout_total(
        self, checkout: "Checkout", discounts: List["DiscountInfo"]
    ) -> TaxedMoney:
        total = checkout.get_total(discounts)
        default_value = quantize_price(
            TaxedMoney(net=total, gross=total), total.currency
        )
        return self.__run_method_on_plugins(
            "calculate_checkout_total", default_value, checkout, discounts
        )

    def calculate_checkout_subtotal(
        self, checkout: "Checkout", discounts: List["DiscountInfo"]
    ) -> TaxedMoney:
        subtotal = checkout.get_subtotal(discounts)
        default_value = quantize_price(
            TaxedMoney(net=subtotal, gross=subtotal), subtotal.currency
        )
        return self.__run_method_on_plugins(
            "calculate_checkout_subtotal", default_value, checkout, discounts
        )

    def calculate_checkout_shipping(
        self, checkout: "Checkout", discounts: List["DiscountInfo"]
    ) -> TaxedMoney:
        total = checkout.get_shipping_price()
        total = TaxedMoney(net=total, gross=total)
        default_value = quantize_price(total, total.currency)
        return self.__run_method_on_plugins(
            "calculate_checkout_shipping", default_value, checkout, discounts
        )

    def calculate_order_shipping(self, order: "Order") -> TaxedMoney:
        shipping_price = order.shipping_method.price
        default_value = quantize_price(
            TaxedMoney(net=shipping_price, gross=shipping_price),
            shipping_price.currency,
        )
        return self.__run_method_on_plugins(
            "calculate_order_shipping", default_value, order
        )

    def calculate_checkout_line_total(
        self, checkout_line: "CheckoutLine", discounts: List["DiscountInfo"]
    ):
        total = checkout_line.get_total(discounts)
        default_value = quantize_price(
            TaxedMoney(net=total, gross=total), total.currency
        )
        return self.__run_method_on_plugins(
            "calculate_checkout_line_total", default_value, checkout_line, discounts
        )

    def calculate_order_line_unit(self, order_line: "OrderLine") -> TaxedMoney:
        unit_price = order_line.unit_price
        default_value = quantize_price(unit_price, unit_price.currency)
        return self.__run_method_on_plugins(
            "calculate_order_line_unit", default_value, order_line
        )

    def get_tax_rate_type_choices(self) -> List[TaxType]:
        default_value = []
        return self.__run_method_on_plugins("get_tax_rate_type_choices", default_value)

    def show_taxes_on_storefront(self) -> bool:
        default_value = False
        return self.__run_method_on_plugins("show_taxes_on_storefront", default_value)

    def taxes_are_enabled(self) -> bool:
        default_value = False
        return self.__run_method_on_plugins("taxes_are_enabled", default_value)

    def apply_taxes_to_product(
        self, product: "Product", price: Money, country: Country
    ):
        default_value = quantize_price(
            TaxedMoney(net=price, gross=price), price.currency
        )
        return self.__run_method_on_plugins(
            "apply_taxes_to_product", default_value, product, price, country
        )

    def apply_taxes_to_shipping(
        self, price: Money, shipping_address: "Address"
    ) -> TaxedMoney:
        default_value = quantize_price(
            TaxedMoney(net=price, gross=price), price.currency
        )
        return self.__run_method_on_plugins(
            "apply_taxes_to_shipping", default_value, price, shipping_address
        )

    def apply_taxes_to_shipping_price_range(self, prices: MoneyRange, country: Country):
        start = TaxedMoney(net=prices.start, gross=prices.start)
        stop = TaxedMoney(net=prices.stop, gross=prices.stop)
        default_value = quantize_price(
            TaxedMoneyRange(start=start, stop=stop), start.currency
        )
        return self.__run_method_on_plugins(
            "apply_taxes_to_shipping_price_range", default_value, prices, country
        )

    def preprocess_order_creation(
        self, checkout: "Checkout", discounts: List["DiscountInfo"]
    ):
        default_value = None
        return self.__run_method_on_plugins(
            "preprocess_order_creation", default_value, checkout, discounts
        )

    def postprocess_order_creation(self, order: "Order"):
        default_value = None
        return self.__run_method_on_plugins(
            "postprocess_order_creation", default_value, order
        )

    def authorize_payment(
        self, gateway: str, payment_information: "PaymentData"
    ) -> "GatewayResponse":
        method_name = "authorize_payment"
        return self.__run_payment_method(gateway, method_name, payment_information)

    def capture_payment(
        self, gateway: str, payment_information: "PaymentData"
    ) -> "GatewayResponse":
        method_name = "capture_payment"
        return self.__run_payment_method(gateway, method_name, payment_information)

    def refund_payment(
        self, gateway: str, payment_information: "PaymentData"
    ) -> "GatewayResponse":
        method_name = "refund_payment"
        return self.__run_payment_method(gateway, method_name, payment_information)

    def void_payment(
        self, gateway: str, payment_information: "PaymentData"
    ) -> "GatewayResponse":
        method_name = "void_payment"
        return self.__run_payment_method(gateway, method_name, payment_information)

    def confirm_payment(
        self, gateway: str, payment_information: "PaymentData"
    ) -> "GatewayResponse":
        method_name = "confirm_payment"
        return self.__run_payment_method(gateway, method_name, payment_information)

    def process_payment(
        self, gateway: str, payment_information: "PaymentData"
    ) -> "GatewayResponse":
        method_name = "process_payment"
        return self.__run_payment_method(gateway, method_name, payment_information)

    def list_payment_sources(self, customer_id: str) -> List["CustomerSource"]:
        default_value = []
        return self.__run_method_on_plugins(
            "list_payment_sources", default_value, customer_id=customer_id
        )

    def __run_payment_method(
        self, gateway: Gateway, method_name: str, payment_information: "PaymentData"
    ) -> Optional["GatewayResposne"]:
        default_value = None
        gateway_name = gateway.value
        gtw = self.get_plugin(gateway_name)
        if gtw is not None:
            return self.__run_method_on_single_plugin(
                gtw, method_name, default_value, payment_information=payment_information
            )

        raise Exception(f"Payment plugin {gateway_name} is inaccessible!")

    # FIXME these methods should be more generic

    def assign_tax_code_to_object_meta(
        self, obj: Union["Product", "ProductType"], tax_code: str
    ):
        default_value = None
        return self.__run_method_on_plugins(
            "assign_tax_code_to_object_meta", default_value, obj, tax_code
        )

    def get_tax_code_from_object_meta(
        self, obj: Union["Product", "ProductType"]
    ) -> TaxType:
        default_value = TaxType(code="", description="")
        return self.__run_method_on_plugins(
            "get_tax_code_from_object_meta", default_value, obj
        )

    def get_tax_rate_percentage_value(
        self, obj: Union["Product", "ProductType"], country: Country
    ) -> Decimal:
        default_value = Decimal("0").quantize(Decimal("1."))
        return self.__run_method_on_plugins(
            "get_tax_rate_percentage_value", default_value, obj, country
        ).quantize(Decimal("1."))

    def save_plugin_configuration(self, plugin_name, cleaned_data: dict):
        plugin_configuration = PluginConfiguration.objects.get(name=plugin_name)
        for plugin in self.plugins:
            if plugin.PLUGIN_NAME == plugin_name:
                return plugin.save_plugin_configuration(
                    plugin_configuration, cleaned_data
                )

    def get_plugin(self, plugin_name: str) -> Optional["BasePlugin"]:
        for plugin in self.plugins:
            if plugin.PLUGIN_NAME == plugin_name:
                return plugin

    def get_plugin_configuration(self, plugin_name) -> Optional["PluginConfiguration"]:
        plugin = self.get_plugin(plugin_name)
        if plugin is not None:
            plugin_configurations_qs = PluginConfiguration.objects.all()
            return plugin.get_plugin_configuration(plugin_configurations_qs)

    def get_plugin_configurations(self) -> List["PluginConfiguration"]:
        plugin_configuration_ids = []
        plugin_configurations_qs = PluginConfiguration.objects.all()
        for plugin in self.plugins:
            plugin_configuration = plugin.get_plugin_configuration(
                plugin_configurations_qs
            )
            plugin_configuration_ids.append(plugin_configuration.pk)
        return PluginConfiguration.objects.filter(pk__in=plugin_configuration_ids)

def get_extensions_manager(
    manager_path: str = None, plugins: List[str] = None
) -> ExtensionsManager:
    if not manager_path:
        manager_path = settings.EXTENSIONS_MANAGER
    if plugins is None:
        plugins = settings.PLUGINS
    manager = import_string(manager_path)
    return manager(plugins)
<|endoftext|>