Entry point for import program.

    If the ``args`` are provided, these should be a list of strings that will
    be used instead of ``sys.argv[1:]``. This is mostly useful for testing.

def main(importer_class=None, args=None):
    """Entry point for import program.

    If the ``args`` are provided, these should be a list of strings that will
    be used instead of ``sys.argv[1:]``. This is mostly useful for testing.
    """
    parser = argparse.ArgumentParser(
        description='Import from external model formats')
    parser.add_argument('--source', metavar='path', default='.',
                        help='Source directory or file')
    parser.add_argument('--dest', metavar='path', default='.',
                        help='Destination directory (default is ".")')
    parser.add_argument('--no-exchange', action='store_true',
                        help=('Disable importing exchange reactions as'
                              ' exchange compound file.'))
    parser.add_argument('--split-subsystem', action='store_true',
                        help='Enable splitting reaction files by subsystem')
    parser.add_argument('--merge-compounds', action='store_true',
                        help=('Merge identical compounds occuring in various'
                              ' compartments.'))
    parser.add_argument('--force', action='store_true',
                        help='Enable overwriting model files')

    if importer_class is None:
        parser.add_argument(
            'format', help='Format to import ("list" to see all)')

    args = parser.parse_args(args)

    # Set up logging for the command line interface
    if 'PSAMM_DEBUG' in os.environ:
        level = getattr(logging, os.environ['PSAMM_DEBUG'].upper(), None)
        if level is not None:
            logging.basicConfig(level=level)
    else:
        logging.basicConfig(
            level=logging.INFO, format='%(levelname)s: %(message)s')

    if importer_class is None:
        # Discover all available model importers
        importers = {}
        for importer_entry in pkg_resources.iter_entry_points(
                'psamm.importer'):
            canonical = importer_entry.name.lower()
            if canonical not in importers:
                importers[canonical] = importer_entry
            else:
                logger.warning('Importer {} was found more than once!'.format(
                    importer_entry.name))

        # Print list of importers
        if args.format in ('list', 'help'):
            if len(importers) == 0:
                logger.error('No importers found!')
            else:
                importer_classes = []
                for name, entry in iteritems(importers):
                    importer_class = entry.load()
                    title = getattr(importer_class, 'title', None)
                    generic = getattr(importer_class, 'generic', False)
                    if title is not None:
                        importer_classes.append(
                            (title, generic, name, importer_class))

                print('Generic importers:')
                for title, _, name, importer_class in sorted(
                        c for c in importer_classes if c[1]):
                    print('{:<12}  {}'.format(name, title))

                print()
                print('Model-specific importers:')
                for title, _, name, importer_class in sorted(
                        c for c in importer_classes if not c[1]):
                    print('{:<12}  {}'.format(name, title))
            sys.exit(0)

        importer_name = args.format.lower()
        if importer_name not in importers:
            logger.error('Importer {} not found!'.format(importer_name))
            logger.info('Use "list" to see available importers.')
            sys.exit(-1)

        importer_class = importers[importer_name].load()

    importer = importer_class()

    try:
        model = importer.import_model(args.source)
    except ModelLoadError as e:
        logger.error('Failed to load model!', exc_info=True)
        importer.help()
        parser.error(text_type(e))
    except ParseError as e:
        logger.error('Failed to parse model!', exc_info=True)
        logger.error(text_type(e))
        sys.exit(-1)

    if args.merge_compounds:
        compounds_before = len(model.compounds)
        sbml.merge_equivalent_compounds(model)
        if len(model.compounds) < compounds_before:
            logger.info(
                'Merged {} compound entries into {} entries by'
                ' removing duplicates in various compartments'.format(
                    compounds_before, len(model.compounds)))

    print('Model: {}'.format(model.name))
    print('- Biomass reaction: {}'.format(model.biomass_reaction))
    print('- Compartments: {}'.format(len(model.compartments)))
    print('- Compounds: {}'.format(len(model.compounds)))
    print('- Reactions: {}'.format(len(model.reactions)))
    print('- Genes: {}'.format(count_genes(model)))

    # Check if dest directory is empty. If we get an error assume that the
    # directory does not exist.
    dest_is_empty = False
    try:
        dest_is_empty = len(os.listdir(args.dest)) == 0
    except OSError:
        dest_is_empty = True

    if not dest_is_empty:
        if not args.force:
            logger.error('Destination directory is not empty. Use --force'
                         ' option to proceed anyway, overwriting any existing'
                         ' files in {}'.format(args.dest))
            return 1
        else:
            logger.warning('Destination directory is not empty, overwriting'
                           ' existing files in {}'.format(args.dest))

    # Create destination directory if not exists
    dest = args.dest
    mkdir_p(dest)

    convert_exchange = not args.no_exchange
    write_yaml_model(model, dest, convert_exchange=convert_exchange,
                     split_subsystem=args.split_subsystem)