diff --git a/CHANGES.rst b/CHANGES.rst
index 480bb80496e13c9abcd317db8b8b5b07356908a8..0406e985f908158314ef2699ce9a095bcced66a4 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -13,6 +13,7 @@
   on Linux. They no longer pass the socket type or protocol to
   ``getaddrinfo`` when ``connect`` is called. Reported in :issue:`944`
   by Bernie Hackett.
+- Replace ``optparse`` module with ``argparse``. See :issue:`947`.
 
 1.2.1 (2017-01-12)
 ==================
diff --git a/src/greentest/bench_spawn.py b/src/greentest/bench_spawn.py
index 3291065433b80396a9fcdfddadc2ca55c92a70f7..894a34df2fbfed631f49ee5370546e8b0e8caf6e 100644
--- a/src/greentest/bench_spawn.py
+++ b/src/greentest/bench_spawn.py
@@ -149,25 +149,19 @@ def all_functions():
 
 
 if __name__ == '__main__':
-    USAGE = 'USAGE: python %s [--with-kwargs] [--eventlet-hub HUB] %s' % (sys.argv[0], '|'.join(all()))
-    if not sys.argv[1:]:
-        sys.exit(USAGE)
-    import optparse
-    parser = optparse.OptionParser()
-    parser.add_option('--with-kwargs', default=False, action='store_true')
-    parser.add_option('--eventlet-hub')
-    parser.add_option('--ignore-import-errors', action='store_true')
-    options, args = parser.parse_args()
+    import argparse
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--with-kwargs', default=False, action='store_true')
+    parser.add_argument('--eventlet-hub')
+    parser.add_argument('--ignore-import-errors', action='store_true')
+    parser.add_argument('benchmark', choices=all() + ['all'])
+    options = parser.parse_args()
     if options.with_kwargs:
         options.kwargs = {'foo': 1, 'bar': 'hello'}
     else:
         options.kwargs = {}
-    if len(args) != 1:
-        sys.exit(USAGE)
-    if args[0] == 'all':
+    if options.benchmark == 'all':
         bench_all(options)
     else:
-        if args[0] not in all():
-            sys.exit(USAGE)
-        function = globals()['bench_' + args[0]]
+        function = globals()['bench_' + options.benchmark]
         function(options)
diff --git a/src/greentest/testrunner.py b/src/greentest/testrunner.py
index c2b78fd06db9ec84a2bb197ae82cfc5376cb8867..d5b7f772beab246459cf45988e1b240266984f30 100644
--- a/src/greentest/testrunner.py
+++ b/src/greentest/testrunner.py
@@ -268,17 +268,17 @@ def print_list(lst):
 
 
 def main():
-    # FIXME: transition to argparse
-    import optparse # pylint:disable=deprecated-module
-    parser = optparse.OptionParser()
-    parser.add_option('--ignore')
-    parser.add_option('--discover', action='store_true')
-    parser.add_option('--full', action='store_true')
-    parser.add_option('--config')
-    parser.add_option('--failfast', action='store_true')
-    parser.add_option("--coverage", action="store_true")
-    parser.add_option("--quiet", action="store_true")
-    options, args = parser.parse_args()
+    import argparse
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--ignore')
+    parser.add_argument('--discover', action='store_true')
+    parser.add_argument('--full', action='store_true')
+    parser.add_argument('--config')
+    parser.add_argument('--failfast', action='store_true')
+    parser.add_argument("--coverage", action="store_true")
+    parser.add_argument("--quiet", action="store_true")
+    parser.add_argument('tests', nargs='*')
+    options = parser.parse_args()
     FAILING_TESTS = []
     coverage = False
     if options.coverage or os.environ.get("GEVENTTEST_COVERAGE"):
@@ -297,7 +297,7 @@ def main():
             config_data = f.read()
         six.exec_(config_data, config)
         FAILING_TESTS = config['FAILING_TESTS']
-    tests = discover(args, options.ignore, coverage)
+    tests = discover(options.tests, options.ignore, coverage)
     if options.discover:
         for cmd, options in tests:
             print(util.getname(cmd, env=options.get('env'), setenv=options.get('setenv')))
