sburns: Yeah in practice its the same, it comes down to what files you expect to see changed during a PR review. Technically, this is backwards incompatible as you're removing `__version__` from the top-level package.

Someone might be doing

```
import redcap
do_something_with(redcap.__version__)
```

That will `AttributeError` if this PR lands.
pwildenhain: Good catch! I've added that variable back in for backwards compatibility. I know it's not ideal to have it documented in two places (i.e why you had the other process 😉) though I think this is worth the short term benefit of making the package version work when installing from GitHub. And in the long term, we might be able to come up with a different strategy that meets both needs.
pwildenhain: Alternatively, if you don't want to double document this, we could try refactoring the `get_version()` function to look more like this: https://github.com/jendrikseipp/vulture/blob/master/setup.py#L18 ?

Or do you think that still wouldn't solve our problem?
diff --git a/README.rst b/README.rst
index cbd84c6..010c4b2 100644
--- a/README.rst
+++ b/README.rst
@@ -22,6 +22,7 @@ Currently, these API calls are available:
 
 -   Export Records
 -   Export Metadata
+-   Delete Records
 -   Import Records
 -   Export File
 -   Import File
@@ -82,6 +83,9 @@ Usage
     ...     # This throws if you made a bad request, e.g. tried to delete a field
     ...     # that isn't a file
 
+    # Delete record
+    >>> response = project.delete_record('1')
+
     # Export form event mappings
     >>> fem = project.export_fem()
     ...
diff --git a/docs/deep.rst b/docs/deep.rst
index ede5c1f..c8e2f79 100644
--- a/docs/deep.rst
+++ b/docs/deep.rst
@@ -141,6 +141,7 @@ Exporting large projects will fail on REDCap's backend and PyCap will throw a ``
 
 Here's an exporting function that trades speed for robustness::
 
+    # Python 3 does not support xrange(), use range() instead
     def chunked_export(project, chunk_size=100):
         def chunks(l, n):
             """Yield successive n-sized chunks from list l"""
diff --git a/redcap/__init__.py b/redcap/__init__.py
index 39c209b..f4df935 100644
--- a/redcap/__init__.py
+++ b/redcap/__init__.py
@@ -4,6 +4,7 @@
 __author__ = 'Scott Burns <scott.s.burns@gmail.com>'
 __license__ = 'MIT'
 __copyright__ = '2014, Vanderbilt University'
+__version__ = '1.0.2'
 
 """
 This module exposes the REDCap API through the Project class. Instantiate the
@@ -18,4 +19,3 @@ Other API requests are available, such as exporting users & Form-Event Mappings.
 
 from .project import Project
 from .request import RCRequest, RCAPIError, RedcapError
-from .version import VERSION as __version__
diff --git a/redcap/project.py b/redcap/project.py
index 3e3e0ad..5a93396 100755
--- a/redcap/project.py
+++ b/redcap/project.py
@@ -179,11 +179,11 @@ class Project(object):
             from pandas import read_csv
             ret_format = 'csv'
         pl = self.__basepl('formEventMapping', format=ret_format)
-        to_add = [arms]
-        str_add = ['arms']
-        for key, data in zip(str_add, to_add):
-            if data:
-                pl[key] = ','.join(data)
+
+        if arms:
+            for i, value in enumerate(arms):
+                pl["arms[{}]".format(i)] = value
+
         response, _ = self._call_api(pl, 'exp_fem')
         if format in ('json', 'csv', 'xml'):
             return response
@@ -226,7 +226,9 @@ class Project(object):
         str_add = ['fields', 'forms']
         for key, data in zip(str_add, to_add):
             if data:
-                pl[key] = ','.join(data)
+                for i, value in enumerate(data):
+                    pl["{}[{}]".format(key, i)] = value
+
         response, _ = self._call_api(pl, 'metadata')
         if format in ('json', 'csv', 'xml'):
             return response
@@ -235,6 +237,32 @@ class Project(object):
                 df_kwargs = {'index_col': 'field_name'}
             return read_csv(StringIO(response), **df_kwargs)
 
+    def delete_record(self, record, format='json'):
+        """
+        Delete a record form the Project.
+
+        Parameters
+        __________
+        record : string
+            The record ID that you wan to delete from the project
+
+        format : (``'json'``), ``'csv'``, ``'xml'``, ``'df'``
+            Return the metadata in native objects, csv or xml.
+            ``'df'`` will return a ``pandas.DataFrame``.
+        Returns
+        _______
+        None
+        """
+        pl = dict()
+        pl['action'] = 'delete'
+        pl['content'] = 'record'
+        pl['token'] = self.token
+        pl['records[0]'] = record
+        pl['format'] = format
+        response, _ = self._call_api(pl,'del_record')
+        if format in ('json', 'csv', 'xml'):
+            return response
+
     def export_records(self, records=None, fields=None, forms=None,
     events=None, raw_or_label='raw', event_name='label',
     format='json', export_survey_fields=False,
@@ -311,9 +339,9 @@ class Project(object):
         'exportCheckboxLabel')
         for key, data in zip(str_keys, keys_to_add):
             if data:
-                #  Make a url-ok string
                 if key in ('fields', 'records', 'forms', 'events'):
-                    pl[key] = ','.join(data)
+                    for i, value in enumerate(data):
+                        pl["{}[{}]".format(key, i)] = value
                 else:
                     pl[key] = data
 
@@ -665,7 +693,7 @@ class Project(object):
         pl = self.__basepl(content='generateNextRecordName')
 
         return self._call_api(pl, 'exp_next_id')[0]
-      
+
     def export_project_info(self, format='json'):
         """
         Export Project Information
