How to specify multiple author(s) / email(s) in setup.py How to specify multiple author(s) / email(s) in setup.py python python

How to specify multiple author(s) / email(s) in setup.py


As far as I know, setuptools doesn't support using a list of strings in order to specify multiple authors. Your best bet is to list the authors in a single string:

author='Foo Bar, Spam Eggs',author_email='foobar@baz.com, spameggs@joe.org',

I'm not sure if PyPI validates the author_email field, so you may run into trouble with that one. In any case, I would recommend you limit these to a single author and mention all contributors in the documentation or description.

[Edit] Some sources:

This has been registered as a bug, actually, but it seems like support for multiple authors was not implemented. Here is an alternative solution. Here is an idea for how to provide a contact email for a project with multiple authors.


I'm sort of just piggybacking off of @modocache's answer, in case you want some specifics.

Throughout this answer, I'll be refering to a python3.6 version of the FOO-PYTHON-ENV\Lib\distutils\dist.py file

To reiterate, you cannot use a list in the author field. Here's why:

Spoiler: Two methods belonging to the DistributionMetadata class are the reason --

def _read_field(name):    value = msg[name]    if value == 'UNKNOWN':        return None    return valuedef _read_list(name):    values = msg.get_all(name, None)    if values == []:        return None    return values

Here's where you'll hit an error if you try to stick a list in the author field:

class DistributionMetadata:#*...(R E D A C T E D)...*#    def read_pkg_file(self, file):        """Reads the metadata values from a file object."""    #*...(R E D A C T E D)...*#        # ####################################        # Note the usage of _read_field() here        # ####################################        self.name = _read_field('name')        self.version = _read_field('version')        self.description = _read_field('summary')        # we are filling author only.        self.author = _read_field('author')        self.maintainer = None        self.author_email = _read_field('author-email')        self.maintainer_email = None        self.url = _read_field('home-page')        self.license = _read_field('license')    #*...(R E D A C T E D)...*#        # ###################################        # Note the usage of _read_list() here        # ###################################        self.platforms = _read_list('platform')        self.classifiers = _read_list('classifier')    #*...(R E D A C T E D)...*#

& Here's the whole thing:

class DistributionMetadata:        """Dummy class to hold the distribution meta-data: name, version,        author, and so forth.        """        _METHOD_BASENAMES = ("name", "version", "author", "author_email",                     "maintainer", "maintainer_email", "url",                     "license", "description", "long_description",                     "keywords", "platforms", "fullname", "contact",                     "contact_email", "classifiers", "download_url",                     # PEP 314                     "provides", "requires", "obsoletes",                     )    def __init__(self, path=None):        if path is not None:            self.read_pkg_file(open(path))        else:            self.name = None            self.version = None            self.author = None            self.author_email = None            self.maintainer = None            self.maintainer_email = None            self.url = None            self.license = None            self.description = None            self.long_description = None            self.keywords = None            self.platforms = None            self.classifiers = None            self.download_url = None            # PEP 314            self.provides = None            self.requires = None            self.obsoletes = None    def read_pkg_file(self, file):        """Reads the metadata values from a file object."""        msg = message_from_file(file)        def _read_field(name):            value = msg[name]            if value == 'UNKNOWN':                return None            return value        def _read_list(name):            values = msg.get_all(name, None)            if values == []:                return None            return values        metadata_version = msg['metadata-version']        self.name = _read_field('name')        self.version = _read_field('version')        self.description = _read_field('summary')        # we are filling author only.        self.author = _read_field('author')        self.maintainer = None        self.author_email = _read_field('author-email')        self.maintainer_email = None        self.url = _read_field('home-page')        self.license = _read_field('license')        if 'download-url' in msg:            self.download_url = _read_field('download-url')        else:            self.download_url = None        self.long_description = _read_field('description')        self.description = _read_field('summary')        if 'keywords' in msg:            self.keywords = _read_field('keywords').split(',')        self.platforms = _read_list('platform')        self.classifiers = _read_list('classifier')        # PEP 314 - these fields only exist in 1.1        if metadata_version == '1.1':            self.requires = _read_list('requires')            self.provides = _read_list('provides')            self.obsoletes = _read_list('obsoletes')        else:            self.requires = None            self.provides = None            self.obsoletes = None