diff --git docs/index.rst docs/index.rst index 1172a49..a4eaf5f 100644 --- docs/index.rst +++ docs/index.rst @@ -7,6 +7,7 @@ Contents: :maxdepth: 2 :includehidden: + installation/index core/index conch/index lore/index diff --git docs/installation/howto/optional.rst docs/installation/howto/optional.rst new file mode 100644 index 0000000..a92343e --- /dev/null +++ docs/installation/howto/optional.rst @@ -0,0 +1,63 @@ + +:LastChangedDate: $LastChangedDate$ +:LastChangedRevision: $LastChangedRevision$ +:LastChangedBy: $LastChangedBy$ + +Installing Optional Dependencies +================================ + +This document describes the optional dependencies that Twisted supports. +The dependencies are python packages that Twisted's developers have found useful either for developing Twisted itself or for developing Twisted applications. + +The intended audience of this document is someone who is familiar with installing optional dependencies using `pip`_. + +If you are unfamiliar with the installation of optional dependencies, the `python packaging tutorial`_ can show you how. +For a deeper explanation of what optional dependencies are and how they are declared, please see the `setuptools documentation`_. + +The following optional dependencies are supported: + +* **dev** - packages that aid in the development of Twisted itself. + * `TwistedChecker`_ + * `pyflakes`_ + * `twisted-dev-tools`_ + * `python-subunit`_ + * `Sphinx`_ + * `pydoctor`_ + +* **tls** - packages that are needed to work with TLS. + * `pyOpenSSL`_ + * `service_identity`_ + +* **conch** - packages for working with conch/SSH. + * `gmpy`_ + * `pyasn1`_ + * `pycrypto`_ + +* **soap** - the `SOAPpy`_ package to work with SOAP. + +* **serial** - the `pyserial`_ package to work with serial data. + +* **all_non_platform** - installs **tls**, **conch**, **soap**, and **serial** options. + +* **osx_platform** - **all_non_platform** options and `pyobjc`_ to work with Objective-C apis. + +* **windows_platform** - **all_non_platform** options and `pypiwin32`_ to work with Windows's apis. + +.. _pip: https://pip.pypa.io/en/latest/quickstart.html +.. _TwistedChecker: https://pypi.python.org/pypi/TwistedChecker +.. _pyflakes: https://pypi.python.org/pypi/pyflakes +.. _twisted-dev-tools: https://pypi.python.org/pypi/twisted-dev-tools +.. _python-subunit: https://pypi.python.org/pypi/python-subunit +.. _Sphinx: https://pypi.python.org/pypi/Sphinx/1.3b1 +.. _pydoctor: https://pypi.python.org/pypi/pydoctor +.. _pyOpenSSL: https://pypi.python.org/pypi/pyOpenSSL +.. _service_identity: https://pypi.python.org/pypi/service_identity +.. _gmpy: https://pypi.python.org/pypi/gmpy/1.17 +.. _pyasn1: https://pypi.python.org/pypi/pyasn1 +.. _pycrypto: https://pypi.python.org/pypi/pycrypto +.. _SOAPpy: https://pypi.python.org/pypi/SOAPpy +.. _pyserial: https://pypi.python.org/pypi/pyserial +.. _pyobjc: https://pypi.python.org/pypi/pyobjc +.. _pypiwin32: https://pypi.python.org/pypi/pypiwin32 +.. _`setuptools documentation`: https://pythonhosted.org/setuptools/setuptools.html#declaring-extras-optional-features-with-their-own-dependencies +.. _`python packaging tutorial`: https://packaging.python.org/en/latest/installing.html#examples diff --git docs/installation/index.rst docs/installation/index.rst new file mode 100644 index 0000000..16617e2 --- /dev/null +++ docs/installation/index.rst @@ -0,0 +1,14 @@ + +:LastChangedDate: $LastChangedDate$ +:LastChangedRevision: $LastChangedRevision$ +:LastChangedBy: $LastChangedBy$ + +Installing Twisted +================== + +.. toctree:: + :hidden: + + howto/optional + +- :doc:`Installing Optional Dependencies `: documentation on how to install Twisted's optional dependencies. diff --git setup.py setup.py index 26f052b..e63f1b5 100755 --- setup.py +++ setup.py @@ -54,7 +54,7 @@ dependency resolution is disabled. from twisted.python.dist import ( STATIC_PACKAGE_METADATA, getDataFiles, getExtensions, getAllScripts, - getPackages, setup) + getPackages, setup, EXTRAS_REQUIRE) scripts = getAllScripts() @@ -62,6 +62,7 @@ dependency resolution is disabled. packages=getPackages('twisted'), conditionalExtensions=getExtensions(), scripts=scripts, + extras_require=EXTRAS_REQUIRE, data_files=getDataFiles('twisted'), **STATIC_PACKAGE_METADATA)) diff --git twisted/python/dist.py twisted/python/dist.py index 63b36b3..2fec522 100644 --- twisted/python/dist.py +++ twisted/python/dist.py @@ -49,6 +49,53 @@ twisted_subprojects = ["conch", "lore", "mail", "names", "news", "pair", "runner", "web", "words"] +# These are the actual package names and versions that will +# be used by extras_require. This is not passed to setup +# directly so that combinations of the packages can be created +# without the need to copy package names multiple times. +EXTRA_OPTIONS = dict( + dev=['twistedchecker >= 0.2.0', + 'pyflakes >= 0.8.1', + 'twisted-dev-tools >= 0.0.2', + 'python-subunit', + 'sphinx >= 1.2.2', + 'pydoctor >= 0.5'], + tls=['pyopenssl >= 0.11', + 'service_identity'], + conch=['gmpy', + 'pyasn1', + 'pycrypto'], + soap=['soappy'], + serial=['pyserial'], + osx=['pyobjc'], + windows=['pypiwin32'] +) + +PLATFORM_INDEPENDENT = ( + EXTRA_OPTIONS['tls'] + + EXTRA_OPTIONS['conch'] + + EXTRA_OPTIONS['soap'] + + EXTRA_OPTIONS['serial'] +) + +# Extras_require is a dictionary of items that can be passed to setup.py +# to install optional dependencies. For example, to install the optional +# dev dependencies one would type `pip install -e ".[dev]"` +# This has been supported by setuptools since 0.5a4 +EXTRAS_REQUIRE = { + 'dev': EXTRA_OPTIONS['dev'], + 'tls': EXTRA_OPTIONS['tls'], + 'conch': EXTRA_OPTIONS['conch'], + 'soap': EXTRA_OPTIONS['soap'], + 'serial': EXTRA_OPTIONS['serial'], + 'all_non_platform': PLATFORM_INDEPENDENT, + 'osx_platform': ( + EXTRA_OPTIONS['osx'] + PLATFORM_INDEPENDENT + ), + 'windows_platform': ( + EXTRA_OPTIONS['windows'] + PLATFORM_INDEPENDENT + ), +} class ConditionalExtension(Extension): diff --git twisted/python/test/test_dist.py twisted/python/test/test_dist.py index d2288ee..5a131f4 100644 --- twisted/python/test/test_dist.py +++ twisted/python/test/test_dist.py @@ -9,15 +9,22 @@ Tests for parts of our release automation system. import os import sys -from distutils.core import Distribution from twisted.trial.unittest import TestCase from twisted.python import dist from twisted.python.dist import (get_setup_args, ConditionalExtension, - build_scripts_twisted) + build_scripts_twisted, EXTRAS_REQUIRE) from twisted.python.filepath import FilePath +usesSetuptools = None +try: + from setuptools.dist import Distribution + usesSetuptools = True +except ImportError: + from disutils.core import Distribution + usesSetuptools = None + class SetupTest(TestCase): @@ -59,6 +66,147 @@ class SetupTest(TestCase): +class OptionalDependenciesTests(TestCase): + """ + Tests for L{dist.EXTRA_REQUIRES} + """ + if usesSetuptools is None: + skip = "cannot test without setuptools" + + + def test_distributeTakesExtrasRequire(self): + """ + Setuptools' Distribution object can use extra_requires. + """ + extras = dict(im_an_extra_dependency="thing") + attrs = dict(extras_require=extras) + distribution = Distribution(attrs) + self.assertEqual( + extras, + distribution.extras_require + ) + + + def test_extrasRequireDictContainsKeys(self): + """ + L{dist.EXTRA_REQUIRES} C{dev} option contains a valid + list with correct dependecies. + """ + self.assertIn('dev', EXTRAS_REQUIRE) + self.assertIn('tls', EXTRAS_REQUIRE) + self.assertIn('conch', EXTRAS_REQUIRE) + self.assertIn('soap', EXTRAS_REQUIRE) + self.assertIn('serial', EXTRAS_REQUIRE) + self.assertIn('all_non_platform', EXTRAS_REQUIRE) + self.assertIn('osx_platform', EXTRAS_REQUIRE) + self.assertIn('windows_platform', EXTRAS_REQUIRE) + + + def test_extrasRequiresDevDepsAreValid(self): + """ + L{dist.EXTRA_REQUIRES} C{dev} option contains the correct + dependencies. + """ + deps = EXTRAS_REQUIRE['dev'] + self.assertIn('twistedchecker >= 0.2.0', deps) + self.assertIn('pyflakes >= 0.8.1', deps) + self.assertIn('twisted-dev-tools >= 0.0.2', deps) + self.assertIn('python-subunit', deps) + self.assertIn('sphinx >= 1.2.2', deps) + self.assertIn('pydoctor >= 0.5', deps) + + + def test_extrasRequiresTlsDepsAreValid(self): + """ + L{dist.EXTRA_REQUIRES} C{tls} option contains the correct + dependencies. + """ + deps = EXTRAS_REQUIRE['tls'] + self.assertIn('pyopenssl >= 0.11', deps) + self.assertIn('service_identity', deps) + + + def test_extrasRequiresConchDepsAreValid(self): + """ + L{dist.EXTRA_REQUIRES} C{conch} option contains the correct + dependencies. + """ + deps = EXTRAS_REQUIRE['conch'] + self.assertIn('gmpy', deps) + self.assertIn('pyasn1', deps) + self.assertIn('pycrypto', deps) + + + def test_extrasRequiresSoapDepsAreValid(self): + """ + L{dist.EXTRA_REQUIRES} C{soap} option contains the correct + dependecies. + """ + self.assertIn( + 'soappy', + EXTRAS_REQUIRE['soap'] + ) + + + def test_extrasRequiresSerialDepsAreValid(self): + """ + L{dist.EXTRA_REQUIRES} C{serial} option contains the correct + dependencies. + """ + self.assertIn( + 'pyserial', + EXTRAS_REQUIRE['serial'] + ) + + + def test_extrasRequiresAllNonPlatformDepsAreValid(self): + """ + L{dist.EXTRA_REQUIRES} C{all_non_platform} option contains the + correct dependencies. + """ + deps = EXTRAS_REQUIRE['all_non_platform'] + self.assertIn('pyopenssl >= 0.11', deps) + self.assertIn('service_identity', deps) + self.assertIn('gmpy', deps) + self.assertIn('pyasn1', deps) + self.assertIn('pycrypto', deps) + self.assertIn('soappy', deps) + self.assertIn('pyserial', deps) + + + def test_extrasRequiresOsxPlatformDepsAreValid(self): + """ + L{dist.EXTRA_REQUIRES} C{osx_platform} option contains the correct + dependecies. + """ + deps = EXTRAS_REQUIRE['osx_platform'] + self.assertIn('pyopenssl >= 0.11', deps) + self.assertIn('service_identity', deps) + self.assertIn('gmpy', deps) + self.assertIn('pyasn1', deps) + self.assertIn('pycrypto', deps) + self.assertIn('soappy', deps) + self.assertIn('pyserial', deps) + self.assertIn('pyobjc', deps) + + + def test_extrasRequiresWindowsPlatformDepsAreValid(self): + """ + L{dist.EXTRA_REQUIRES} C{windows_platform} option contains the correct + dependecies. + """ + deps = EXTRAS_REQUIRE['windows_platform'] + self.assertIn('pyopenssl >= 0.11', deps) + self.assertIn('service_identity', deps) + self.assertIn('gmpy', deps) + self.assertIn('pyasn1', deps) + self.assertIn('pycrypto', deps) + self.assertIn('soappy', deps) + self.assertIn('pyserial', deps) + self.assertIn('pypiwin32', deps) + + + class GetExtensionsTest(TestCase): """ Tests for L{dist.getExtensions}. diff --git twisted/topfiles/3696.feature twisted/topfiles/3696.feature new file mode 100644 index 0000000..24d3d09 --- /dev/null +++ twisted/topfiles/3696.feature @@ -0,0 +1 @@ +Optional dependencies can be installed using the extra_requires facility provided by setuptools. \ No newline at end of file