diff --git a/redcap/request.py b/redcap/request.py
index 487ea6b..1aacba0 100644
--- a/redcap/request.py
+++ b/redcap/request.py
@@ -67,6 +67,8 @@ class RCRequest(object):
         valid_data = {
             'exp_record': (['type', 'format'], 'record',
                 'Exporting record but content is not record'),
+            'del_record': (['format'], 'record',
+                'Deleting record but content is not record'),
             'imp_record': (['type', 'overwriteBehavior', 'data', 'format'],
                 'record', 'Importing record but content is not record'),
             'metadata': (['format'], 'metadata',
@@ -172,3 +174,6 @@ class RCRequest(object):
         # specifically 10.5
         if 500 <= r.status_code < 600:
             raise RedcapError(r.content)
+
+        if 400 == r.status_code and self.type == 'exp_record':
+            raise RedcapError(r.content)
diff --git a/redcap/version.py b/redcap/version.py
deleted file mode 100644
index a98c726..0000000
--- a/redcap/version.py
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-__author__ = 'Scott Burns <scott.s.burns@gmail.com>'
-__license__ = 'MIT'
-__copyright__ = '2014, Vanderbilt University'
-
-VERSION = '1.0.2'
diff --git a/setup.py b/setup.py
index cece97d..dbe302b 100644
--- a/setup.py
+++ b/setup.py
@@ -5,21 +5,30 @@ __author__ = 'Scott Burns <scott.s.burns@gmail.com>'
 __license__ = 'MIT'
 __copyright__ = '2014, Vanderbilt University'
 
+import codecs
 import os
+import re
+
+# Taken from vulture setup.py: https://github.com/jendrikseipp/vulture/blob/master/setup.py
+def read(*parts):
+    here = os.path.abspath(os.path.dirname(__file__))
+    with codecs.open(os.path.join(here, *parts), "r") as f:
+        return f.read()
+
+def find_version(*file_parts):
+    version_file = read(*file_parts)
+    version_match = re.search(
+        r"^__version__ = ['\"]([^'\"]*)['\"]$", version_file, re.M
+    )
+    if version_match:
+        return version_match.group(1)
+    raise RuntimeError("Unable to find version string.")
 
 try:
     from setuptools import setup
 except ImportError:
     from distutils.core import setup
 
-
-def get_version():
-    base = os.path.dirname(__file__)
-    with open(os.path.join(base, 'redcap/version.py')) as f:
-        VERSION = None
-        exec(f.read())
-        return VERSION
-
 required = [
     'requests>=1.0.0',
     'semantic-version>=2.3.1'
@@ -37,7 +46,7 @@ if __name__ == '__main__':
         description="""PyCap: Python interface to REDCap""",
         license='MIT',
         url='http://sburns.github.com/PyCap',
-        version=get_version(),
+        version=find_version("redcap", "__init__.py"),
         download_url='http://sburns.github.com/PyCap',
         long_description=long_desc,
         packages=['redcap'],

Version is set to 0.0.0 after installing with pip
`./pip install -e git+https://github.com/sburns/PyCap.git#egg=PyCap`


$ ./pip show pycap
Name: PyCap
Version: 0.0.0
Summary: PyCap: Python interface to REDCap
Home-page: http://sburns.github.com/PyCap
Author: None
Author-email: None
License: MIT
Location: /programs/x86_64-linux/python/3.6.5/bin/src/pycap
Requires: requests, semantic-version
Required-by: 
diff --git a/test/test_project.py b/test/test_project.py
index f2661c4..83f7c2e 100644
--- a/test/test_project.py
+++ b/test/test_project.py
@@ -29,6 +29,11 @@ except NameError:
     def is_bytestring(s):
         return isinstance(s, bytes)
 
+try:
+    from unittest import mock
+except ImportError:
+    import mock
+
 
 class ProjectTests(unittest.TestCase):
     """docstring for ProjectTests"""
@@ -392,6 +397,23 @@ class ProjectTests(unittest.TestCase):
         csv = self.reg_proj.export_metadata(format='csv')
         self.assertTrue(self.is_good_csv(csv))
 
+    def test_metadata_export_passes_filters_as_arrays(self):
+        self.reg_proj._call_api = mock.Mock()
+        self.reg_proj._call_api.return_value = (None, None)
+        self.reg_proj.export_metadata(
+            fields=['field0', 'field1', 'field2'],
+            forms=['form0', 'form1', 'form2'],
+        )
+
+        args, _ = self.reg_proj._call_api.call_args
+
+        payload = args[0]
+
+        self.assertEqual(payload['fields[0]'], 'field0')
+        self.assertEqual(payload['fields[1]'], 'field1')
+        self.assertEqual(payload['fields[2]'], 'field2')
+        self.assertEqual(payload['forms[2]'], 'form2')
+
     def test_bad_creds(self):
         "Test that exceptions are raised with bad URL or tokens"
         with self.assertRaises(RedcapError):
@@ -408,6 +430,21 @@ class ProjectTests(unittest.TestCase):
         for arm in fem:
             self.assertIsInstance(arm, dict)
 
+    def test_fem_export_passes_filters_as_arrays(self):
+        self.reg_proj._call_api = mock.Mock()
+        self.reg_proj._call_api.return_value = (None, None)
+        self.reg_proj.export_fem(
+            arms=['arm0', 'arm1', 'arm2'],
+        )
+
+        args, _ = self.reg_proj._call_api.call_args
+
+        payload = args[0]
+
+        self.assertEqual(payload['arms[0]'], 'arm0')
+        self.assertEqual(payload['arms[1]'], 'arm1')
+        self.assertEqual(payload['arms[2]'], 'arm2')
+
     @responses.activate
     def test_file_export(self):
         """Test file export and proper content-type parsing"""
@@ -634,6 +671,27 @@ class ProjectTests(unittest.TestCase):
         for record in records:
             self.assertIn(self.reg_proj.def_field, record)
 
+    def test_export_passes_filters_as_arrays(self):
+        self.reg_proj._call_api = mock.Mock()
+        self.reg_proj._call_api.return_value = (None, None)
+        self.reg_proj.export_records(
+            records=['record0', 'record1', 'record2'],
+            fields=['field0', 'field1', 'field2'],
+            forms=['form0', 'form1', 'form2'],
+            events=['event0', 'event1', 'event2']
+        )
+
+        args, _ = self.reg_proj._call_api.call_args
+
+        payload = args[0]
+
+        self.assertEqual(payload['records[0]'], 'record0')
+        self.assertEqual(payload['records[1]'], 'record1')
+        self.assertEqual(payload['records[2]'], 'record2')
+        self.assertEqual(payload['fields[1]'], 'field1')
+        self.assertEqual(payload['forms[2]'], 'form2')
+        self.assertEqual(payload['events[0]'], 'event0')
+
     @responses.activate
     def test_generate_next_record_name(self):
         "Test exporting the next potential record ID for a project"

pip install -e .
parse_log_pytest
requirements.txt
numpy>=1.16.0 pandas>=1.0.0 pytest
apt-get update apt-get install -y gcc
3.6
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
attrs==22.2.0
Babel==2.11.0
certifi==2021.5.30
charset-normalizer==2.0.12
cookies==2.2.1
docutils==0.18.1
idna==3.10
imagesize==1.4.1
importlib-metadata==4.8.3
iniconfig==1.1.1
Jinja2==3.0.3
MarkupSafe==2.0.1
nose==1.2.1
numpy==1.19.5
numpydoc==0.8.0
packaging==21.3
pandas==1.1.5
pluggy==1.0.0
py==1.11.0
-e git+https://github.com/redcap-tools/PyCap.git@60b6e2aebff271b3bf70b76877d1303c563d85c1#egg=PyCap
Pygments==2.14.0
pyparsing==3.1.4
pytest==7.0.1
python-dateutil==2.9.0.post0
pytz==2025.2
requests==2.27.1
responses==0.9.0
semantic-version==2.3.1
six==1.17.0
snowballstemmer==2.2.0
Sphinx==1.8.3
sphinxcontrib-serializinghtml==1.1.5
sphinxcontrib-websupport==1.2.4
tomli==1.2.3
typing_extensions==4.1.1
urllib3==1.26.20
zipp==3.6.0

name: PyCap
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - attrs==22.2.0
      - babel==2.11.0
      - charset-normalizer==2.0.12
      - cookies==2.2.1
      - docutils==0.18.1
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - jinja2==3.0.3
      - markupsafe==2.0.1
      - nose==1.2.1
      - numpy==1.19.5
      - numpydoc==0.8.0
      - packaging==21.3
      - pandas==1.1.5
      - pluggy==1.0.0
      - py==1.11.0
      - pygments==2.14.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - requests==2.27.1
      - responses==0.9.0
      - semantic-version==2.3.1
      - six==1.17.0
      - snowballstemmer==2.2.0
      - sphinx==1.8.3
      - sphinxcontrib-serializinghtml==1.1.5
      - sphinxcontrib-websupport==1.2.4
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - wheel==0.22.0
      - zipp==3.6.0
prefix: /opt/conda/envs/PyCap

test/test_project.py::ProjectTests::test_export_passes_filters_as_arrays test/test_project.py::ProjectTests::test_fem_export_passes_filters_as_arrays test/test_project.py::ProjectTests::test_metadata_export_passes_filters_as_arrays
test/test_project.py::ProjectTests::test_file_export test/test_project.py::ProjectTests::test_file_import
test/test_project.py::ProjectTests::test_bad_creds test/test_project.py::ProjectTests::test_csv_export test/test_project.py::ProjectTests::test_date_formatting test/test_project.py::ProjectTests::test_export_always_include_def_field test/test_project.py::ProjectTests::test_export_checkbox_labels test/test_project.py::ProjectTests::test_export_data_access_groups test/test_project.py::ProjectTests::test_export_df_kwargs test/test_project.py::ProjectTests::test_export_project_info test/test_project.py::ProjectTests::test_export_survey_fields test/test_project.py::ProjectTests::test_export_to_df test/test_project.py::ProjectTests::test_fem_export test/test_project.py::ProjectTests::test_file_delete test/test_project.py::ProjectTests::test_generate_next_record_name test/test_project.py::ProjectTests::test_get_version test/test_project.py::ProjectTests::test_good_init test/test_project.py::ProjectTests::test_import_dataframe test/test_project.py::ProjectTests::test_import_exception test/test_project.py::ProjectTests::test_import_records test/test_project.py::ProjectTests::test_is_longitudinal test/test_project.py::ProjectTests::test_json_export test/test_project.py::ProjectTests::test_long_attrs test/test_project.py::ProjectTests::test_long_export test/test_project.py::ProjectTests::test_metadata_df_kwargs test/test_project.py::ProjectTests::test_metadata_export test/test_project.py::ProjectTests::test_metadata_to_df test/test_project.py::ProjectTests::test_normal_attrs test/test_project.py::ProjectTests::test_regular_attrs test/test_project.py::ProjectTests::test_user_export test/test_project.py::ProjectTests::test_verify_ssl
README.rst redcap/request.py docs/deep.rst redcap/version.py redcap/__init__.py setup.py redcap/project.py
README.rst redcap/request.py docs/deep.rst redcap/version.py redcap/__init__.py setup.py redcap/project.py