diff --git a/util/makedist.py b/util/makedist.py
index 05f55bda2408e53b5e0af0fb60212c84b974c074..abb9e00ec3f994c220d8f92f65b6c63381766727 100755
--- a/util/makedist.py
+++ b/util/makedist.py
@@ -13,7 +13,7 @@ from __future__ import print_function
 import sys
 import os
 import glob
-import optparse
+import argparse
 from os.path import exists, join, abspath, basename
 from pipes import quote
 
@@ -76,11 +76,10 @@ def _makedist(version=None, dest=None):
 
 
 def main():
-    parser = optparse.OptionParser()
-    parser.add_option('--dest')
-    parser.add_option('--version')
-    options, args = parser.parse_args()
-    assert not args, 'Expected no arguments'
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--dest')
+    parser.add_argument('--version')
+    options = parser.parse_args()
     return makedist(options.version, dest=options.dest)
 
 
diff --git a/util/set_version.py b/util/set_version.py
index bb372f8bae5aa2db0f1862e10d66cf1a0ee82048..e7245746c71b36da624acfdd55744f5f15c61e57 100755
--- a/util/set_version.py
+++ b/util/set_version.py
@@ -9,7 +9,7 @@ from __future__ import print_function
 import sys
 import os
 import re
-from optparse import OptionParser
+from argparse import ArgumentParser
 from distutils.version import LooseVersion
 
 
@@ -132,26 +132,25 @@ def write(filename, data):
 
 def main():
     global options
-    parser = OptionParser()
-    parser.add_option('--version', default='dev')
-    parser.add_option('--dry-run', action='store_true')
-    options, args = parser.parse_args()
-    assert len(args) == 1, 'One argument is expected, got %s' % len(args)
+    parser = ArgumentParser()
+    parser.add_argument('--version', default='dev')
+    parser.add_argument('--dry-run', action='store_true')
+    parser.add_argument('filename')
+    options = parser.parse_args()
     version = options.version
     if version.lower() == 'dev':
         version = ''
     if version and strict_version_re.match(version) is None:
         sys.stderr.write('WARNING: Not a strict version: %r (bdist_msi will fail)' % version)
-    filename = args[0]
-    original_content, new_content = modify_version(filename, version)
+    original_content, new_content = modify_version(options.filename, version)
     if options.dry_run:
-        tmpname = '/tmp/' + os.path.basename(filename) + '.set_version'
+        tmpname = '/tmp/' + os.path.basename(options.filename) + '.set_version'
         write(tmpname, new_content)
-        if not os.system('diff -u %s %s' % (filename, tmpname)):
+        if not os.system('diff -u %s %s' % (options.filename, tmpname)):
             sys.exit('No differences applied')
     else:
-        write(filename, new_content)
-        print('Updated %s' % filename)
+        write(options.filename, new_content)
+        print('Updated %s' % options.filename)
 
 
 if __name__ == '__main__':
diff --git a/util/wintest.py b/util/wintest.py
index 32218263757d46370cf23d0b076edf8be2abbd55..06af104a18c156cc1973c2c1a87ec3a77bb50dd6 100755
--- a/util/wintest.py
+++ b/util/wintest.py
@@ -4,7 +4,7 @@ Unix utilities must be installed on target machine for this to work: http://unxu
 """
 import sys
 import os
-import optparse
+import argparse
 
 
 def system(cmd, exit=True):
@@ -16,13 +16,15 @@ def system(cmd, exit=True):
     return retcode
 
 
-parser = optparse.OptionParser()
-parser.add_option('--host')
-parser.add_option('--username', default='Administrator')
-parser.add_option('--source')
-parser.add_option('--dist', action='store_true')
-parser.add_option('--python', default='27')
-options, args = parser.parse_args()
+parser = argparse.ArgumentParser()
+parser.add_argument('--host')
+parser.add_argument('--username', default='Administrator')
+parser.add_argument('--source')
+parser.add_argument('--dist', action='store_true')
+parser.add_argument('--python', default='27')
+parser.add_argument('args', nargs='*')
+options = parser.parse_args()
+args = options.args
 
 
 def prepare():